From 71e6cf2fcf329795ab04b66d71328ec01296a81d Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Thu, 18 Apr 2024 15:05:27 -0400 Subject: [PATCH 01/39] WIP --- .../data_migration/transformation/__init__.py | 0 .../transformation/transform_oracle_data.py | 118 ++++++++++++++++++ api/tests/lib/seed_local_db.py | 2 + 3 files changed, 120 insertions(+) create mode 100644 api/src/data_migration/transformation/__init__.py create mode 100644 api/src/data_migration/transformation/transform_oracle_data.py diff --git a/api/src/data_migration/transformation/__init__.py b/api/src/data_migration/transformation/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/api/src/data_migration/transformation/transform_oracle_data.py b/api/src/data_migration/transformation/transform_oracle_data.py new file mode 100644 index 000000000..027e15261 --- /dev/null +++ b/api/src/data_migration/transformation/transform_oracle_data.py @@ -0,0 +1,118 @@ +import logging +from enum import StrEnum + +from sqlalchemy import Select + +from src.adapters.db import PostgresDBClient +from src.db.models.opportunity_models import Opportunity +from src.db.models.transfer.topportunity_models import TransferTopportunity +from src.task.task import Task + + +from src.constants.lookup_constants import OpportunityCategory + +logger = logging.getLogger(__name__) + + +class TransformOracleData(Task): + class Metrics(StrEnum): + TOTAL_RECORDS_PROCESSED = "total_records_processed" + TOTAL_RECORDS_DELETED = "total_records_deleted" + TOTAL_RECORDS_INSERTED = "total_records_inserted" + TOTAL_RECORDS_UPDATED = "total_records_updated" + + def run_task(self) -> None: + with self.db_session.begin(): + self.process_opportunities() + # Opportunities + + # Assistance Listings + + # Opportunity Summary + + # One-to-many lookups + + def process_opportunities(self) -> None: + # todo handle deletes + + + # TODO - add filters so it only contains updates + # TODO - we can probably select a tuple with both in a join? + source_opportunities: list[TransferTopportunity] = self.db_session.execute( + Select(TransferTopportunity) + ).scalars() + + # TODO - this is just update/inserts + # TODO - add the incrementer counter + for source_opportunity in source_opportunities: + target_opportunity: Opportunity | None = self.db_session.execute( + Select(Opportunity).where( + Opportunity.opportunity_id == source_opportunity.opportunity_id + ) + ).scalar_one_or_none() + + # transform + transformed_opportunity = self.transform_opportunity( + source_opportunity, target_opportunity + ) + self.db_session.add(transformed_opportunity) + + # TODO - set the field we query by to null (or set it? which way are we doing it?) + # target_opportunity.whatever_field = None + + def transform_opportunity( + self, source_opportunity: TransferTopportunity, target_opportunity: Opportunity | None + ) -> Opportunity: + if target_opportunity is None: + self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + target_opportunity = Opportunity(opportunity_id=source_opportunity.opportunity_id) + else: + self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) + + target_opportunity.opportunity_number = source_opportunity.oppnumber + target_opportunity.opportunity_title = source_opportunity.opptitle + target_opportunity.agency = source_opportunity.owningagency + target_opportunity.opportunity_category = transform_opportunity_category( + source_opportunity.oppcategory + ) + target_opportunity.category_explanation = source_opportunity.category_explanation + target_opportunity.is_draft = source_opportunity.is_draft != "N" + target_opportunity.revision_number = source_opportunity.revision_number + target_opportunity.modified_comments = source_opportunity.modified_comments + target_opportunity.publisher_user_id = source_opportunity.publisheruid + target_opportunity.publisher_profile_id = source_opportunity.publisher_profile_id + + return target_opportunity + +def transform_opportunity_category(value: str | None) -> OpportunityCategory | None: + if value is None or value == "": + return None + + match value: + case "D": + return OpportunityCategory.DISCRETIONARY + case "M": + return OpportunityCategory.MANDATORY + case "C": + return OpportunityCategory.CONTINUATION + case "E": + return OpportunityCategory.EARMARK + case "O": + return OpportunityCategory.OTHER + + raise ValueError(f"Unrecognized opportunity category") + + +# TODO - this is likely going to be run as part of a separate script +# but just to help build it out at the moment and test it, setting +# an entrypoint for easy local manual testing +def main(): + import src.logging + + with src.logging.init("transform_oracle_data"): + db_client = PostgresDBClient() + + with db_client.get_session() as db_session: + TransformOracleData(db_session).run() + +main() \ No newline at end of file diff --git a/api/tests/lib/seed_local_db.py b/api/tests/lib/seed_local_db.py index d52758448..016b31a25 100644 --- a/api/tests/lib/seed_local_db.py +++ b/api/tests/lib/seed_local_db.py @@ -28,6 +28,8 @@ def _build_opportunities(db_session: db.Session, iterations: int) -> None: factories.OpportunityFactory.reset_sequence(value=max_opportunity_id + 1) for i in range(iterations): + if True: + continue logger.info(f"Creating opportunity batch number {i}") # Create a few opportunities in various scenarios factories.OpportunityFactory.create_batch(size=5, is_forecasted_summary=True) From cfc8076a38fd200d1c330c84877bc424e124a784 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Thu, 18 Apr 2024 16:21:31 -0400 Subject: [PATCH 02/39] WIP --- .../transformation/transform_oracle_data.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/api/src/data_migration/transformation/transform_oracle_data.py b/api/src/data_migration/transformation/transform_oracle_data.py index 027e15261..8a1925804 100644 --- a/api/src/data_migration/transformation/transform_oracle_data.py +++ b/api/src/data_migration/transformation/transform_oracle_data.py @@ -1,7 +1,7 @@ import logging from enum import StrEnum -from sqlalchemy import Select +from sqlalchemy import select, delete from src.adapters.db import PostgresDBClient from src.db.models.opportunity_models import Opportunity @@ -33,20 +33,21 @@ def run_task(self) -> None: # One-to-many lookups def process_opportunities(self) -> None: - # todo handle deletes - + # todo handle deletes and get a count somehow? + opportunity_ids_to_delete = select(TransferTopportunity.opportunity_id).where(TransferTopportunity.publisher_profile_id == None) # TODO - make this the real query + self.db_session.execute(delete(Opportunity).where(Opportunity.opportunity_id.in_(opportunity_ids_to_delete))) # TODO - add filters so it only contains updates # TODO - we can probably select a tuple with both in a join? source_opportunities: list[TransferTopportunity] = self.db_session.execute( - Select(TransferTopportunity) + select(TransferTopportunity) ).scalars() # TODO - this is just update/inserts # TODO - add the incrementer counter for source_opportunity in source_opportunities: target_opportunity: Opportunity | None = self.db_session.execute( - Select(Opportunity).where( + select(Opportunity).where( Opportunity.opportunity_id == source_opportunity.opportunity_id ) ).scalar_one_or_none() From 6ccd8e65e1e4a6acbd3e966db294fb1415cd1435 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Fri, 19 Apr 2024 13:29:51 -0400 Subject: [PATCH 03/39] More organization --- .../transformation/transform_oracle_data.py | 73 ++++++++++++++----- api/tests/lib/seed_local_db.py | 2 - 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/api/src/data_migration/transformation/transform_oracle_data.py b/api/src/data_migration/transformation/transform_oracle_data.py index 8a1925804..7ebb41d8c 100644 --- a/api/src/data_migration/transformation/transform_oracle_data.py +++ b/api/src/data_migration/transformation/transform_oracle_data.py @@ -1,5 +1,6 @@ import logging from enum import StrEnum +from typing import Tuple from sqlalchemy import select, delete @@ -21,6 +22,8 @@ class Metrics(StrEnum): TOTAL_RECORDS_INSERTED = "total_records_inserted" TOTAL_RECORDS_UPDATED = "total_records_updated" + + def run_task(self) -> None: with self.db_session.begin(): self.process_opportunities() @@ -33,33 +36,63 @@ def run_task(self) -> None: # One-to-many lookups def process_opportunities(self) -> None: - # todo handle deletes and get a count somehow? - opportunity_ids_to_delete = select(TransferTopportunity.opportunity_id).where(TransferTopportunity.publisher_profile_id == None) # TODO - make this the real query - self.db_session.execute(delete(Opportunity).where(Opportunity.opportunity_id.in_(opportunity_ids_to_delete))) + # Fetch all opportunities that were modified + # Alongside that, grab the existing opportunity record # TODO - add filters so it only contains updates - # TODO - we can probably select a tuple with both in a join? - source_opportunities: list[TransferTopportunity] = self.db_session.execute( - select(TransferTopportunity) - ).scalars() - - # TODO - this is just update/inserts - # TODO - add the incrementer counter - for source_opportunity in source_opportunities: - target_opportunity: Opportunity | None = self.db_session.execute( - select(Opportunity).where( - Opportunity.opportunity_id == source_opportunity.opportunity_id - ) - ).scalar_one_or_none() - - # transform + # TODO - add a yield_per + opportunities: list[Tuple[TransferTopportunity, Opportunity | None]] = self.db_session.execute( + select(TransferTopportunity, Opportunity).join(Opportunity, TransferTopportunity.opportunity_id == Opportunity.opportunity_id, isouter=True) + ).all() + + for source_opportunity, target_opportunity in opportunities: + try: + self.process_opportunity(source_opportunity, target_opportunity) + except Exception: + # TODO - more info + # TODO - metric + logger.exception("Failed to process opportunity") + + def process_opportunity(self, source_opportunity: TransferTopportunity, target_opportunity: Opportunity | None) -> None: + extra = {"opportunity_id": source_opportunity.opportunity_id} + logger.info("Processing opportunity", extra=extra) + + # TODO - whatever check we need to do to see if something should be deleted + if source_opportunity.publisheruid == "should be deleted": + logger.info("Deleting opportunity", extra=extra) + + if target_opportunity is None: + raise Exception("Cannot delete opportunity as it does not exist") + + self.increment(self.Metrics.TOTAL_RECORDS_DELETED) + self.db_session.delete(target_opportunity) + + + else: + logger.info("Transforming and upserting opportunity", extra=extra) transformed_opportunity = self.transform_opportunity( source_opportunity, target_opportunity ) self.db_session.add(transformed_opportunity) - # TODO - set the field we query by to null (or set it? which way are we doing it?) - # target_opportunity.whatever_field = None + + logger.info("Processed opportunity", extra=extra) + # TODO - set the field we query by to null (or set it? which way are we doing it?) + # target_opportunity.whatever_field = None + + def process_assistance_listings(self) -> None: + # TODO - https://github.com/HHS/simpler-grants-gov/issues/1746 + + pass + + def process_opportunity_summaries(self) -> None: + # TODO - https://github.com/HHS/simpler-grants-gov/issues/1747 + pass + + def process_one_to_many_lookup_tables(self) -> None: + # TODO - https://github.com/HHS/simpler-grants-gov/issues/1749 + pass + def transform_opportunity( self, source_opportunity: TransferTopportunity, target_opportunity: Opportunity | None diff --git a/api/tests/lib/seed_local_db.py b/api/tests/lib/seed_local_db.py index 016b31a25..d52758448 100644 --- a/api/tests/lib/seed_local_db.py +++ b/api/tests/lib/seed_local_db.py @@ -28,8 +28,6 @@ def _build_opportunities(db_session: db.Session, iterations: int) -> None: factories.OpportunityFactory.reset_sequence(value=max_opportunity_id + 1) for i in range(iterations): - if True: - continue logger.info(f"Creating opportunity batch number {i}") # Create a few opportunities in various scenarios factories.OpportunityFactory.create_batch(size=5, is_forecasted_summary=True) From 3741df57b7b8e68c42bae4bed87ec3d883bbfd3d Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Mon, 22 Apr 2024 14:07:35 -0400 Subject: [PATCH 04/39] Adding more implementation --- .../transformation/transform_oracle_data.py | 98 ++++++++++++------- .../data_migration/transformation/__init__.py | 0 .../test_transform_oracle_data.py | 57 +++++++++++ api/tests/src/db/models/factories.py | 6 ++ 4 files changed, 126 insertions(+), 35 deletions(-) create mode 100644 api/tests/src/data_migration/transformation/__init__.py create mode 100644 api/tests/src/data_migration/transformation/test_transform_oracle_data.py diff --git a/api/src/data_migration/transformation/transform_oracle_data.py b/api/src/data_migration/transformation/transform_oracle_data.py index 7ebb41d8c..99a434d7c 100644 --- a/api/src/data_migration/transformation/transform_oracle_data.py +++ b/api/src/data_migration/transformation/transform_oracle_data.py @@ -1,14 +1,17 @@ import logging from enum import StrEnum -from typing import Tuple +from typing import Tuple, Type, TypeVar, cast -from sqlalchemy import select, delete +from sqlalchemy import select from src.adapters.db import PostgresDBClient +from src.db.models.base import Base from src.db.models.opportunity_models import Opportunity from src.db.models.transfer.topportunity_models import TransferTopportunity from src.task.task import Task +S = TypeVar("S", bound=Base) +D = TypeVar("D", bound=Base) from src.constants.lookup_constants import OpportunityCategory @@ -22,38 +25,62 @@ class Metrics(StrEnum): TOTAL_RECORDS_INSERTED = "total_records_inserted" TOTAL_RECORDS_UPDATED = "total_records_updated" - + ERROR_COUNT = "error_count" def run_task(self) -> None: with self.db_session.begin(): - self.process_opportunities() # Opportunities + self.process_opportunities() # Assistance Listings + self.process_assistance_listings() # Opportunity Summary + self.process_opportunity_summaries() # One-to-many lookups + self.process_one_to_many_lookup_tables() - def process_opportunities(self) -> None: + def fetch( + self, source_model: Type[S], destination_model: Type[D], join_clause: list + ) -> list[Tuple[S, D | None]]: + # The real type is: Sequence[Row[Tuple[S, D | None]]] + # but MyPy is weird about this and the Row+Tuple causes some + # confusion in the parsing so it ends up assuming everything is Any + # So just cast it to a simpler type that doesn't confuse anything + # TODO - add a yield_per + return cast( + list[Tuple[S, D | None]], + self.db_session.execute( + select(source_model, destination_model, Opportunity).join( + destination_model, *join_clause, isouter=True + ) + ).all(), + ) + + def process_opportunities(self) -> None: # Fetch all opportunities that were modified # Alongside that, grab the existing opportunity record - # TODO - add filters so it only contains updates - # TODO - add a yield_per - opportunities: list[Tuple[TransferTopportunity, Opportunity | None]] = self.db_session.execute( - select(TransferTopportunity, Opportunity).join(Opportunity, TransferTopportunity.opportunity_id == Opportunity.opportunity_id, isouter=True) - ).all() + opportunities: list[Tuple[TransferTopportunity, Opportunity | None]] = self.fetch( + TransferTopportunity, + Opportunity, + [TransferTopportunity.opportunity_id == Opportunity.opportunity_id], + ) - for source_opportunity, target_opportunity in opportunities: + for source_opportunity, target_opportunity, x in opportunities: try: self.process_opportunity(source_opportunity, target_opportunity) except Exception: - # TODO - more info - # TODO - metric - logger.exception("Failed to process opportunity") + self.increment(self.Metrics.ERROR_COUNT) + logger.exception( + "Failed to process opportunity", + extra={"opportunity_id": source_opportunity.opportunity_id}, + ) - def process_opportunity(self, source_opportunity: TransferTopportunity, target_opportunity: Opportunity | None) -> None: + def process_opportunity( + self, source_opportunity: TransferTopportunity, target_opportunity: Opportunity | None + ) -> None: extra = {"opportunity_id": source_opportunity.opportunity_id} logger.info("Processing opportunity", extra=extra) @@ -67,7 +94,6 @@ def process_opportunity(self, source_opportunity: TransferTopportunity, target_o self.increment(self.Metrics.TOTAL_RECORDS_DELETED) self.db_session.delete(target_opportunity) - else: logger.info("Transforming and upserting opportunity", extra=extra) transformed_opportunity = self.transform_opportunity( @@ -75,14 +101,12 @@ def process_opportunity(self, source_opportunity: TransferTopportunity, target_o ) self.db_session.add(transformed_opportunity) - logger.info("Processed opportunity", extra=extra) # TODO - set the field we query by to null (or set it? which way are we doing it?) # target_opportunity.whatever_field = None def process_assistance_listings(self) -> None: # TODO - https://github.com/HHS/simpler-grants-gov/issues/1746 - pass def process_opportunity_summaries(self) -> None: @@ -93,7 +117,6 @@ def process_one_to_many_lookup_tables(self) -> None: # TODO - https://github.com/HHS/simpler-grants-gov/issues/1749 pass - def transform_opportunity( self, source_opportunity: TransferTopportunity, target_opportunity: Opportunity | None ) -> Opportunity: @@ -106,9 +129,7 @@ def transform_opportunity( target_opportunity.opportunity_number = source_opportunity.oppnumber target_opportunity.opportunity_title = source_opportunity.opptitle target_opportunity.agency = source_opportunity.owningagency - target_opportunity.opportunity_category = transform_opportunity_category( - source_opportunity.oppcategory - ) + target_opportunity.category = transform_opportunity_category(source_opportunity.oppcategory) target_opportunity.category_explanation = source_opportunity.category_explanation target_opportunity.is_draft = source_opportunity.is_draft != "N" target_opportunity.revision_number = source_opportunity.revision_number @@ -116,25 +137,31 @@ def transform_opportunity( target_opportunity.publisher_user_id = source_opportunity.publisheruid target_opportunity.publisher_profile_id = source_opportunity.publisher_profile_id + # TODO - do we want to set created_at/updated_at using the values from the legacy + # system or just use "now" (the default)? + return target_opportunity + +OPPORTUNITY_CATEGORY_MAP = { + "D": OpportunityCategory.DISCRETIONARY, + "M": OpportunityCategory.MANDATORY, + "C": OpportunityCategory.CONTINUATION, + "E": OpportunityCategory.EARMARK, + "O": OpportunityCategory.OTHER, +} + + def transform_opportunity_category(value: str | None) -> OpportunityCategory | None: if value is None or value == "": return None - match value: - case "D": - return OpportunityCategory.DISCRETIONARY - case "M": - return OpportunityCategory.MANDATORY - case "C": - return OpportunityCategory.CONTINUATION - case "E": - return OpportunityCategory.EARMARK - case "O": - return OpportunityCategory.OTHER + transformed_value = OPPORTUNITY_CATEGORY_MAP.get(value) - raise ValueError(f"Unrecognized opportunity category") + if transformed_value is None: + raise ValueError(f"Unrecognized opportunity category: %s" % value) + + return transformed_value # TODO - this is likely going to be run as part of a separate script @@ -149,4 +176,5 @@ def main(): with db_client.get_session() as db_session: TransformOracleData(db_session).run() -main() \ No newline at end of file + +main() diff --git a/api/tests/src/data_migration/transformation/__init__.py b/api/tests/src/data_migration/transformation/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data.py new file mode 100644 index 000000000..ad91033a0 --- /dev/null +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data.py @@ -0,0 +1,57 @@ +from typing import Tuple + +import pytest + +from src.data_migration.transformation.transform_oracle_data import TransformOracleData +from src.db.models.opportunity_models import Opportunity +from src.db.models.transfer.topportunity_models import TransferTopportunity +from tests.src.db.models.factories import OpportunityFactory, TransferTopportunityFactory + + +@pytest.fixture() +def transform_oracle_data(db_session) -> TransformOracleData: + return TransformOracleData(db_session) + + +def setup_opportunity( + create_existing: bool, + is_delete: bool, + is_already_processed: bool = False, + transfer_values: dict | None = None, +) -> Tuple[TransferTopportunity, Opportunity | None]: + if transfer_values is None: + transfer_values = {} + + # TODO - right transfer opportunity + transfer_opportunity = TransferTopportunityFactory.create( + **transfer_values + # TODO - use is_delete + # TODO - use is_already_processed + ) + + opportunity = None + if create_existing: + opportunity = OpportunityFactory.create( + opportunity_id=transfer_opportunity.opportunity_id, + # set created_at/updated_at to an earlier time so its clear + # when they were last updated + timestamps_in_past=True, + ) + + return transfer_opportunity, opportunity + + +def validate_opportunity(db_session): + pass + + +def test_process_opportunity(): + # TODO - what do we want to test? + # + # Regular scenarios + # + # + # Error scenarios + # Attempting to delete something that doesn't exist + # Impossible conversions + pass diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index 9553c228d..45db90c28 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -276,6 +276,12 @@ class Params: opportunity_assistance_listings=None, ) + # Set the timestamps in the past rather than using the default of "now" + timestamps_in_past = factory.Trait( + created_at=factory.Faker("date_time_between", start_date="-5y", end_date="-3y"), + updated_at=factory.Faker("date_time_between", start_date="-3y", end_date="-1y"), + ) + class OpportunitySummaryFactory(BaseFactory): class Meta: From 2520062b99d481171b6af93b363239d1299eafd5 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Tue, 23 Apr 2024 16:56:24 -0400 Subject: [PATCH 05/39] Cleanup, still a WIP --- ..._data.py => transform_oracle_data_task.py} | 25 ++-- .../test_transform_oracle_data.py | 57 -------- .../test_transform_oracle_data_task.py | 138 ++++++++++++++++++ api/tests/src/db/models/factories.py | 2 +- 4 files changed, 151 insertions(+), 71 deletions(-) rename api/src/data_migration/transformation/{transform_oracle_data.py => transform_oracle_data_task.py} (89%) delete mode 100644 api/tests/src/data_migration/transformation/test_transform_oracle_data.py create mode 100644 api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py diff --git a/api/src/data_migration/transformation/transform_oracle_data.py b/api/src/data_migration/transformation/transform_oracle_data_task.py similarity index 89% rename from api/src/data_migration/transformation/transform_oracle_data.py rename to api/src/data_migration/transformation/transform_oracle_data_task.py index 99a434d7c..082627f0e 100644 --- a/api/src/data_migration/transformation/transform_oracle_data.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -18,7 +18,7 @@ logger = logging.getLogger(__name__) -class TransformOracleData(Task): +class TransformOracleDataTask(Task): class Metrics(StrEnum): TOTAL_RECORDS_PROCESSED = "total_records_processed" TOTAL_RECORDS_DELETED = "total_records_deleted" @@ -48,14 +48,13 @@ def fetch( # but MyPy is weird about this and the Row+Tuple causes some # confusion in the parsing so it ends up assuming everything is Any # So just cast it to a simpler type that doesn't confuse anything - - # TODO - add a yield_per return cast( list[Tuple[S, D | None]], self.db_session.execute( - select(source_model, destination_model, Opportunity).join( - destination_model, *join_clause, isouter=True - ) + select(source_model, destination_model) + .join(destination_model, *join_clause, isouter=True) + .where(source_model.publisher_profile_id.is_(None)) + .execution_options(yield_per=5000) ).all(), ) @@ -68,10 +67,10 @@ def process_opportunities(self) -> None: [TransferTopportunity.opportunity_id == Opportunity.opportunity_id], ) - for source_opportunity, target_opportunity, x in opportunities: + for source_opportunity, target_opportunity in opportunities: try: self.process_opportunity(source_opportunity, target_opportunity) - except Exception: + except ValueError: self.increment(self.Metrics.ERROR_COUNT) logger.exception( "Failed to process opportunity", @@ -85,11 +84,11 @@ def process_opportunity( logger.info("Processing opportunity", extra=extra) # TODO - whatever check we need to do to see if something should be deleted - if source_opportunity.publisheruid == "should be deleted": + if source_opportunity.publisheruid == "delete_me": logger.info("Deleting opportunity", extra=extra) if target_opportunity is None: - raise Exception("Cannot delete opportunity as it does not exist") + raise ValueError("Cannot delete opportunity as it does not exist") self.increment(self.Metrics.TOTAL_RECORDS_DELETED) self.db_session.delete(target_opportunity) @@ -102,8 +101,8 @@ def process_opportunity( self.db_session.add(transformed_opportunity) logger.info("Processed opportunity", extra=extra) - # TODO - set the field we query by to null (or set it? which way are we doing it?) - # target_opportunity.whatever_field = None + # TODO - set the field we query by correctly, using this field temporarily + source_opportunity.publisher_profile_id = 1 def process_assistance_listings(self) -> None: # TODO - https://github.com/HHS/simpler-grants-gov/issues/1746 @@ -174,7 +173,7 @@ def main(): db_client = PostgresDBClient() with db_client.get_session() as db_session: - TransformOracleData(db_session).run() + TransformOracleDataTask(db_session).run() main() diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data.py deleted file mode 100644 index ad91033a0..000000000 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data.py +++ /dev/null @@ -1,57 +0,0 @@ -from typing import Tuple - -import pytest - -from src.data_migration.transformation.transform_oracle_data import TransformOracleData -from src.db.models.opportunity_models import Opportunity -from src.db.models.transfer.topportunity_models import TransferTopportunity -from tests.src.db.models.factories import OpportunityFactory, TransferTopportunityFactory - - -@pytest.fixture() -def transform_oracle_data(db_session) -> TransformOracleData: - return TransformOracleData(db_session) - - -def setup_opportunity( - create_existing: bool, - is_delete: bool, - is_already_processed: bool = False, - transfer_values: dict | None = None, -) -> Tuple[TransferTopportunity, Opportunity | None]: - if transfer_values is None: - transfer_values = {} - - # TODO - right transfer opportunity - transfer_opportunity = TransferTopportunityFactory.create( - **transfer_values - # TODO - use is_delete - # TODO - use is_already_processed - ) - - opportunity = None - if create_existing: - opportunity = OpportunityFactory.create( - opportunity_id=transfer_opportunity.opportunity_id, - # set created_at/updated_at to an earlier time so its clear - # when they were last updated - timestamps_in_past=True, - ) - - return transfer_opportunity, opportunity - - -def validate_opportunity(db_session): - pass - - -def test_process_opportunity(): - # TODO - what do we want to test? - # - # Regular scenarios - # - # - # Error scenarios - # Attempting to delete something that doesn't exist - # Impossible conversions - pass diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py new file mode 100644 index 000000000..4ce962b33 --- /dev/null +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -0,0 +1,138 @@ +from typing import Tuple + +import pytest + +from src.data_migration.transformation.transform_oracle_data_task import TransformOracleDataTask +from src.db.models.opportunity_models import Opportunity +from src.db.models.transfer.topportunity_models import TransferTopportunity +from tests.conftest import BaseTestClass +from tests.src.db.models.factories import OpportunityFactory, TransferTopportunityFactory + + +def setup_opportunity( + create_existing: bool, + is_delete: bool = False, + is_already_processed: bool = False, + transfer_values: dict | None = None, +) -> TransferTopportunity: + if transfer_values is None: + transfer_values = {} + + # TODO - right transfer opportunity + transfer_opportunity = TransferTopportunityFactory.create( + **transfer_values, + # TODO - use is_delete + # TODO - use is_already_processed + publisheruid="delete_me" if is_delete else None, + publisher_profile_id=1 if is_already_processed else None, + ) + + if create_existing: + OpportunityFactory.create( + opportunity_id=transfer_opportunity.opportunity_id, + # set created_at/updated_at to an earlier time so its clear + # when they were last updated + timestamps_in_past=True, + ) + + return transfer_opportunity + + +def validate_opportunity( + db_session, + source_opportunity: TransferTopportunity, + expect_in_db: bool = True, + expect_values_to_match: bool = True, +): + opportunity = ( + db_session.query(Opportunity) + .filter(Opportunity.opportunity_id == source_opportunity.opportunity_id) + .one_or_none() + ) + + if expect_in_db: + assert opportunity is not None + + # TODO - check created/updated at + # assert opportunity.created_at > datetime.utcnow() + + # TODO - there's gotta be a better way to do this compare + # just check a few basic fields as matching or not + # these are only on fields that can't generate the same + # based on how our factories work for the fields + if expect_values_to_match: + assert opportunity.opportunity_title == source_opportunity.opptitle + assert opportunity.opportunity_number == source_opportunity.oppnumber + else: + assert opportunity.opportunity_title != source_opportunity.opptitle + assert opportunity.opportunity_number != source_opportunity.oppnumber + else: + assert opportunity is None + + +class TestTransformOracleDataTask(BaseTestClass): + @pytest.fixture() + def transform_oracle_data_task( + self, db_session, enable_factory_create + ) -> TransformOracleDataTask: + return TransformOracleDataTask(db_session) + + def test_process_opportunities(self, db_session, transform_oracle_data_task): + ordinary_delete = setup_opportunity(create_existing=True, is_delete=True) + delete_but_current_missing = setup_opportunity( + create_existing=False, is_delete=True + ) # TODO - probably should verify it logged error + + basic_insert = setup_opportunity(create_existing=False) + basic_update = setup_opportunity(create_existing=True) + + # Something else deleted it + already_processed_insert = setup_opportunity( + create_existing=False, is_already_processed=True + ) + already_processed_update = setup_opportunity( + create_existing=True, is_already_processed=True + ) + + insert_that_will_fail = setup_opportunity( + create_existing=False, transfer_values={"oppcategory": "X"} + ) + + transform_oracle_data_task.process_opportunities() + + validate_opportunity(db_session, ordinary_delete, expect_in_db=False) + validate_opportunity(db_session, delete_but_current_missing, expect_in_db=False) + + validate_opportunity(db_session, basic_insert) + validate_opportunity(db_session, basic_update) + + validate_opportunity(db_session, already_processed_insert, expect_in_db=False) + validate_opportunity(db_session, already_processed_update, expect_values_to_match=False) + + validate_opportunity(db_session, insert_that_will_fail, expect_in_db=False) + + # Rerunning won't do anything + # TODO + + # TODO - check metrics + + def test_process_opportunity_delete_but_current_missing( + self, db_session, transform_oracle_data_task + ): + # Verify an error is raised when we try to delete + delete_but_current_missing = setup_opportunity(create_existing=False, is_delete=True) + + with pytest.raises(ValueError, match="Cannot delete opportunity as it does not exist"): + transform_oracle_data_task.process_opportunity(delete_but_current_missing, None) + + validate_opportunity(db_session, delete_but_current_missing, expect_in_db=False) + + def test_process_opportunity_invalid_category(self, db_session, transform_oracle_data_task): + insert_that_will_fail = setup_opportunity( + create_existing=False, transfer_values={"oppcategory": "X"} + ) + + with pytest.raises(ValueError, match="Unrecognized opportunity category"): + transform_oracle_data_task.process_opportunity(insert_that_will_fail, None) + + validate_opportunity(db_session, insert_that_will_fail, expect_in_db=False) diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index 45db90c28..39c647c3b 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -595,7 +595,7 @@ class Meta: opportunity_id = factory.Sequence(lambda n: n) oppnumber = factory.Sequence(lambda n: f"ABC-{n}-XYZ-001") - opptitle = factory.LazyFunction(lambda: f"Research into {fake.job()} industry") + opptitle = factory.LazyFunction(lambda: f"Detailed research into {fake.job()} industry") owningagency = factory.Faker("agency") From 5d4639e5c10cde2a968c28c6cc3d78aa5ccfed80 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Wed, 24 Apr 2024 11:21:09 -0400 Subject: [PATCH 06/39] adding a comment --- .../transformation/test_transform_oracle_data_task.py | 1 + 1 file changed, 1 insertion(+) diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py index 4ce962b33..57925c427 100644 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -128,6 +128,7 @@ def test_process_opportunity_delete_but_current_missing( validate_opportunity(db_session, delete_but_current_missing, expect_in_db=False) def test_process_opportunity_invalid_category(self, db_session, transform_oracle_data_task): + # This will error in the transform as that isn't a category we have configured insert_that_will_fail = setup_opportunity( create_existing=False, transfer_values={"oppcategory": "X"} ) From 44abd4ffbc8a302575680686c011de3abfee232f Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Fri, 26 Apr 2024 12:38:23 -0400 Subject: [PATCH 07/39] Switching to the staging tables --- .../transform_oracle_data_task.py | 39 ++++++++++++------- api/tests/conftest.py | 2 + .../test_transform_oracle_data_task.py | 37 ++++++++---------- api/tests/src/db/models/factories.py | 10 ++++- 4 files changed, 51 insertions(+), 37 deletions(-) diff --git a/api/src/data_migration/transformation/transform_oracle_data_task.py b/api/src/data_migration/transformation/transform_oracle_data_task.py index 082627f0e..b1695cf7b 100644 --- a/api/src/data_migration/transformation/transform_oracle_data_task.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -1,19 +1,23 @@ import logging +from datetime import datetime from enum import StrEnum from typing import Tuple, Type, TypeVar, cast from sqlalchemy import select +from src.adapters import db from src.adapters.db import PostgresDBClient -from src.db.models.base import Base +from src.constants.lookup_constants import OpportunityCategory +from src.db.models.base import ApiSchemaTable from src.db.models.opportunity_models import Opportunity -from src.db.models.transfer.topportunity_models import TransferTopportunity +from src.db.models.staging.opportunity import Topportunity +from src.db.models.staging.staging_base import StagingParamMixin from src.task.task import Task +from src.util import datetime_util -S = TypeVar("S", bound=Base) -D = TypeVar("D", bound=Base) +S = TypeVar("S", bound=StagingParamMixin) +D = TypeVar("D", bound=ApiSchemaTable) -from src.constants.lookup_constants import OpportunityCategory logger = logging.getLogger(__name__) @@ -27,6 +31,13 @@ class Metrics(StrEnum): ERROR_COUNT = "error_count" + def __init__(self, db_session: db.Session, transform_time: datetime | None = None) -> None: + super().__init__(db_session) + + if transform_time is None: + transform_time = datetime_util.utcnow() + self.transform_time = transform_time + def run_task(self) -> None: with self.db_session.begin(): # Opportunities @@ -53,7 +64,7 @@ def fetch( self.db_session.execute( select(source_model, destination_model) .join(destination_model, *join_clause, isouter=True) - .where(source_model.publisher_profile_id.is_(None)) + .where(source_model.transformed_at.is_(None)) .execution_options(yield_per=5000) ).all(), ) @@ -61,10 +72,10 @@ def fetch( def process_opportunities(self) -> None: # Fetch all opportunities that were modified # Alongside that, grab the existing opportunity record - opportunities: list[Tuple[TransferTopportunity, Opportunity | None]] = self.fetch( - TransferTopportunity, + opportunities: list[Tuple[Topportunity, Opportunity | None]] = self.fetch( + Topportunity, Opportunity, - [TransferTopportunity.opportunity_id == Opportunity.opportunity_id], + [Topportunity.opportunity_id == Opportunity.opportunity_id], ) for source_opportunity, target_opportunity in opportunities: @@ -78,13 +89,12 @@ def process_opportunities(self) -> None: ) def process_opportunity( - self, source_opportunity: TransferTopportunity, target_opportunity: Opportunity | None + self, source_opportunity: Topportunity, target_opportunity: Opportunity | None ) -> None: extra = {"opportunity_id": source_opportunity.opportunity_id} logger.info("Processing opportunity", extra=extra) - # TODO - whatever check we need to do to see if something should be deleted - if source_opportunity.publisheruid == "delete_me": + if source_opportunity.is_deleted: logger.info("Deleting opportunity", extra=extra) if target_opportunity is None: @@ -101,8 +111,7 @@ def process_opportunity( self.db_session.add(transformed_opportunity) logger.info("Processed opportunity", extra=extra) - # TODO - set the field we query by correctly, using this field temporarily - source_opportunity.publisher_profile_id = 1 + source_opportunity.transformed_at = self.transform_time def process_assistance_listings(self) -> None: # TODO - https://github.com/HHS/simpler-grants-gov/issues/1746 @@ -117,7 +126,7 @@ def process_one_to_many_lookup_tables(self) -> None: pass def transform_opportunity( - self, source_opportunity: TransferTopportunity, target_opportunity: Opportunity | None + self, source_opportunity: Topportunity, target_opportunity: Opportunity | None ) -> Opportunity: if target_opportunity is None: self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) diff --git a/api/tests/conftest.py b/api/tests/conftest.py index e87f736bf..928932b67 100644 --- a/api/tests/conftest.py +++ b/api/tests/conftest.py @@ -15,6 +15,7 @@ from src.db import models from src.db.models.lookup.sync_lookup_values import sync_lookup_values from src.db.models.opportunity_models import Opportunity +from src.db.models.staging import metadata as staging_metadata from src.util.local import load_local_env_vars from tests.lib import db_testing @@ -99,6 +100,7 @@ def db_client(monkeypatch_session, db_schema_prefix) -> db.DBClient: with db_testing.create_isolated_db(monkeypatch_session, db_schema_prefix) as db_client: with db_client.get_connection() as conn, conn.begin(): models.metadata.create_all(bind=conn) + staging_metadata.create_all(bind=conn) sync_lookup_values(db_client) yield db_client diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py index 57925c427..4bc612a51 100644 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -4,43 +4,40 @@ from src.data_migration.transformation.transform_oracle_data_task import TransformOracleDataTask from src.db.models.opportunity_models import Opportunity -from src.db.models.transfer.topportunity_models import TransferTopportunity +from src.db.models.staging.opportunity import Topportunity from tests.conftest import BaseTestClass -from tests.src.db.models.factories import OpportunityFactory, TransferTopportunityFactory +from tests.src.db.models.factories import OpportunityFactory, StagingTopportunityFactory def setup_opportunity( create_existing: bool, is_delete: bool = False, is_already_processed: bool = False, - transfer_values: dict | None = None, -) -> TransferTopportunity: - if transfer_values is None: - transfer_values = {} - - # TODO - right transfer opportunity - transfer_opportunity = TransferTopportunityFactory.create( - **transfer_values, - # TODO - use is_delete - # TODO - use is_already_processed - publisheruid="delete_me" if is_delete else None, - publisher_profile_id=1 if is_already_processed else None, + source_values: dict | None = None, +) -> Topportunity: + if source_values is None: + source_values = {} + + source_opportunity = StagingTopportunityFactory.create( + **source_values, + is_deleted=is_delete, + already_transformed=is_already_processed, ) if create_existing: OpportunityFactory.create( - opportunity_id=transfer_opportunity.opportunity_id, + opportunity_id=source_opportunity.opportunity_id, # set created_at/updated_at to an earlier time so its clear # when they were last updated timestamps_in_past=True, ) - return transfer_opportunity + return source_opportunity def validate_opportunity( db_session, - source_opportunity: TransferTopportunity, + source_opportunity: Topportunity, expect_in_db: bool = True, expect_values_to_match: bool = True, ): @@ -73,7 +70,7 @@ def validate_opportunity( class TestTransformOracleDataTask(BaseTestClass): @pytest.fixture() def transform_oracle_data_task( - self, db_session, enable_factory_create + self, db_session, enable_factory_create, truncate_opportunities ) -> TransformOracleDataTask: return TransformOracleDataTask(db_session) @@ -95,7 +92,7 @@ def test_process_opportunities(self, db_session, transform_oracle_data_task): ) insert_that_will_fail = setup_opportunity( - create_existing=False, transfer_values={"oppcategory": "X"} + create_existing=False, source_values={"oppcategory": "X"} ) transform_oracle_data_task.process_opportunities() @@ -130,7 +127,7 @@ def test_process_opportunity_delete_but_current_missing( def test_process_opportunity_invalid_category(self, db_session, transform_oracle_data_task): # This will error in the transform as that isn't a category we have configured insert_that_will_fail = setup_opportunity( - create_existing=False, transfer_values={"oppcategory": "X"} + create_existing=False, source_values={"oppcategory": "X"} ) with pytest.raises(ValueError, match="Unrecognized opportunity category"): diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index 36dd01036..d82efd720 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -32,6 +32,13 @@ ) +def sometimes_none(factory_value, none_chance: float = 0.5): + if random.random() > none_chance: + return factory_value + + return None + + class CustomProvider(BaseProvider): """ This class is a custom faker provider that can be used to generate @@ -613,13 +620,12 @@ class Meta: revision_number = 0 created_date = factory.Faker("date_between", start_date="-10y", end_date="-5y") - last_upd_date = factory.Faker("date_between", start_date="-5y", end_date="today") + last_upd_date = sometimes_none(factory.Faker("date_between", start_date="-5y", end_date="today")) is_deleted = False transformed_at = None class Params: - never_updated = factory.Trait(last_upd_date=None) already_transformed = factory.Trait( transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) From 703672e7a8b3de2066f6d537972da175043f03d4 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Fri, 26 Apr 2024 15:29:52 -0400 Subject: [PATCH 08/39] WIP --- .../transform_oracle_data_task.py | 26 +++++++-- .../test_transform_oracle_data_task.py | 53 +++++++++++++++---- api/tests/src/db/models/factories.py | 9 ++-- 3 files changed, 70 insertions(+), 18 deletions(-) diff --git a/api/src/data_migration/transformation/transform_oracle_data_task.py b/api/src/data_migration/transformation/transform_oracle_data_task.py index b1695cf7b..b27d5c5b3 100644 --- a/api/src/data_migration/transformation/transform_oracle_data_task.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -139,14 +139,30 @@ def transform_opportunity( target_opportunity.agency = source_opportunity.owningagency target_opportunity.category = transform_opportunity_category(source_opportunity.oppcategory) target_opportunity.category_explanation = source_opportunity.category_explanation - target_opportunity.is_draft = source_opportunity.is_draft != "N" target_opportunity.revision_number = source_opportunity.revision_number target_opportunity.modified_comments = source_opportunity.modified_comments target_opportunity.publisher_user_id = source_opportunity.publisheruid target_opportunity.publisher_profile_id = source_opportunity.publisher_profile_id - # TODO - do we want to set created_at/updated_at using the values from the legacy - # system or just use "now" (the default)? + # The legacy system doesn't actually have this value as a boolean. There are several + # different letter codes. However, their API implementation also does this for their draft flag. + target_opportunity.is_draft = source_opportunity.is_draft != "N" + + # TODO The timezones in the legacy system are stored in EST, so convert them to UTC here + # TODO - we'll need this for every transform, make it more generic in a function + if source_opportunity.created_date is not None: + target_opportunity.created_at = source_opportunity.created_date + else: + logger.warning("TODO") + target_opportunity.created_at = datetime_util.utcnow() + + if source_opportunity.last_upd_date is not None: + target_opportunity.updated_at = source_opportunity.last_upd_date + else: + # In the legacy system, they don't set whether something was updated + # until it receives an update. We always set the value, and on initial insert + # want it to be the same as the created_at. + target_opportunity.updated_at = target_opportunity.created_at return target_opportunity @@ -167,7 +183,7 @@ def transform_opportunity_category(value: str | None) -> OpportunityCategory | N transformed_value = OPPORTUNITY_CATEGORY_MAP.get(value) if transformed_value is None: - raise ValueError(f"Unrecognized opportunity category: %s" % value) + raise ValueError("Unrecognized opportunity category: %s" % value) return transformed_value @@ -175,7 +191,7 @@ def transform_opportunity_category(value: str | None) -> OpportunityCategory | N # TODO - this is likely going to be run as part of a separate script # but just to help build it out at the moment and test it, setting # an entrypoint for easy local manual testing -def main(): +def main() -> None: import src.logging with src.logging.init("transform_oracle_data"): diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py index 4bc612a51..abfb17a9a 100644 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -35,6 +35,32 @@ def setup_opportunity( return source_opportunity +def validate_matching_fields( + source, destination, fields: list[Tuple[str, str]], expect_all_to_match: bool +): + mismatched_fields = [] + + for source_field, destination_field in fields: + source_value = getattr(source, source_field) + destination_value = getattr(destination, destination_field) + if source_value != destination_value: + mismatched_fields.append( + f"{source_field}/{destination_field}: '{source_value}' != '{destination_value}'" + ) + + # If a values weren't copied in an update + # then we should expect most things to not match, + # but randomness in the factories might cause some overlap + if expect_all_to_match: + assert ( + len(mismatched_fields) == 0 + ), f"Expected all fields to match between {source.__class__} and {destination.__class__}, but found mismatched fields: {','.join(mismatched_fields)}" + else: + assert ( + len(mismatched_fields) != 0 + ), f"Did not expect all fields to match between {source.__class__} and {destination.__class__}, but they did which means an unexpected update occurred" + + def validate_opportunity( db_session, source_opportunity: Topportunity, @@ -53,16 +79,23 @@ def validate_opportunity( # TODO - check created/updated at # assert opportunity.created_at > datetime.utcnow() - # TODO - there's gotta be a better way to do this compare - # just check a few basic fields as matching or not - # these are only on fields that can't generate the same - # based on how our factories work for the fields - if expect_values_to_match: - assert opportunity.opportunity_title == source_opportunity.opptitle - assert opportunity.opportunity_number == source_opportunity.oppnumber - else: - assert opportunity.opportunity_title != source_opportunity.opptitle - assert opportunity.opportunity_number != source_opportunity.oppnumber + # For fields that we expect to match 1:1, verify that they match as expected + validate_matching_fields( + source_opportunity, + opportunity, + [ + ("oppnumber", "opportunity_number"), + ("opptitle", "opportunity_title"), + ("owningagency", "agency"), + ("category_explanation", "category_explanation"), + ("revision_number", "revision_number"), + ("modified_comments", "modified_comments"), + ("publisheruid", "publisher_user_id"), + ("publisher_profile_id", "publisher_profile_id"), + ], + expect_values_to_match, + ) + else: assert opportunity is None diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index d82efd720..a36873da4 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -611,17 +611,20 @@ class Meta: # only set the category explanation if category is Other category_explanation = factory.Maybe( decider=factory.LazyAttribute(lambda o: o.oppcategory == OpportunityCategoryLegacy.OTHER), - yes_declaration=factory.Sequence(lambda n: f"Category as chosen by order #{n * n - 1}"), + yes_declaration=factory.Faker("sentence", nb_words=5), no_declaration=None, ) - is_draft = "N" # Because we filter out drafts, just default these to False + is_draft = "N" revision_number = 0 created_date = factory.Faker("date_between", start_date="-10y", end_date="-5y") - last_upd_date = sometimes_none(factory.Faker("date_between", start_date="-5y", end_date="today")) + last_upd_date = sometimes_none( + factory.Faker("date_between", start_date="-5y", end_date="today") + ) + # Default to being a new insert/update is_deleted = False transformed_at = None From b5e1ebeb0ec8ac1fbcfbcfee0c9aaa51c13eb4b7 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Fri, 26 Apr 2024 15:51:15 -0400 Subject: [PATCH 09/39] WIP --- .../transform_oracle_data_task.py | 2 +- .../test_transform_oracle_data_task.py | 36 +++++++++++++++++-- api/tests/src/db/models/factories.py | 4 +-- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/api/src/data_migration/transformation/transform_oracle_data_task.py b/api/src/data_migration/transformation/transform_oracle_data_task.py index b27d5c5b3..d6e498cd8 100644 --- a/api/src/data_migration/transformation/transform_oracle_data_task.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -153,7 +153,7 @@ def transform_opportunity( if source_opportunity.created_date is not None: target_opportunity.created_at = source_opportunity.created_date else: - logger.warning("TODO") + logger.warning("Opportunity does not have a create timestamp, setting value to now.", {"opportunity_id": source_opportunity.opportunity_id}) target_opportunity.created_at = datetime_util.utcnow() if source_opportunity.last_upd_date is not None: diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py index abfb17a9a..dbe12b610 100644 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -2,7 +2,8 @@ import pytest -from src.data_migration.transformation.transform_oracle_data_task import TransformOracleDataTask +from src.constants.lookup_constants import OpportunityCategory +from src.data_migration.transformation.transform_oracle_data_task import TransformOracleDataTask, transform_opportunity_category from src.db.models.opportunity_models import Opportunity from src.db.models.staging.opportunity import Topportunity from tests.conftest import BaseTestClass @@ -61,6 +62,9 @@ def validate_matching_fields( ), f"Did not expect all fields to match between {source.__class__} and {destination.__class__}, but they did which means an unexpected update occurred" +def validate_create_update_timestamps(): + pass + def validate_opportunity( db_session, source_opportunity: Topportunity, @@ -96,6 +100,16 @@ def validate_opportunity( expect_values_to_match, ) + # TODO - updated_at/created_at + if expect_values_to_match: + # Deliberately doing this in a more direct manner + if source_opportunity.is_draft == "N": + assert opportunity.is_draft is False + else: + assert opportunity.is_draft is True + else: + pass + else: assert opportunity is None @@ -149,7 +163,7 @@ def test_process_opportunities(self, db_session, transform_oracle_data_task): def test_process_opportunity_delete_but_current_missing( self, db_session, transform_oracle_data_task ): - # Verify an error is raised when we try to delete + # Verify an error is raised when we try to delete something that doesn't exist delete_but_current_missing = setup_opportunity(create_existing=False, is_delete=True) with pytest.raises(ValueError, match="Cannot delete opportunity as it does not exist"): @@ -167,3 +181,21 @@ def test_process_opportunity_invalid_category(self, db_session, transform_oracle transform_oracle_data_task.process_opportunity(insert_that_will_fail, None) validate_opportunity(db_session, insert_that_will_fail, expect_in_db=False) + + +@pytest.mark.parametrize("value,expected_value", [ + # Just check a few + ("D", OpportunityCategory.DISCRETIONARY), + ("M", OpportunityCategory.MANDATORY), + ("O", OpportunityCategory.OTHER), + (None, None), + ("", None) +]) +def test_transform_opportunity_category(value, expected_value): + assert transform_opportunity_category(value) == expected_value + +@pytest.mark.parametrize("value", ["A", "B", "mandatory", "other", "hello"]) +def test_transform_opportunity_category_unexpected_value(value): + with pytest.raises(ValueError, match="Unrecognized opportunity category"): + transform_opportunity_category(value) + pass \ No newline at end of file diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index a36873da4..0316f234d 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -619,9 +619,9 @@ class Meta: revision_number = 0 - created_date = factory.Faker("date_between", start_date="-10y", end_date="-5y") + created_date = factory.Faker("date_time_between", start_date="-10y", end_date="-5y") last_upd_date = sometimes_none( - factory.Faker("date_between", start_date="-5y", end_date="today") + factory.Faker("date_time_between", start_date="-5y", end_date="today") ) # Default to being a new insert/update From 38ad4779c1d8cd6773b80c7c6c56695774294a05 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Mon, 29 Apr 2024 12:09:05 -0400 Subject: [PATCH 10/39] Final tests and cleanup --- api/poetry.lock | 29 ++-- api/pyproject.toml | 1 + .../transform_oracle_data_task.py | 147 +++++++++++------- api/src/util/datetime_util.py | 5 + .../test_transform_oracle_data_task.py | 133 +++++++++++++--- api/tests/src/db/models/factories.py | 16 +- 6 files changed, 233 insertions(+), 98 deletions(-) diff --git a/api/poetry.lock b/api/poetry.lock index 9626fa5c9..4f96b968e 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "alembic" @@ -653,6 +653,20 @@ docs = ["Sphinx (==7.2.6)", "marshmallow-sqlalchemy (>=0.19.0)", "sphinx-issues sqlalchemy = ["flask-sqlalchemy (>=3.0.0)", "marshmallow-sqlalchemy (>=0.29.0)"] tests = ["flask-marshmallow[sqlalchemy]", "pytest"] +[[package]] +name = "freezegun" +version = "1.5.0" +description = "Let your Python tests travel through time" +optional = false +python-versions = ">=3.7" +files = [ + {file = "freezegun-1.5.0-py3-none-any.whl", hash = "sha256:ec3f4ba030e34eb6cf7e1e257308aee2c60c3d038ff35996d7475760c9ff3719"}, + {file = "freezegun-1.5.0.tar.gz", hash = "sha256:200a64359b363aa3653d8aac289584078386c7c3da77339d257e46a01fb5c77c"}, +] + +[package.dependencies] +python-dateutil = ">=2.7" + [[package]] name = "greenlet" version = "3.0.3" @@ -1551,7 +1565,6 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1559,16 +1572,8 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1585,7 +1590,6 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1593,7 +1597,6 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -2049,4 +2052,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "65863f6642d7b20a3575ba3161fedf5794be606fc07d78a11b0a075eb367b0c0" +content-hash = "954f01fe8520d326695d60271066fd700497abb3d5bdfe88acb033610c5c56df" diff --git a/api/pyproject.toml b/api/pyproject.toml index b60d7e4d8..e3c59de10 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -42,6 +42,7 @@ pydot = "1.4.2" sadisplay = "0.4.9" ruff = "^0.3.5" debugpy = "^1.8.1" +freezegun = "^1.5.0" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/api/src/data_migration/transformation/transform_oracle_data_task.py b/api/src/data_migration/transformation/transform_oracle_data_task.py index d6e498cd8..54be6c2d1 100644 --- a/api/src/data_migration/transformation/transform_oracle_data_task.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -6,12 +6,11 @@ from sqlalchemy import select from src.adapters import db -from src.adapters.db import PostgresDBClient from src.constants.lookup_constants import OpportunityCategory -from src.db.models.base import ApiSchemaTable +from src.db.models.base import ApiSchemaTable, TimestampMixin from src.db.models.opportunity_models import Opportunity from src.db.models.staging.opportunity import Topportunity -from src.db.models.staging.staging_base import StagingParamMixin +from src.db.models.staging.staging_base import StagingBase, StagingParamMixin from src.task.task import Task from src.util import datetime_util @@ -29,7 +28,7 @@ class Metrics(StrEnum): TOTAL_RECORDS_INSERTED = "total_records_inserted" TOTAL_RECORDS_UPDATED = "total_records_updated" - ERROR_COUNT = "error_count" + TOTAL_ERROR_COUNT = "total_error_count" def __init__(self, db_session: db.Session, transform_time: datetime | None = None) -> None: super().__init__(db_session) @@ -82,7 +81,7 @@ def process_opportunities(self) -> None: try: self.process_opportunity(source_opportunity, target_opportunity) except ValueError: - self.increment(self.Metrics.ERROR_COUNT) + self.increment(self.Metrics.TOTAL_ERROR_COUNT) logger.exception( "Failed to process opportunity", extra={"opportunity_id": source_opportunity.opportunity_id}, @@ -91,6 +90,7 @@ def process_opportunities(self) -> None: def process_opportunity( self, source_opportunity: Topportunity, target_opportunity: Opportunity | None ) -> None: + self.increment(self.Metrics.TOTAL_RECORDS_PROCESSED) extra = {"opportunity_id": source_opportunity.opportunity_id} logger.info("Processing opportunity", extra=extra) @@ -104,12 +104,19 @@ def process_opportunity( self.db_session.delete(target_opportunity) else: + # To avoid incrementing metrics for records we fail to transform, record + # here whether it's an insert/update and we'll increment after transforming + is_insert = target_opportunity is None + logger.info("Transforming and upserting opportunity", extra=extra) - transformed_opportunity = self.transform_opportunity( - source_opportunity, target_opportunity - ) + transformed_opportunity = transform_opportunity(source_opportunity, target_opportunity) self.db_session.add(transformed_opportunity) + if is_insert: + self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + else: + self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) + logger.info("Processed opportunity", extra=extra) source_opportunity.transformed_at = self.transform_time @@ -125,46 +132,37 @@ def process_one_to_many_lookup_tables(self) -> None: # TODO - https://github.com/HHS/simpler-grants-gov/issues/1749 pass - def transform_opportunity( - self, source_opportunity: Topportunity, target_opportunity: Opportunity | None - ) -> Opportunity: - if target_opportunity is None: - self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) - target_opportunity = Opportunity(opportunity_id=source_opportunity.opportunity_id) - else: - self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) - - target_opportunity.opportunity_number = source_opportunity.oppnumber - target_opportunity.opportunity_title = source_opportunity.opptitle - target_opportunity.agency = source_opportunity.owningagency - target_opportunity.category = transform_opportunity_category(source_opportunity.oppcategory) - target_opportunity.category_explanation = source_opportunity.category_explanation - target_opportunity.revision_number = source_opportunity.revision_number - target_opportunity.modified_comments = source_opportunity.modified_comments - target_opportunity.publisher_user_id = source_opportunity.publisheruid - target_opportunity.publisher_profile_id = source_opportunity.publisher_profile_id - - # The legacy system doesn't actually have this value as a boolean. There are several - # different letter codes. However, their API implementation also does this for their draft flag. - target_opportunity.is_draft = source_opportunity.is_draft != "N" - - # TODO The timezones in the legacy system are stored in EST, so convert them to UTC here - # TODO - we'll need this for every transform, make it more generic in a function - if source_opportunity.created_date is not None: - target_opportunity.created_at = source_opportunity.created_date - else: - logger.warning("Opportunity does not have a create timestamp, setting value to now.", {"opportunity_id": source_opportunity.opportunity_id}) - target_opportunity.created_at = datetime_util.utcnow() - if source_opportunity.last_upd_date is not None: - target_opportunity.updated_at = source_opportunity.last_upd_date - else: - # In the legacy system, they don't set whether something was updated - # until it receives an update. We always set the value, and on initial insert - # want it to be the same as the created_at. - target_opportunity.updated_at = target_opportunity.created_at +############################### +# Transformations +############################### + + +def transform_opportunity( + source_opportunity: Topportunity, target_opportunity: Opportunity | None +) -> Opportunity: + log_extra = {"opportunity_id": source_opportunity.opportunity_id} - return target_opportunity + if target_opportunity is None: + logger.info("Creating new opportunity record", extra=log_extra) + target_opportunity = Opportunity(opportunity_id=source_opportunity.opportunity_id) + + target_opportunity.opportunity_number = source_opportunity.oppnumber + target_opportunity.opportunity_title = source_opportunity.opptitle + target_opportunity.agency = source_opportunity.owningagency + target_opportunity.category = transform_opportunity_category(source_opportunity.oppcategory) + target_opportunity.category_explanation = source_opportunity.category_explanation + target_opportunity.revision_number = source_opportunity.revision_number + target_opportunity.modified_comments = source_opportunity.modified_comments + target_opportunity.publisher_user_id = source_opportunity.publisheruid + target_opportunity.publisher_profile_id = source_opportunity.publisher_profile_id + + # The legacy system doesn't actually have this value as a boolean. There are several + # different letter codes. However, their API implementation also does this for their draft flag. + target_opportunity.is_draft = source_opportunity.is_draft != "N" + transform_update_create_timestamp(source_opportunity, target_opportunity, log_extra=log_extra) + + return target_opportunity OPPORTUNITY_CATEGORY_MAP = { @@ -188,17 +186,48 @@ def transform_opportunity_category(value: str | None) -> OpportunityCategory | N return transformed_value -# TODO - this is likely going to be run as part of a separate script -# but just to help build it out at the moment and test it, setting -# an entrypoint for easy local manual testing -def main() -> None: - import src.logging - - with src.logging.init("transform_oracle_data"): - db_client = PostgresDBClient() - - with db_client.get_session() as db_session: - TransformOracleDataTask(db_session).run() - +def convert_est_timestamp_to_utc(timestamp: datetime | None) -> datetime | None: + if timestamp is None: + return None -main() + # The timestamps we get from the legacy system have no timezone info + # but we know the database uses US Eastern timezone by default + # + # First add the America/New_York timezone without any other modification + aware_timestamp = datetime_util.make_timezone_aware(timestamp, "US/Eastern") + # Then adjust the timezone to UTC this will handle any DST or other conversion complexities + return datetime_util.adjust_timezone(aware_timestamp, "UTC") + + +def transform_update_create_timestamp( + source: StagingBase, target: TimestampMixin, log_extra: dict | None = None +) -> None: + # Convert the source timestamps to UTC + # Note: the type ignores are because created_date/last_upd_date are added + # on the individual class definitions, not the base class - due to how + # we need to maintain the column order of the legacy system. + # Every legacy table does have these columns. + created_timestamp = convert_est_timestamp_to_utc(source.created_date) # type: ignore[attr-defined] + updated_timestamp = convert_est_timestamp_to_utc(source.last_upd_date) # type: ignore[attr-defined] + + if created_timestamp is not None: + target.created_at = created_timestamp + else: + # This is incredibly rare, but possible - because our system requires + # we set something, we'll default to the current time and log a warning. + if log_extra is None: + log_extra = {} + + logger.warning( + f"{source.__class__} does not have a created_date timestamp set, setting value to now.", + extra=log_extra, + ) + target.created_at = datetime_util.utcnow() + + if updated_timestamp is not None: + target.updated_at = updated_timestamp + else: + # In the legacy system, they don't set whether something was updated + # until it receives an update. We always set the value, and on initial insert + # want it to be the same as the created_at. + target.updated_at = target.created_at diff --git a/api/src/util/datetime_util.py b/api/src/util/datetime_util.py index 8445f11e4..264b19148 100644 --- a/api/src/util/datetime_util.py +++ b/api/src/util/datetime_util.py @@ -31,6 +31,11 @@ def adjust_timezone(timestamp: datetime, timezone_str: str) -> datetime: return timestamp.astimezone(new_timezone) +def make_timezone_aware(timestamp: datetime, timezone_str: str) -> datetime: + new_timezone = pytz.timezone(timezone_str) + return new_timezone.localize(timestamp) + + def get_now_us_eastern_datetime() -> datetime: """ Return the current time in the eastern time zone. DST is handled based on the local time. diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py index dbe12b610..fbccb46e6 100644 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -1,9 +1,15 @@ +from datetime import datetime from typing import Tuple import pytest +from freezegun import freeze_time from src.constants.lookup_constants import OpportunityCategory -from src.data_migration.transformation.transform_oracle_data_task import TransformOracleDataTask, transform_opportunity_category +from src.data_migration.transformation.transform_oracle_data_task import ( + TransformOracleDataTask, + transform_opportunity_category, + transform_update_create_timestamp, +) from src.db.models.opportunity_models import Opportunity from src.db.models.staging.opportunity import Topportunity from tests.conftest import BaseTestClass @@ -15,6 +21,7 @@ def setup_opportunity( is_delete: bool = False, is_already_processed: bool = False, source_values: dict | None = None, + all_fields_null: bool = False, ) -> Topportunity: if source_values is None: source_values = {} @@ -23,6 +30,7 @@ def setup_opportunity( **source_values, is_deleted=is_delete, already_transformed=is_already_processed, + all_fields_null=all_fields_null, ) if create_existing: @@ -62,9 +70,6 @@ def validate_matching_fields( ), f"Did not expect all fields to match between {source.__class__} and {destination.__class__}, but they did which means an unexpected update occurred" -def validate_create_update_timestamps(): - pass - def validate_opportunity( db_session, source_opportunity: Topportunity, @@ -80,9 +85,6 @@ def validate_opportunity( if expect_in_db: assert opportunity is not None - # TODO - check created/updated at - # assert opportunity.created_at > datetime.utcnow() - # For fields that we expect to match 1:1, verify that they match as expected validate_matching_fields( source_opportunity, @@ -100,9 +102,9 @@ def validate_opportunity( expect_values_to_match, ) - # TODO - updated_at/created_at + # Validation of fields that aren't copied exactly if expect_values_to_match: - # Deliberately doing this in a more direct manner + # Deliberately validating is_draft with a different calculation if source_opportunity.is_draft == "N": assert opportunity.is_draft is False else: @@ -122,13 +124,26 @@ def transform_oracle_data_task( return TransformOracleDataTask(db_session) def test_process_opportunities(self, db_session, transform_oracle_data_task): - ordinary_delete = setup_opportunity(create_existing=True, is_delete=True) + ordinary_delete = setup_opportunity( + create_existing=True, is_delete=True, all_fields_null=True + ) + ordinary_delete2 = setup_opportunity( + create_existing=True, is_delete=True, all_fields_null=False + ) delete_but_current_missing = setup_opportunity( create_existing=False, is_delete=True ) # TODO - probably should verify it logged error basic_insert = setup_opportunity(create_existing=False) - basic_update = setup_opportunity(create_existing=True) + basic_insert2 = setup_opportunity(create_existing=False, all_fields_null=True) + basic_insert3 = setup_opportunity(create_existing=False) + + basic_update = setup_opportunity( + create_existing=True, + ) + basic_update2 = setup_opportunity(create_existing=True, all_fields_null=True) + basic_update3 = setup_opportunity(create_existing=True, all_fields_null=True) + basic_update4 = setup_opportunity(create_existing=True) # Something else deleted it already_processed_insert = setup_opportunity( @@ -145,20 +160,40 @@ def test_process_opportunities(self, db_session, transform_oracle_data_task): transform_oracle_data_task.process_opportunities() validate_opportunity(db_session, ordinary_delete, expect_in_db=False) + validate_opportunity(db_session, ordinary_delete2, expect_in_db=False) validate_opportunity(db_session, delete_but_current_missing, expect_in_db=False) validate_opportunity(db_session, basic_insert) + validate_opportunity(db_session, basic_insert2) + validate_opportunity(db_session, basic_insert3) + validate_opportunity(db_session, basic_update) + validate_opportunity(db_session, basic_update2) + validate_opportunity(db_session, basic_update3) + validate_opportunity(db_session, basic_update4) validate_opportunity(db_session, already_processed_insert, expect_in_db=False) validate_opportunity(db_session, already_processed_update, expect_values_to_match=False) validate_opportunity(db_session, insert_that_will_fail, expect_in_db=False) - # Rerunning won't do anything - # TODO + metrics = transform_oracle_data_task.metrics + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED] == 11 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_DELETED] == 2 + # Note this insert counts the case where the category fails + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_INSERTED] == 3 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_UPDATED] == 4 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_ERROR_COUNT] == 2 - # TODO - check metrics + # Rerunning does mostly nothing, it will attempt to re-process the two that errored + # but otherwise won't find anything else + transform_oracle_data_task.process_opportunities() + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED] == 13 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_DELETED] == 2 + # Note this insert counts the case where the category fails + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_INSERTED] == 3 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_UPDATED] == 4 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_ERROR_COUNT] == 4 def test_process_opportunity_delete_but_current_missing( self, db_session, transform_oracle_data_task @@ -183,19 +218,71 @@ def test_process_opportunity_invalid_category(self, db_session, transform_oracle validate_opportunity(db_session, insert_that_will_fail, expect_in_db=False) -@pytest.mark.parametrize("value,expected_value", [ - # Just check a few - ("D", OpportunityCategory.DISCRETIONARY), - ("M", OpportunityCategory.MANDATORY), - ("O", OpportunityCategory.OTHER), - (None, None), - ("", None) -]) +@pytest.mark.parametrize( + "value,expected_value", + [ + # Just check a few + ("D", OpportunityCategory.DISCRETIONARY), + ("M", OpportunityCategory.MANDATORY), + ("O", OpportunityCategory.OTHER), + (None, None), + ("", None), + ], +) def test_transform_opportunity_category(value, expected_value): assert transform_opportunity_category(value) == expected_value + @pytest.mark.parametrize("value", ["A", "B", "mandatory", "other", "hello"]) def test_transform_opportunity_category_unexpected_value(value): with pytest.raises(ValueError, match="Unrecognized opportunity category"): transform_opportunity_category(value) - pass \ No newline at end of file + + +@pytest.mark.parametrize( + "created_date,last_upd_date,expected_created_at,expected_updated_at", + [ + ### Using string timestamps rather than defining the dates directly for readability + # A few happy scenarios + ( + "2020-01-01T12:00:00", + "2020-06-01T12:00:00", + "2020-01-01T17:00:00+00:00", + "2020-06-01T16:00:00+00:00", + ), + ( + "2021-01-31T21:30:15", + "2021-12-31T23:59:59", + "2021-02-01T02:30:15+00:00", + "2022-01-01T04:59:59+00:00", + ), + # Leap year handling + ( + "2024-02-28T23:00:59", + "2024-02-29T19:10:10", + "2024-02-29T04:00:59+00:00", + "2024-03-01T00:10:10+00:00", + ), + # last_upd_date is None, created_date is used for both + ("2020-05-31T16:32:08", None, "2020-05-31T20:32:08+00:00", "2020-05-31T20:32:08+00:00"), + ("2020-07-15T20:00:00", None, "2020-07-16T00:00:00+00:00", "2020-07-16T00:00:00+00:00"), + # both input values are None, the current time is used (which we set for the purposes of this test below) + (None, None, "2023-05-10T12:00:00+00:00", "2023-05-10T12:00:00+00:00"), + ], +) +@freeze_time("2023-05-10 12:00:00", tz_offset=0) +def test_transform_update_create_timestamp( + created_date, last_upd_date, expected_created_at, expected_updated_at +): + created_datetime = datetime.fromisoformat(created_date) if created_date is not None else None + last_upd_datetime = datetime.fromisoformat(last_upd_date) if last_upd_date is not None else None + + source = StagingTopportunityFactory.build( + created_date=created_datetime, last_upd_date=last_upd_datetime + ) + destination = OpportunityFactory.build() + + transform_update_create_timestamp(source, destination) + + assert destination.created_at == datetime.fromisoformat(expected_created_at) + assert destination.updated_at == datetime.fromisoformat(expected_updated_at) diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index 0316f234d..c1f878d88 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -615,13 +615,13 @@ class Meta: no_declaration=None, ) - is_draft = "N" + is_draft = factory.fuzzy.FuzzyChoice(["N", "S"]) - revision_number = 0 + revision_number = factory.Faker("random_int", min=1, max=10) created_date = factory.Faker("date_time_between", start_date="-10y", end_date="-5y") last_upd_date = sometimes_none( - factory.Faker("date_time_between", start_date="-5y", end_date="today") + factory.Faker("date_time_between", start_date="-5y", end_date="now") ) # Default to being a new insert/update @@ -633,6 +633,16 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) + # Trait to set all nullable fields to None + all_fields_null = factory.Trait( + oppnumber=None, + revision_number=None, + opptitle=None, + owningagency=None, + oppcategory=None, + category_explanation=None, + ) + #################################### # Transfer Table Factories From e1dfff0f134b13397d932432d7ee79c97c6e214f Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Mon, 29 Apr 2024 12:26:46 -0400 Subject: [PATCH 11/39] Minor adjustment --- .../test_transform_oracle_data_task.py | 61 +++++++++---------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py index fbccb46e6..4917195cc 100644 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -82,38 +82,35 @@ def validate_opportunity( .one_or_none() ) - if expect_in_db: - assert opportunity is not None - - # For fields that we expect to match 1:1, verify that they match as expected - validate_matching_fields( - source_opportunity, - opportunity, - [ - ("oppnumber", "opportunity_number"), - ("opptitle", "opportunity_title"), - ("owningagency", "agency"), - ("category_explanation", "category_explanation"), - ("revision_number", "revision_number"), - ("modified_comments", "modified_comments"), - ("publisheruid", "publisher_user_id"), - ("publisher_profile_id", "publisher_profile_id"), - ], - expect_values_to_match, - ) + if not expect_in_db: + assert opportunity is None + return + + assert opportunity is not None + # For fields that we expect to match 1:1, verify that they match as expected + validate_matching_fields( + source_opportunity, + opportunity, + [ + ("oppnumber", "opportunity_number"), + ("opptitle", "opportunity_title"), + ("owningagency", "agency"), + ("category_explanation", "category_explanation"), + ("revision_number", "revision_number"), + ("modified_comments", "modified_comments"), + ("publisheruid", "publisher_user_id"), + ("publisher_profile_id", "publisher_profile_id"), + ], + expect_values_to_match, + ) - # Validation of fields that aren't copied exactly - if expect_values_to_match: - # Deliberately validating is_draft with a different calculation - if source_opportunity.is_draft == "N": - assert opportunity.is_draft is False - else: - assert opportunity.is_draft is True + # Validation of fields that aren't copied exactly + if expect_values_to_match: + # Deliberately validating is_draft with a different calculation + if source_opportunity.is_draft == "N": + assert opportunity.is_draft is False else: - pass - - else: - assert opportunity is None + assert opportunity.is_draft is True class TestTransformOracleDataTask(BaseTestClass): @@ -130,9 +127,7 @@ def test_process_opportunities(self, db_session, transform_oracle_data_task): ordinary_delete2 = setup_opportunity( create_existing=True, is_delete=True, all_fields_null=False ) - delete_but_current_missing = setup_opportunity( - create_existing=False, is_delete=True - ) # TODO - probably should verify it logged error + delete_but_current_missing = setup_opportunity(create_existing=False, is_delete=True) basic_insert = setup_opportunity(create_existing=False) basic_insert2 = setup_opportunity(create_existing=False, all_fields_null=True) From 54774fd952e6acbd6a5c58e987f0410c213b5826 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Mon, 29 Apr 2024 14:41:22 -0400 Subject: [PATCH 12/39] [Issue #1746] Add transformations for assistance listing table --- .../transform_oracle_data_task.py | 79 ++++++++++++++++++- api/src/db/models/staging/opportunity.py | 4 + api/tests/src/db/models/factories.py | 35 ++++++++ 3 files changed, 114 insertions(+), 4 deletions(-) diff --git a/api/src/data_migration/transformation/transform_oracle_data_task.py b/api/src/data_migration/transformation/transform_oracle_data_task.py index 54be6c2d1..167984ab0 100644 --- a/api/src/data_migration/transformation/transform_oracle_data_task.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -8,8 +8,8 @@ from src.adapters import db from src.constants.lookup_constants import OpportunityCategory from src.db.models.base import ApiSchemaTable, TimestampMixin -from src.db.models.opportunity_models import Opportunity -from src.db.models.staging.opportunity import Topportunity +from src.db.models.opportunity_models import Opportunity, OpportunityAssistanceListing +from src.db.models.staging.opportunity import Topportunity, TopportunityCfda from src.db.models.staging.staging_base import StagingBase, StagingParamMixin from src.task.task import Task from src.util import datetime_util @@ -68,6 +68,15 @@ def fetch( ).all(), ) + def fetch_with_opportunity(self, source_model: Type[S], destination_model: Type[D], join_clause: list) -> list[Tuple[S, D | None, Opportunity | None]]: + return self.db_session.execute( + select(source_model, destination_model, Opportunity) + .join(destination_model, *join_clause, isouter=True) + .join(Opportunity, source_model.opportunity_id == Opportunity.opportunity_id) + .where(source_model.transformed_at.is_(None)) + .execution_options(yield_per=5000) + ).all() + def process_opportunities(self) -> None: # Fetch all opportunities that were modified # Alongside that, grab the existing opportunity record @@ -121,8 +130,58 @@ def process_opportunity( source_opportunity.transformed_at = self.transform_time def process_assistance_listings(self) -> None: - # TODO - https://github.com/HHS/simpler-grants-gov/issues/1746 - pass + + assistance_listings: list[Tuple[TopportunityCfda, OpportunityAssistanceListing | None, Opportunity | None]] = self.fetch_with_opportunity( + TopportunityCfda, + OpportunityAssistanceListing, + [TopportunityCfda.opp_cfda_id == OpportunityAssistanceListing.opportunity_assistance_listing_id] + ) + + for source_assistance_listing, target_assistance_listing, opportunity in assistance_listings: + try: + # TODO - docs & more detail + if opportunity is None: + logger.warning("TODO") + continue + + self.process_assistance_listing(source_assistance_listing, target_assistance_listing) + except ValueError: + self.increment(self.Metrics.TOTAL_ERROR_COUNT) + logger.exception( + "Failed to process assistance listing", + extra={"opportunity_assistance_listing_id": source_assistance_listing.opp_cfda_id}, + ) + + def process_assistance_listing(self, source_assistance_listing: TopportunityCfda, target_assistance_listing: OpportunityAssistanceListing | None) -> None: + self.increment(self.Metrics.TOTAL_RECORDS_PROCESSED) + extra = {"opportunity_assistance_listing_id": source_assistance_listing.opp_cfda_id} + logger.info("Processing assistance listing", extra=extra) + + if source_assistance_listing.is_deleted: + logger.info("Deleting assistance listing", extra=extra) + + if target_assistance_listing is None: + raise ValueError("Cannot delete assistance listing as it does not exist") + + self.increment(self.Metrics.TOTAL_RECORDS_DELETED) + self.db_session.delete(target_assistance_listing) + + else: + # To avoid incrementing metrics for records we fail to transform, record + # here whether it's an insert/update and we'll increment after transforming + is_insert = target_assistance_listing is None + + logger.info("Transforming and upserting assistance listing", extra=extra) + transformed_assistance_listing = transform_assistance_listing(source_assistance_listing, target_assistance_listing) + self.db_session.add(transformed_assistance_listing) + + if is_insert: + self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + else: + self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) + + logger.info("Processed assistance listing", extra=extra) + source_assistance_listing.transformed_at = self.transform_time def process_opportunity_summaries(self) -> None: # TODO - https://github.com/HHS/simpler-grants-gov/issues/1747 @@ -186,6 +245,18 @@ def transform_opportunity_category(value: str | None) -> OpportunityCategory | N return transformed_value +def transform_assistance_listing(source_assistance_listing: TopportunityCfda, target_assistance_listing: OpportunityAssistanceListing | None) -> OpportunityAssistanceListing: + log_extra = {"opportunity_assistance_listing_id": source_assistance_listing.opp_cfda_id} + + if target_assistance_listing is None: + logger.info("Creating new assistance listing record", extra=log_extra) + target_assistance_listing = OpportunityAssistanceListing(opportunity_assistance_listing_id=source_assistance_listing.opp_cfda_id, opportunity_id=source_assistance_listing.opportunity_id) + + target_assistance_listing.assistance_listing_number = source_assistance_listing.cfdanumber + target_assistance_listing.program_title = source_assistance_listing.programtitle + + transform_update_create_timestamp(source_assistance_listing, target_assistance_listing, log_extra=log_extra) + def convert_est_timestamp_to_utc(timestamp: datetime | None) -> datetime | None: if timestamp is None: return None diff --git a/api/src/db/models/staging/opportunity.py b/api/src/db/models/staging/opportunity.py index 5ec200645..5dc7c222b 100644 --- a/api/src/db/models/staging/opportunity.py +++ b/api/src/db/models/staging/opportunity.py @@ -1,10 +1,14 @@ from src.db.legacy_mixin import opportunity_mixin from src.db.models.staging.staging_base import StagingBase, StagingParamMixin +from sqlalchemy.orm import Mapped, relationship class Topportunity(StagingBase, opportunity_mixin.TopportunityMixin, StagingParamMixin): __tablename__ = "topportunity" + cfdas: Mapped[list["TopportunityCfda"]] = relationship(primaryjoin="remote(Topportunity.opportunity_id) == foreign(TopportunityCfda.opportunity_id)", uselist=True) class TopportunityCfda(StagingBase, opportunity_mixin.TopportunityCfdaMixin, StagingParamMixin): __tablename__ = "topportunity_cfda" + + opportunity: Mapped[Topportunity | None] = relationship(primaryjoin="remote(TopportunityCfda.opportunity_id) == foreign(Topportunity.opportunity_id)", uselist=False) \ No newline at end of file diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index c1f878d88..5cd11b194 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -595,6 +595,12 @@ class Meta: # Staging Table Factories #################################### +class ForeignTopportunityFactory(BaseFactory): + class Meta: + model = x + + + class StagingTopportunityFactory(BaseFactory): class Meta: @@ -628,6 +634,12 @@ class Meta: is_deleted = False transformed_at = None + cfdas = factory.RelatedFactoryList( + "tests.src.db.models.factories.StagingTopportunityCfdaFactory", + factory_related_name="opportunity", + size=lambda: random.randint(1, 3), + ) + class Params: already_transformed = factory.Trait( transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") @@ -643,6 +655,29 @@ class Params: category_explanation=None, ) +class StagingTopportunityCfdaFactory(BaseFactory): + class Meta: + model = staging.opportunity.TopportunityCfda + + opp_cfda_id = factory.Sequence(lambda n: n) + + opportunity = factory.SubFactory(StagingTopportunityFactory) + opportunity_id = factory.LazyAttribute(lambda s: s.opportunity.opportunity_id) + + programtitle = factory.Faker("company") + cfdanumber = factory.LazyFunction( + lambda: f"{fake.random_int(min=1, max=99):02}.{fake.random_int(min=1, max=999):03}" + ) + + created_date = factory.Faker("date_time_between", start_date="-10y", end_date="-5y") + last_upd_date = sometimes_none( + factory.Faker("date_time_between", start_date="-5y", end_date="now") + ) + + # Default to being a new insert/update + is_deleted = False + transformed_at = None + #################################### # Transfer Table Factories From 7954c594e8b674bac9c23385a550ed4854245e69 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Mon, 29 Apr 2024 14:42:48 -0400 Subject: [PATCH 13/39] trim --- api/tests/src/db/models/factories.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index 5cd11b194..46d1e742d 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -595,11 +595,6 @@ class Meta: # Staging Table Factories #################################### -class ForeignTopportunityFactory(BaseFactory): - class Meta: - model = x - - class StagingTopportunityFactory(BaseFactory): From fce4cafbc4191782249ac76b949537d9ccc76139 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Tue, 30 Apr 2024 13:26:45 -0400 Subject: [PATCH 14/39] Adding more tests, cleanup, fixing warnings --- .../transform_oracle_data_task.py | 105 ++++++-- api/src/db/models/staging/opportunity.py | 14 +- .../test_transform_oracle_data_task.py | 226 +++++++++++++++++- api/tests/src/db/models/factories.py | 22 +- 4 files changed, 333 insertions(+), 34 deletions(-) diff --git a/api/src/data_migration/transformation/transform_oracle_data_task.py b/api/src/data_migration/transformation/transform_oracle_data_task.py index 167984ab0..632d28180 100644 --- a/api/src/data_migration/transformation/transform_oracle_data_task.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -27,6 +27,7 @@ class Metrics(StrEnum): TOTAL_RECORDS_DELETED = "total_records_deleted" TOTAL_RECORDS_INSERTED = "total_records_inserted" TOTAL_RECORDS_UPDATED = "total_records_updated" + TOTAL_RECORDS_ORPHANED = "total_records_orphaned" TOTAL_ERROR_COUNT = "total_error_count" @@ -68,14 +69,26 @@ def fetch( ).all(), ) - def fetch_with_opportunity(self, source_model: Type[S], destination_model: Type[D], join_clause: list) -> list[Tuple[S, D | None, Opportunity | None]]: - return self.db_session.execute( - select(source_model, destination_model, Opportunity) - .join(destination_model, *join_clause, isouter=True) - .join(Opportunity, source_model.opportunity_id == Opportunity.opportunity_id) - .where(source_model.transformed_at.is_(None)) - .execution_options(yield_per=5000) - ).all() + def fetch_with_opportunity( + self, source_model: Type[S], destination_model: Type[D], join_clause: list + ) -> list[Tuple[S, D | None, Opportunity | None]]: + # Similar to the above fetch function, but also grabs an opportunity record + # Note that this requires your source_model to have an opportunity_id field defined. + + return cast( + list[Tuple[S, D | None, Opportunity | None]], + self.db_session.execute( + select(source_model, destination_model, Opportunity) + .join(destination_model, *join_clause, isouter=True) + .join( + Opportunity, + source_model.opportunity_id == Opportunity.opportunity_id, # type: ignore[attr-defined] + isouter=True, + ) + .where(source_model.transformed_at.is_(None)) + .execution_options(yield_per=5000) + ).all(), + ) def process_opportunities(self) -> None: # Fetch all opportunities that were modified @@ -130,34 +143,61 @@ def process_opportunity( source_opportunity.transformed_at = self.transform_time def process_assistance_listings(self) -> None: - - assistance_listings: list[Tuple[TopportunityCfda, OpportunityAssistanceListing | None, Opportunity | None]] = self.fetch_with_opportunity( + assistance_listings: list[ + Tuple[TopportunityCfda, OpportunityAssistanceListing | None, Opportunity | None] + ] = self.fetch_with_opportunity( TopportunityCfda, OpportunityAssistanceListing, - [TopportunityCfda.opp_cfda_id == OpportunityAssistanceListing.opportunity_assistance_listing_id] + [ + TopportunityCfda.opp_cfda_id + == OpportunityAssistanceListing.opportunity_assistance_listing_id + ], ) - for source_assistance_listing, target_assistance_listing, opportunity in assistance_listings: + for ( + source_assistance_listing, + target_assistance_listing, + opportunity, + ) in assistance_listings: try: - # TODO - docs & more detail - if opportunity is None: - logger.warning("TODO") - continue - - self.process_assistance_listing(source_assistance_listing, target_assistance_listing) + self.process_assistance_listing( + source_assistance_listing, target_assistance_listing, opportunity + ) except ValueError: self.increment(self.Metrics.TOTAL_ERROR_COUNT) logger.exception( "Failed to process assistance listing", - extra={"opportunity_assistance_listing_id": source_assistance_listing.opp_cfda_id}, + extra={ + "opportunity_assistance_listing_id": source_assistance_listing.opp_cfda_id + }, ) - def process_assistance_listing(self, source_assistance_listing: TopportunityCfda, target_assistance_listing: OpportunityAssistanceListing | None) -> None: + def process_assistance_listing( + self, + source_assistance_listing: TopportunityCfda, + target_assistance_listing: OpportunityAssistanceListing | None, + opportunity: Opportunity | None, + ) -> None: self.increment(self.Metrics.TOTAL_RECORDS_PROCESSED) - extra = {"opportunity_assistance_listing_id": source_assistance_listing.opp_cfda_id} + extra = { + "opportunity_assistance_listing_id": source_assistance_listing.opp_cfda_id, + "opportunity_id": source_assistance_listing.opportunity_id, + } logger.info("Processing assistance listing", extra=extra) - if source_assistance_listing.is_deleted: + if opportunity is None: + # The Oracle system we're importing these from does not have a foreign key between + # the opportunity ID in the TOPPORTUNITY_CFDA table and the TOPPORTUNITY table. + # There are many (2306 as of writing) orphaned CFDA records, created between 2007 and 2011 + # We don't want to continuously process these, so won't error for these, and will just + # mark them as transformed below. + self.increment(self.Metrics.TOTAL_RECORDS_ORPHANED) + logger.info( + "Assistance listing is orphaned and does not connect to any opportunity", + extra=extra, + ) + + elif source_assistance_listing.is_deleted: logger.info("Deleting assistance listing", extra=extra) if target_assistance_listing is None: @@ -172,7 +212,9 @@ def process_assistance_listing(self, source_assistance_listing: TopportunityCfda is_insert = target_assistance_listing is None logger.info("Transforming and upserting assistance listing", extra=extra) - transformed_assistance_listing = transform_assistance_listing(source_assistance_listing, target_assistance_listing) + transformed_assistance_listing = transform_assistance_listing( + source_assistance_listing, target_assistance_listing + ) self.db_session.add(transformed_assistance_listing) if is_insert: @@ -245,17 +287,28 @@ def transform_opportunity_category(value: str | None) -> OpportunityCategory | N return transformed_value -def transform_assistance_listing(source_assistance_listing: TopportunityCfda, target_assistance_listing: OpportunityAssistanceListing | None) -> OpportunityAssistanceListing: +def transform_assistance_listing( + source_assistance_listing: TopportunityCfda, + target_assistance_listing: OpportunityAssistanceListing | None, +) -> OpportunityAssistanceListing: log_extra = {"opportunity_assistance_listing_id": source_assistance_listing.opp_cfda_id} if target_assistance_listing is None: logger.info("Creating new assistance listing record", extra=log_extra) - target_assistance_listing = OpportunityAssistanceListing(opportunity_assistance_listing_id=source_assistance_listing.opp_cfda_id, opportunity_id=source_assistance_listing.opportunity_id) + target_assistance_listing = OpportunityAssistanceListing( + opportunity_assistance_listing_id=source_assistance_listing.opp_cfda_id, + opportunity_id=source_assistance_listing.opportunity_id, + ) target_assistance_listing.assistance_listing_number = source_assistance_listing.cfdanumber target_assistance_listing.program_title = source_assistance_listing.programtitle - transform_update_create_timestamp(source_assistance_listing, target_assistance_listing, log_extra=log_extra) + transform_update_create_timestamp( + source_assistance_listing, target_assistance_listing, log_extra=log_extra + ) + + return target_assistance_listing + def convert_est_timestamp_to_utc(timestamp: datetime | None) -> datetime | None: if timestamp is None: diff --git a/api/src/db/models/staging/opportunity.py b/api/src/db/models/staging/opportunity.py index 5dc7c222b..7840a71f8 100644 --- a/api/src/db/models/staging/opportunity.py +++ b/api/src/db/models/staging/opportunity.py @@ -1,14 +1,22 @@ +from sqlalchemy.orm import Mapped, relationship + from src.db.legacy_mixin import opportunity_mixin from src.db.models.staging.staging_base import StagingBase, StagingParamMixin -from sqlalchemy.orm import Mapped, relationship class Topportunity(StagingBase, opportunity_mixin.TopportunityMixin, StagingParamMixin): __tablename__ = "topportunity" - cfdas: Mapped[list["TopportunityCfda"]] = relationship(primaryjoin="remote(Topportunity.opportunity_id) == foreign(TopportunityCfda.opportunity_id)", uselist=True) + cfdas: Mapped[list["TopportunityCfda"]] = relationship( + primaryjoin="Topportunity.opportunity_id == foreign(TopportunityCfda.opportunity_id)", + uselist=True, + ) + class TopportunityCfda(StagingBase, opportunity_mixin.TopportunityCfdaMixin, StagingParamMixin): __tablename__ = "topportunity_cfda" - opportunity: Mapped[Topportunity | None] = relationship(primaryjoin="remote(TopportunityCfda.opportunity_id) == foreign(Topportunity.opportunity_id)", uselist=False) \ No newline at end of file + opportunity: Mapped[Topportunity | None] = relationship( + primaryjoin="TopportunityCfda.opportunity_id == foreign(Topportunity.opportunity_id)", + uselist=False, + ) diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py index 4917195cc..8f5f56f5b 100644 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -10,10 +10,15 @@ transform_opportunity_category, transform_update_create_timestamp, ) -from src.db.models.opportunity_models import Opportunity -from src.db.models.staging.opportunity import Topportunity +from src.db.models.opportunity_models import Opportunity, OpportunityAssistanceListing +from src.db.models.staging.opportunity import Topportunity, TopportunityCfda from tests.conftest import BaseTestClass -from tests.src.db.models.factories import OpportunityFactory, StagingTopportunityFactory +from tests.src.db.models.factories import ( + OpportunityAssistanceListingFactory, + OpportunityFactory, + StagingTopportunityCfdaFactory, + StagingTopportunityFactory, +) def setup_opportunity( @@ -31,6 +36,7 @@ def setup_opportunity( is_deleted=is_delete, already_transformed=is_already_processed, all_fields_null=all_fields_null, + cfdas=[], ) if create_existing: @@ -44,6 +50,41 @@ def setup_opportunity( return source_opportunity +def setup_cfda( + create_existing: bool, + is_delete: bool = False, + is_already_processed: bool = False, + source_values: dict | None = None, + all_fields_null: bool = False, + opportunity: Opportunity | None = None, +) -> TopportunityCfda: + if source_values is None: + source_values = {} + + # If you don't provide an opportunity, you need to provide an ID + if opportunity is not None: + source_values["opportunity_id"] = opportunity.opportunity_id + + source_cfda = StagingTopportunityCfdaFactory.create( + **source_values, + opportunity=None, # To override the factory trying to create something + is_deleted=is_delete, + already_transformed=is_already_processed, + all_fields_null=all_fields_null, + ) + + if create_existing: + OpportunityAssistanceListingFactory.create( + opportunity=opportunity, + opportunity_assistance_listing_id=source_cfda.opp_cfda_id, + # set created_at/updated_at to an earlier time so its clear + # when they were last updated + timestamps_in_past=True, + ) + + return source_cfda + + def validate_matching_fields( source, destination, fields: list[Tuple[str, str]], expect_all_to_match: bool ): @@ -113,7 +154,39 @@ def validate_opportunity( assert opportunity.is_draft is True -class TestTransformOracleDataTask(BaseTestClass): +def validate_assistance_listing( + db_session, + source_cfda: TopportunityCfda, + expect_in_db: bool = True, + expect_values_to_match: bool = True, +): + assistance_listing = ( + db_session.query(OpportunityAssistanceListing) + .filter( + OpportunityAssistanceListing.opportunity_assistance_listing_id + == source_cfda.opp_cfda_id + ) + .one_or_none() + ) + + if not expect_in_db: + assert assistance_listing is None + return + + assert assistance_listing is not None + # For fields that we expect to match 1:1, verify that they match as expected + validate_matching_fields( + source_cfda, + assistance_listing, + [ + ("cfdanumber", "assistance_listing_number"), + ("programtitle", "program_title"), + ], + expect_values_to_match, + ) + + +class TestTransformOpportunity(BaseTestClass): @pytest.fixture() def transform_oracle_data_task( self, db_session, enable_factory_create, truncate_opportunities @@ -213,6 +286,151 @@ def test_process_opportunity_invalid_category(self, db_session, transform_oracle validate_opportunity(db_session, insert_that_will_fail, expect_in_db=False) +class TestTransformAssistanceListing(BaseTestClass): + @pytest.fixture() + def transform_oracle_data_task( + self, db_session, enable_factory_create, truncate_opportunities + ) -> TransformOracleDataTask: + return TransformOracleDataTask(db_session) + + def test_process_opportunity_assistance_listings(self, db_session, transform_oracle_data_task): + opportunity1 = OpportunityFactory.create(opportunity_assistance_listings=[]) + cfda_insert1 = setup_cfda(create_existing=False, opportunity=opportunity1) + cfda_insert2 = setup_cfda(create_existing=False, opportunity=opportunity1) + cfda_update1 = setup_cfda(create_existing=True, opportunity=opportunity1) + cfda_delete1 = setup_cfda(create_existing=True, is_delete=True, opportunity=opportunity1) + cfda_update_already_processed1 = setup_cfda( + create_existing=True, is_already_processed=True, opportunity=opportunity1 + ) + + opportunity2 = OpportunityFactory.create(opportunity_assistance_listings=[]) + cfda_insert3 = setup_cfda(create_existing=False, opportunity=opportunity2) + cfda_update_already_processed2 = setup_cfda( + create_existing=True, is_already_processed=True, opportunity=opportunity2 + ) + cfda_delete_already_processed1 = setup_cfda( + create_existing=False, + is_already_processed=True, + is_delete=True, + opportunity=opportunity2, + ) + cfda_delete2 = setup_cfda(create_existing=True, is_delete=True, opportunity=opportunity2) + + opportunity3 = OpportunityFactory.create(opportunity_assistance_listings=[]) + cfda_update2 = setup_cfda(create_existing=True, opportunity=opportunity3) + cfda_delete_but_current_missing = setup_cfda( + create_existing=False, is_delete=True, opportunity=opportunity3 + ) + + cfda_insert_without_opportunity = setup_cfda( + create_existing=False, source_values={"opportunity_id": 12345678}, opportunity=None + ) + cfda_delete_without_opportunity = setup_cfda( + create_existing=False, source_values={"opportunity_id": 34567890}, opportunity=None + ) + + transform_oracle_data_task.process_assistance_listings() + + validate_assistance_listing(db_session, cfda_insert1) + validate_assistance_listing(db_session, cfda_insert2) + validate_assistance_listing(db_session, cfda_insert3) + validate_assistance_listing(db_session, cfda_update1) + validate_assistance_listing(db_session, cfda_update2) + validate_assistance_listing(db_session, cfda_delete1, expect_in_db=False) + validate_assistance_listing(db_session, cfda_delete2, expect_in_db=False) + + # Records that won't have been fetched + validate_assistance_listing( + db_session, + cfda_update_already_processed1, + expect_in_db=True, + expect_values_to_match=False, + ) + validate_assistance_listing( + db_session, + cfda_update_already_processed2, + expect_in_db=True, + expect_values_to_match=False, + ) + validate_assistance_listing(db_session, cfda_delete_already_processed1, expect_in_db=False) + + validate_assistance_listing(db_session, cfda_delete_but_current_missing, expect_in_db=False) + + validate_assistance_listing(db_session, cfda_insert_without_opportunity, expect_in_db=False) + validate_assistance_listing(db_session, cfda_delete_without_opportunity, expect_in_db=False) + + metrics = transform_oracle_data_task.metrics + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED] == 10 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_DELETED] == 2 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_INSERTED] == 3 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_UPDATED] == 2 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_ORPHANED] == 2 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_ERROR_COUNT] == 1 + + # Rerunning just attempts to re-process the error record, nothing else gets picked up + transform_oracle_data_task.process_assistance_listings() + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED] == 11 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_DELETED] == 2 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_INSERTED] == 3 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_UPDATED] == 2 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_ORPHANED] == 2 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_ERROR_COUNT] == 2 + + def test_process_assistance_listing_orphaned_record( + self, db_session, transform_oracle_data_task + ): + cfda_insert_without_opportunity = setup_cfda( + create_existing=False, source_values={"opportunity_id": 987654321}, opportunity=None + ) + + # Verify it gets marked as transformed + assert cfda_insert_without_opportunity.transformed_at is None + transform_oracle_data_task.process_assistance_listing( + cfda_insert_without_opportunity, None, None + ) + assert cfda_insert_without_opportunity.transformed_at is not None + assert ( + transform_oracle_data_task.metrics[ + transform_oracle_data_task.Metrics.TOTAL_RECORDS_ORPHANED + ] + == 1 + ) + + # Verify nothing actually gets created + opportunity = ( + db_session.query(Opportunity) + .filter(Opportunity.opportunity_id == cfda_insert_without_opportunity.opportunity_id) + .one_or_none() + ) + assert opportunity is None + assistance_listing = ( + db_session.query(OpportunityAssistanceListing) + .filter( + OpportunityAssistanceListing.opportunity_assistance_listing_id + == cfda_insert_without_opportunity.opp_cfda_id + ) + .one_or_none() + ) + assert assistance_listing is None + + def test_process_assistance_listing_delete_but_current_missing( + self, db_session, transform_oracle_data_task + ): + opportunity = OpportunityFactory.create(opportunity_assistance_listings=[]) + delete_but_current_missing = setup_cfda( + create_existing=False, is_delete=True, opportunity=opportunity + ) + + with pytest.raises( + ValueError, match="Cannot delete assistance listing as it does not exist" + ): + transform_oracle_data_task.process_assistance_listing( + delete_but_current_missing, None, opportunity + ) + + validate_assistance_listing(db_session, delete_but_current_missing, expect_in_db=False) + + @pytest.mark.parametrize( "value,expected_value", [ diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index 46d1e742d..f7d6e9804 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -543,6 +543,8 @@ class OpportunityAssistanceListingFactory(BaseFactory): class Meta: model = opportunity_models.OpportunityAssistanceListing + opportunity_assistance_listing_id = factory.Sequence(lambda n: n) + opportunity = factory.SubFactory(OpportunityFactory) opportunity_id = factory.LazyAttribute(lambda a: a.opportunity.opportunity_id) @@ -551,6 +553,13 @@ class Meta: lambda: f"{fake.random_int(min=1, max=99):02}.{fake.random_int(min=1, max=999):03}" ) + class Params: + # Set the timestamps in the past rather than using the default of "now" + timestamps_in_past = factory.Trait( + created_at=factory.Faker("date_time_between", start_date="-5y", end_date="-3y"), + updated_at=factory.Faker("date_time_between", start_date="-3y", end_date="-1y"), + ) + class LinkOpportunitySummaryFundingInstrumentFactory(BaseFactory): class Meta: @@ -596,7 +605,6 @@ class Meta: #################################### - class StagingTopportunityFactory(BaseFactory): class Meta: model = staging.opportunity.Topportunity @@ -650,6 +658,7 @@ class Params: category_explanation=None, ) + class StagingTopportunityCfdaFactory(BaseFactory): class Meta: model = staging.opportunity.TopportunityCfda @@ -673,6 +682,17 @@ class Meta: is_deleted = False transformed_at = None + class Params: + already_transformed = factory.Trait( + transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") + ) + + # Trait to set all nullable fields to None + all_fields_null = factory.Trait( + programtitle=None, + cfdanumber=None, + ) + #################################### # Transfer Table Factories From 588ec110b397429ac234b7169fc980b10293163a Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Wed, 1 May 2024 13:33:24 -0400 Subject: [PATCH 15/39] [Issue #1747] Add transformations for the opportunity summary table --- api/docker-compose.yml | 2 +- .../transform_oracle_data_task.py | 188 +++++++++++++++++- api/src/db/migrations/env.py | 2 + ...2024_04_30_unique_constraint_on_summary.py | 38 ++++ api/src/db/models/opportunity_models.py | 10 +- api/src/db/models/staging/forecast.py | 8 +- api/src/db/models/staging/synopsis.py | 7 +- 7 files changed, 245 insertions(+), 10 deletions(-) create mode 100644 api/src/db/migrations/versions/2024_04_30_unique_constraint_on_summary.py diff --git a/api/docker-compose.yml b/api/docker-compose.yml index 44cb64454..a364c74c3 100644 --- a/api/docker-compose.yml +++ b/api/docker-compose.yml @@ -3,7 +3,7 @@ version: '3' services: grants-db: - image: postgres:14-alpine + image: postgres:15-alpine container_name: grants-db command: postgres -c "log_lock_waits=on" -N 1000 -c "fsync=off" env_file: ./local.env diff --git a/api/src/data_migration/transformation/transform_oracle_data_task.py b/api/src/data_migration/transformation/transform_oracle_data_task.py index 632d28180..97c8d66bb 100644 --- a/api/src/data_migration/transformation/transform_oracle_data_task.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -8,15 +8,18 @@ from src.adapters import db from src.constants.lookup_constants import OpportunityCategory from src.db.models.base import ApiSchemaTable, TimestampMixin -from src.db.models.opportunity_models import Opportunity, OpportunityAssistanceListing +from src.db.models.opportunity_models import Opportunity, OpportunityAssistanceListing, OpportunitySummary +from src.db.models.staging.forecast import Tforecast, TforecastHist from src.db.models.staging.opportunity import Topportunity, TopportunityCfda from src.db.models.staging.staging_base import StagingBase, StagingParamMixin +from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist from src.task.task import Task from src.util import datetime_util S = TypeVar("S", bound=StagingParamMixin) D = TypeVar("D", bound=ApiSchemaTable) +type SourceSummary = Tforecast | Tsynopsis | TforecastHist | TsynopsisHist logger = logging.getLogger(__name__) @@ -70,10 +73,12 @@ def fetch( ) def fetch_with_opportunity( - self, source_model: Type[S], destination_model: Type[D], join_clause: list + self, source_model: Type[S], destination_model: Type[D], join_clause: list, additional_where_clause: list | None = None ) -> list[Tuple[S, D | None, Opportunity | None]]: # Similar to the above fetch function, but also grabs an opportunity record # Note that this requires your source_model to have an opportunity_id field defined. + if additional_where_clause is None: + additional_where_clause = [] return cast( list[Tuple[S, D | None, Opportunity | None]], @@ -85,7 +90,7 @@ def fetch_with_opportunity( source_model.opportunity_id == Opportunity.opportunity_id, # type: ignore[attr-defined] isouter=True, ) - .where(source_model.transformed_at.is_(None)) + .where(source_model.transformed_at.is_(None), *additional_where_clause) .execution_options(yield_per=5000) ).all(), ) @@ -226,8 +231,67 @@ def process_assistance_listing( source_assistance_listing.transformed_at = self.transform_time def process_opportunity_summaries(self) -> None: - # TODO - https://github.com/HHS/simpler-grants-gov/issues/1747 - pass + synopsis_records = self.fetch_with_opportunity(Tsynopsis, OpportunitySummary, [Tsynopsis.opportunity_id == OpportunitySummary.opportunity_id], additional_where_clause=[OpportunitySummary.is_forecast.is_(False), OpportunitySummary.revision_number.is_(None)]) + self.process_opportunity_summary_group(synopsis_records) + + synopsis_hist_records = self.fetch_with_opportunity(TsynopsisHist, OpportunitySummary, [TsynopsisHist.opportunity_id == OpportunitySummary.opportunity_id, TsynopsisHist.revision_number == OpportunitySummary.revision_number], additional_where_clause=[OpportunitySummary.is_forecast.is_(False)]) + self.process_opportunity_summary_group(synopsis_hist_records) + + forecast_records = self.fetch_with_opportunity(Tforecast, OpportunitySummary, [Tforecast.opportunity_id == OpportunitySummary.opportunity_id], additional_where_clause=[OpportunitySummary.is_forecast.is_(True), OpportunitySummary.revision_number.is_(None)]) + self.process_opportunity_summary_group(forecast_records) + + forecast_hist_records = self.fetch_with_opportunity(TforecastHist, OpportunitySummary, [TforecastHist.opportunity_id == OpportunitySummary.opportunity_id, TforecastHist.revision_number == OpportunitySummary.revision_number], additional_where_clause=[OpportunitySummary.is_forecast.is_(True)]) + self.process_opportunity_summary_group(forecast_hist_records) + + + def process_opportunity_summary_group(self, records: list[Tuple[SourceSummary, OpportunitySummary | None, Opportunity | None]]) -> None: + for source_summary, target_summary, opportunity in records: + try: + self.process_opportunity_summary( + source_summary, target_summary, opportunity + ) + except ValueError: + self.increment(self.Metrics.TOTAL_ERROR_COUNT) + logger.exception( + "Failed to process opportunity summary", + extra=get_log_extra_summary(source_summary), + ) + + def process_opportunity_summary(self, source_summary: SourceSummary, target_summary: OpportunitySummary | None, opportunity: Opportunity | None) -> None: + self.increment(self.Metrics.TOTAL_RECORDS_PROCESSED) + extra = get_log_extra_summary(source_summary) + logger.info("Processing opportunity summary", extra=extra) + + if opportunity is None: + # This shouldn't be possible as the incoming data has foreign keys, but as a safety net + # we'll make sure the opportunity actually exists + raise ValueError("Opportunity summary cannot be processed as the opportunity for it does not exist") + + if source_summary.is_deleted: + logger.info("Deleting opportunity summary", extra=extra) + + if target_summary is None: + raise ValueError("Cannot delete opportunity summary as it does not exist") + + self.increment(self.Metrics.TOTAL_RECORDS_DELETED) + self.db_session.delete(target_summary) + + else: + # To avoid incrementing metrics for records we fail to transform, record + # here whether it's an insert/update and we'll increment after transforming + is_insert = target_summary is None + + logger.info("Transforming and upserting opportunity summary", extra=extra) + transformed_opportunity_summary = transform_opportunity_summary(source_summary, target_summary) + self.db_session.add(transformed_opportunity_summary) + + if is_insert: + self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + else: + self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) + + logger.info("Processed opportunity summary", extra=extra) + source_summary.transformed_at = self.transform_time def process_one_to_many_lookup_tables(self) -> None: # TODO - https://github.com/HHS/simpler-grants-gov/issues/1749 @@ -310,6 +374,72 @@ def transform_assistance_listing( return target_assistance_listing +def transform_opportunity_summary(source_summary: SourceSummary, target_summary: OpportunitySummary | None) -> OpportunitySummary: + log_extra = get_log_extra_summary(source_summary) + + if target_summary is None: + logger.info("Creating new opportunity summary record", extra=log_extra) + target_summary = OpportunitySummary(opportunity_id=source_summary.opportunity_id, is_forecast=source_summary.is_forecast, revision_number=None) + + # Revision number is only found in the historical table + if isinstance(source_summary, (TsynopsisHist, TforecastHist)): + target_summary.revision_number = source_summary.revision_number + + # Fields in both + target_summary.version_number = source_summary.version_nbr + target_summary.is_cost_sharing = convert_yn_bool(source_summary.cost_sharing) + target_summary.post_date = source_summary.posting_date + target_summary.archive_date = source_summary.archive_date + target_summary.unarchive_date = source_summary.unarchive_date + target_summary.expected_number_of_awards = convert_numeric_str_to_int(source_summary.number_of_awards) + target_summary.estimated_total_program_funding = convert_numeric_str_to_int(source_summary.est_funding) + target_summary.award_floor = convert_numeric_str_to_int(source_summary.award_floor) + target_summary.award_ceiling = convert_numeric_str_to_int(source_summary.award_ceiling) + target_summary.additional_info_url = source_summary.fd_link_url + target_summary.additional_info_url_description = source_summary.fd_link_desc + target_summary.modification_comments = source_summary.modification_comments + target_summary.funding_category_description = source_summary.oth_cat_fa_desc + target_summary.agency_name = source_summary.ac_name + target_summary.agency_phone_number = source_summary.ac_phone_number + target_summary.agency_contact_description = source_summary.agency_contact_desc + target_summary.agency_email_address = source_summary.ac_email_addr + target_summary.agency_email_address_description = source_summary.ac_email_desc + target_summary.can_send_mail = convert_yn_bool(source_summary.sendmail) + target_summary.publisher_profile_id = source_summary.publisher_profile_id + target_summary.publisher_user_id = source_summary.publisheruid + target_summary.updated_by = source_summary.last_upd_id + target_summary.created_by = source_summary.creator_id + + # Some fields either are named different in synopsis/forecast + # or only come from one of those tables, so handle those here + if isinstance(source_summary, (Tsynopsis, TsynopsisHist)): + target_summary.summary_description = source_summary.syn_desc + target_summary.agency_code = source_summary.a_sa_code + + target_summary.close_date = source_summary.response_date + target_summary.close_date_description = source_summary.response_date_desc + + else: + target_summary.summary_description = source_summary.forecast_desc + target_summary.agency_code = source_summary.agency_code + + target_summary.forecasted_post_date = source_summary.est_synopsis_posting_date + target_summary.forecasted_close_date = source_summary.est_appl_response_date + target_summary.forecasted_close_date_description = source_summary.est_appl_response_date_desc + target_summary.forecasted_award_date = source_summary.est_award_date + target_summary.forecasted_project_start_date = source_summary.est_project_start_date + target_summary.fiscal_year = source_summary.fiscal_year + + # Historical only + if isinstance(source_summary, (TsynopsisHist, TforecastHist)): + target_summary.is_deleted = convert_action_type_to_is_deleted(source_summary.action_type) + + transform_update_create_timestamp( + source_summary, target_summary, log_extra=log_extra + ) + + return target_summary + def convert_est_timestamp_to_utc(timestamp: datetime | None) -> datetime | None: if timestamp is None: return None @@ -355,3 +485,51 @@ def transform_update_create_timestamp( # until it receives an update. We always set the value, and on initial insert # want it to be the same as the created_at. target.updated_at = target.created_at + + +def convert_yn_bool(value: str | None) -> bool | None: + # Booleans in the Oracle database are stored as varchar/char + # columns with the values as Y/N + if value is None or value == "": + return None + + if value == "Y": + return True + + if value == "N": + return False + + # Just in case the column isn't actually a boolean + raise ValueError("Unexpected Y/N bool value: %s" % value) + +def convert_action_type_to_is_deleted(value: str | None) -> bool | None: + if value is None or value == "": + return None + + if value == "D": # D = Delete + return True + + if value == "U": # U = Update + return False + + raise ValueError("Unexpected action type value: %s" % value) + +def convert_numeric_str_to_int(value: str | None) -> int | None: + if value is None or value == "": + return None + + if value.isnumeric(): + return int(value) + + # From what we've found in the legacy data, some of these numeric strings + # are written out as "none", "not available", "n/a" or similar. All of these + # we're fine with collectively treating as null-equivalent + return None + + +def get_log_extra_summary(source_summary: SourceSummary) -> dict: + return { + "opportunity_id": source_summary.opportunity_id, + "is_forecast": source_summary.is_forecast, + "revision_number": getattr(source_summary, "revision_number", None) + } \ No newline at end of file diff --git a/api/src/db/migrations/env.py b/api/src/db/migrations/env.py index 4abeee6ca..2612bfeb7 100644 --- a/api/src/db/migrations/env.py +++ b/api/src/db/migrations/env.py @@ -44,7 +44,9 @@ def include_object( if type_ == "schema" and getattr(object, "schema", None) is not None: return False + if type_ == "table" and name is not None and name.startswith("foreign_"): + # We create foreign tables to an Oracle database, if we see those locally # just ignore them as they aren't something we want included in Alembic return False diff --git a/api/src/db/migrations/versions/2024_04_30_unique_constraint_on_summary.py b/api/src/db/migrations/versions/2024_04_30_unique_constraint_on_summary.py new file mode 100644 index 000000000..b19c0a6d9 --- /dev/null +++ b/api/src/db/migrations/versions/2024_04_30_unique_constraint_on_summary.py @@ -0,0 +1,38 @@ +"""unique constraint on summary + +Revision ID: 103db9c2afe6 +Revises: e3a1be603d26 +Create Date: 2024-04-30 14:47:35.336878 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "103db9c2afe6" +down_revision = "e3a1be603d26" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_unique_constraint( + op.f("opportunity_summary_is_forecast_uniq"), + "opportunity_summary", + ["is_forecast", "revision_number", "opportunity_id"], + schema="api", + postgresql_nulls_not_distinct=True + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint( + op.f("opportunity_summary_is_forecast_uniq"), + "opportunity_summary", + schema="api", + type_="unique", + ) + # ### end Alembic commands ### diff --git a/api/src/db/models/opportunity_models.py b/api/src/db/models/opportunity_models.py index 9f19dcd4f..b3e4e9091 100644 --- a/api/src/db/models/opportunity_models.py +++ b/api/src/db/models/opportunity_models.py @@ -1,6 +1,6 @@ from datetime import date -from sqlalchemy import BigInteger, ForeignKey +from sqlalchemy import BigInteger, ForeignKey, UniqueConstraint from sqlalchemy.ext.associationproxy import AssociationProxy, association_proxy from sqlalchemy.orm import Mapped, mapped_column, relationship @@ -86,6 +86,14 @@ def opportunity_status(self) -> OpportunityStatus | None: class OpportunitySummary(ApiSchemaTable, TimestampMixin): __tablename__ = "opportunity_summary" + __table_args__ = ( + # nulls not distinct makes it so nulls work in the unique constraint + UniqueConstraint("is_forecast", "revision_number", "opportunity_id", postgresql_nulls_not_distinct=True), + # Need to define the table args like this to inherit whatever we set on the super table + # otherwise we end up overwriting things and Alembic remakes the whole table + ApiSchemaTable.__table_args__ + ) + opportunity_summary_id: Mapped[int] = mapped_column(BigInteger, primary_key=True) opportunity_id: Mapped[int] = mapped_column( diff --git a/api/src/db/models/staging/forecast.py b/api/src/db/models/staging/forecast.py index 1fa99bdc4..5b87a1e38 100644 --- a/api/src/db/models/staging/forecast.py +++ b/api/src/db/models/staging/forecast.py @@ -5,11 +5,15 @@ class Tforecast(StagingBase, forecast_mixin.TforecastMixin, StagingParamMixin): __tablename__ = "tforecast" - + @property + def is_forecast(self) -> bool: + return True class TforecastHist(StagingBase, forecast_mixin.TforecastHistMixin, StagingParamMixin): __tablename__ = "tforecast_hist" - + @property + def is_forecast(self) -> bool: + return True class TapplicanttypesForecast( StagingBase, forecast_mixin.TapplicanttypesForecastMixin, StagingParamMixin ): diff --git a/api/src/db/models/staging/synopsis.py b/api/src/db/models/staging/synopsis.py index 574c1b1c1..c478bc65e 100644 --- a/api/src/db/models/staging/synopsis.py +++ b/api/src/db/models/staging/synopsis.py @@ -5,10 +5,15 @@ class Tsynopsis(StagingBase, synopsis_mixin.TsynopsisMixin, StagingParamMixin): __tablename__ = "tsynopsis" - + @property + def is_forecast(self) -> bool: + return False class TsynopsisHist(StagingBase, synopsis_mixin.TsynopsisHistMixin, StagingParamMixin): __tablename__ = "tsynopsis_hist" + @property + def is_forecast(self) -> bool: + return False class TapplicanttypesSynopsis( StagingBase, synopsis_mixin.TapplicanttypesSynopsisMixin, StagingParamMixin From fcf16ca1e859e2160bcfc4a2e4f2a3e2b325c13f Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Thu, 2 May 2024 10:09:13 -0400 Subject: [PATCH 16/39] Adding a lot of implementation --- .../transform_oracle_data_task.py | 148 +++++++++++++----- api/src/db/foreign/dialect.py | 4 +- api/src/db/migrations/env.py | 1 - ...2024_04_30_unique_constraint_on_summary.py | 3 +- api/src/db/models/base.py | 2 +- api/src/db/models/opportunity_models.py | 6 +- api/src/db/models/staging/forecast.py | 4 + api/src/db/models/staging/opportunity.py | 2 +- api/src/db/models/staging/synopsis.py | 12 +- api/src/util/datetime_util.py | 2 + api/tests/src/db/models/factories.py | 63 +++++++- 11 files changed, 195 insertions(+), 52 deletions(-) diff --git a/api/src/data_migration/transformation/transform_oracle_data_task.py b/api/src/data_migration/transformation/transform_oracle_data_task.py index 97c8d66bb..85d3648c9 100644 --- a/api/src/data_migration/transformation/transform_oracle_data_task.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -1,14 +1,18 @@ import logging from datetime import datetime from enum import StrEnum -from typing import Tuple, Type, TypeVar, cast +from typing import Sequence, Tuple, Type, TypeAlias, TypeVar, cast -from sqlalchemy import select +from sqlalchemy import select, and_ from src.adapters import db from src.constants.lookup_constants import OpportunityCategory from src.db.models.base import ApiSchemaTable, TimestampMixin -from src.db.models.opportunity_models import Opportunity, OpportunityAssistanceListing, OpportunitySummary +from src.db.models.opportunity_models import ( + Opportunity, + OpportunityAssistanceListing, + OpportunitySummary, +) from src.db.models.staging.forecast import Tforecast, TforecastHist from src.db.models.staging.opportunity import Topportunity, TopportunityCfda from src.db.models.staging.staging_base import StagingBase, StagingParamMixin @@ -19,7 +23,7 @@ S = TypeVar("S", bound=StagingParamMixin) D = TypeVar("D", bound=ApiSchemaTable) -type SourceSummary = Tforecast | Tsynopsis | TforecastHist | TsynopsisHist +SourceSummary: TypeAlias = Tforecast | Tsynopsis | TforecastHist | TsynopsisHist logger = logging.getLogger(__name__) @@ -73,24 +77,24 @@ def fetch( ) def fetch_with_opportunity( - self, source_model: Type[S], destination_model: Type[D], join_clause: list, additional_where_clause: list | None = None + self, + source_model: Type[S], + destination_model: Type[D], + join_clause: list, ) -> list[Tuple[S, D | None, Opportunity | None]]: # Similar to the above fetch function, but also grabs an opportunity record # Note that this requires your source_model to have an opportunity_id field defined. - if additional_where_clause is None: - additional_where_clause = [] - return cast( list[Tuple[S, D | None, Opportunity | None]], self.db_session.execute( select(source_model, destination_model, Opportunity) - .join(destination_model, *join_clause, isouter=True) + .join(destination_model, and_(*join_clause), isouter=True) .join( Opportunity, source_model.opportunity_id == Opportunity.opportunity_id, # type: ignore[attr-defined] isouter=True, ) - .where(source_model.transformed_at.is_(None), *additional_where_clause) + .where(source_model.transformed_at.is_(None)) .execution_options(yield_per=5000) ).all(), ) @@ -231,25 +235,46 @@ def process_assistance_listing( source_assistance_listing.transformed_at = self.transform_time def process_opportunity_summaries(self) -> None: - synopsis_records = self.fetch_with_opportunity(Tsynopsis, OpportunitySummary, [Tsynopsis.opportunity_id == OpportunitySummary.opportunity_id], additional_where_clause=[OpportunitySummary.is_forecast.is_(False), OpportunitySummary.revision_number.is_(None)]) + logger.info("Starting processing of opportunity summaries") + synopsis_records = self.fetch_with_opportunity( + Tsynopsis, + OpportunitySummary, + [Tsynopsis.opportunity_id == OpportunitySummary.opportunity_id, OpportunitySummary.is_forecast.is_(False), OpportunitySummary.revision_number.is_(None)], + ) self.process_opportunity_summary_group(synopsis_records) - synopsis_hist_records = self.fetch_with_opportunity(TsynopsisHist, OpportunitySummary, [TsynopsisHist.opportunity_id == OpportunitySummary.opportunity_id, TsynopsisHist.revision_number == OpportunitySummary.revision_number], additional_where_clause=[OpportunitySummary.is_forecast.is_(False)]) + synopsis_hist_records = self.fetch_with_opportunity( + TsynopsisHist, + OpportunitySummary, + [ + TsynopsisHist.opportunity_id == OpportunitySummary.opportunity_id, TsynopsisHist.revision_number == OpportunitySummary.revision_number, OpportunitySummary.is_forecast.is_(False) + ], + ) self.process_opportunity_summary_group(synopsis_hist_records) - forecast_records = self.fetch_with_opportunity(Tforecast, OpportunitySummary, [Tforecast.opportunity_id == OpportunitySummary.opportunity_id], additional_where_clause=[OpportunitySummary.is_forecast.is_(True), OpportunitySummary.revision_number.is_(None)]) + forecast_records = self.fetch_with_opportunity( + Tforecast, + OpportunitySummary, + [Tforecast.opportunity_id == OpportunitySummary.opportunity_id, OpportunitySummary.is_forecast.is_(True), OpportunitySummary.revision_number.is_(None)], + ) self.process_opportunity_summary_group(forecast_records) - forecast_hist_records = self.fetch_with_opportunity(TforecastHist, OpportunitySummary, [TforecastHist.opportunity_id == OpportunitySummary.opportunity_id, TforecastHist.revision_number == OpportunitySummary.revision_number], additional_where_clause=[OpportunitySummary.is_forecast.is_(True)]) + forecast_hist_records = self.fetch_with_opportunity( + TforecastHist, + OpportunitySummary, + [ + TforecastHist.opportunity_id == OpportunitySummary.opportunity_id, + TforecastHist.revision_number == OpportunitySummary.revision_number, OpportunitySummary.is_forecast.is_(True) + ], + ) self.process_opportunity_summary_group(forecast_hist_records) - - def process_opportunity_summary_group(self, records: list[Tuple[SourceSummary, OpportunitySummary | None, Opportunity | None]]) -> None: + def process_opportunity_summary_group( + self, records: Sequence[Tuple[SourceSummary, OpportunitySummary | None, Opportunity | None]] + ) -> None: for source_summary, target_summary, opportunity in records: try: - self.process_opportunity_summary( - source_summary, target_summary, opportunity - ) + self.process_opportunity_summary(source_summary, target_summary, opportunity) except ValueError: self.increment(self.Metrics.TOTAL_ERROR_COUNT) logger.exception( @@ -257,7 +282,12 @@ def process_opportunity_summary_group(self, records: list[Tuple[SourceSummary, O extra=get_log_extra_summary(source_summary), ) - def process_opportunity_summary(self, source_summary: SourceSummary, target_summary: OpportunitySummary | None, opportunity: Opportunity | None) -> None: + def process_opportunity_summary( + self, + source_summary: SourceSummary, + target_summary: OpportunitySummary | None, + opportunity: Opportunity | None, + ) -> None: self.increment(self.Metrics.TOTAL_RECORDS_PROCESSED) extra = get_log_extra_summary(source_summary) logger.info("Processing opportunity summary", extra=extra) @@ -265,7 +295,9 @@ def process_opportunity_summary(self, source_summary: SourceSummary, target_summ if opportunity is None: # This shouldn't be possible as the incoming data has foreign keys, but as a safety net # we'll make sure the opportunity actually exists - raise ValueError("Opportunity summary cannot be processed as the opportunity for it does not exist") + raise ValueError( + "Opportunity summary cannot be processed as the opportunity for it does not exist" + ) if source_summary.is_deleted: logger.info("Deleting opportunity summary", extra=extra) @@ -282,7 +314,9 @@ def process_opportunity_summary(self, source_summary: SourceSummary, target_summ is_insert = target_summary is None logger.info("Transforming and upserting opportunity summary", extra=extra) - transformed_opportunity_summary = transform_opportunity_summary(source_summary, target_summary) + transformed_opportunity_summary = transform_opportunity_summary( + source_summary, target_summary + ) self.db_session.add(transformed_opportunity_summary) if is_insert: @@ -374,34 +408,42 @@ def transform_assistance_listing( return target_assistance_listing -def transform_opportunity_summary(source_summary: SourceSummary, target_summary: OpportunitySummary | None) -> OpportunitySummary: +def transform_opportunity_summary( + source_summary: SourceSummary, target_summary: OpportunitySummary | None +) -> OpportunitySummary: log_extra = get_log_extra_summary(source_summary) if target_summary is None: logger.info("Creating new opportunity summary record", extra=log_extra) - target_summary = OpportunitySummary(opportunity_id=source_summary.opportunity_id, is_forecast=source_summary.is_forecast, revision_number=None) + target_summary = OpportunitySummary( + opportunity_id=source_summary.opportunity_id, + is_forecast=source_summary.is_forecast, + revision_number=None, + ) # Revision number is only found in the historical table if isinstance(source_summary, (TsynopsisHist, TforecastHist)): target_summary.revision_number = source_summary.revision_number - # Fields in both + # Fields in all 4 source tables target_summary.version_number = source_summary.version_nbr target_summary.is_cost_sharing = convert_yn_bool(source_summary.cost_sharing) target_summary.post_date = source_summary.posting_date target_summary.archive_date = source_summary.archive_date - target_summary.unarchive_date = source_summary.unarchive_date - target_summary.expected_number_of_awards = convert_numeric_str_to_int(source_summary.number_of_awards) - target_summary.estimated_total_program_funding = convert_numeric_str_to_int(source_summary.est_funding) + target_summary.expected_number_of_awards = convert_numeric_str_to_int( + source_summary.number_of_awards + ) + target_summary.estimated_total_program_funding = convert_numeric_str_to_int( + source_summary.est_funding + ) target_summary.award_floor = convert_numeric_str_to_int(source_summary.award_floor) target_summary.award_ceiling = convert_numeric_str_to_int(source_summary.award_ceiling) target_summary.additional_info_url = source_summary.fd_link_url target_summary.additional_info_url_description = source_summary.fd_link_desc target_summary.modification_comments = source_summary.modification_comments target_summary.funding_category_description = source_summary.oth_cat_fa_desc + target_summary.applicant_eligibility_description = source_summary.applicant_elig_desc target_summary.agency_name = source_summary.ac_name - target_summary.agency_phone_number = source_summary.ac_phone_number - target_summary.agency_contact_description = source_summary.agency_contact_desc target_summary.agency_email_address = source_summary.ac_email_addr target_summary.agency_email_address_description = source_summary.ac_email_desc target_summary.can_send_mail = convert_yn_bool(source_summary.sendmail) @@ -415,17 +457,25 @@ def transform_opportunity_summary(source_summary: SourceSummary, target_summary: if isinstance(source_summary, (Tsynopsis, TsynopsisHist)): target_summary.summary_description = source_summary.syn_desc target_summary.agency_code = source_summary.a_sa_code + target_summary.agency_phone_number = source_summary.ac_phone_number + # Synopsis only fields + target_summary.agency_contact_description = source_summary.agency_contact_desc target_summary.close_date = source_summary.response_date target_summary.close_date_description = source_summary.response_date_desc + target_summary.unarchive_date = source_summary.unarchive_date - else: + else: # TForecast & TForecastHist target_summary.summary_description = source_summary.forecast_desc target_summary.agency_code = source_summary.agency_code + target_summary.agency_phone_number = source_summary.ac_phone + # Forecast only fields target_summary.forecasted_post_date = source_summary.est_synopsis_posting_date target_summary.forecasted_close_date = source_summary.est_appl_response_date - target_summary.forecasted_close_date_description = source_summary.est_appl_response_date_desc + target_summary.forecasted_close_date_description = ( + source_summary.est_appl_response_date_desc + ) target_summary.forecasted_award_date = source_summary.est_award_date target_summary.forecasted_project_start_date = source_summary.est_project_start_date target_summary.fiscal_year = source_summary.fiscal_year @@ -433,13 +483,14 @@ def transform_opportunity_summary(source_summary: SourceSummary, target_summary: # Historical only if isinstance(source_summary, (TsynopsisHist, TforecastHist)): target_summary.is_deleted = convert_action_type_to_is_deleted(source_summary.action_type) + else: + target_summary.is_deleted = False - transform_update_create_timestamp( - source_summary, target_summary, log_extra=log_extra - ) + transform_update_create_timestamp(source_summary, target_summary, log_extra=log_extra) return target_summary + def convert_est_timestamp_to_utc(timestamp: datetime | None) -> datetime | None: if timestamp is None: return None @@ -502,18 +553,20 @@ def convert_yn_bool(value: str | None) -> bool | None: # Just in case the column isn't actually a boolean raise ValueError("Unexpected Y/N bool value: %s" % value) + def convert_action_type_to_is_deleted(value: str | None) -> bool | None: if value is None or value == "": return None - if value == "D": # D = Delete + if value == "D": # D = Delete return True - if value == "U": # U = Update + if value == "U": # U = Update return False raise ValueError("Unexpected action type value: %s" % value) + def convert_numeric_str_to_int(value: str | None) -> int | None: if value is None or value == "": return None @@ -531,5 +584,22 @@ def get_log_extra_summary(source_summary: SourceSummary) -> dict: return { "opportunity_id": source_summary.opportunity_id, "is_forecast": source_summary.is_forecast, - "revision_number": getattr(source_summary, "revision_number", None) - } \ No newline at end of file + "revision_number": getattr(source_summary, "revision_number", None), + } + + + +def main(): + import src.logging + + with src.logging.init("TMP_THING"): + logger.info("starting") + + db_client = db.PostgresDBClient() + + with db_client.get_session() as db_session: + TransformOracleDataTask(db_session).run() + + +main() + diff --git a/api/src/db/foreign/dialect.py b/api/src/db/foreign/dialect.py index 8603a04c3..149239335 100644 --- a/api/src/db/foreign/dialect.py +++ b/api/src/db/foreign/dialect.py @@ -8,6 +8,8 @@ import sqlalchemy + + class ForeignTableDDLCompiler(sqlalchemy.sql.compiler.DDLCompiler): """SQLAlchemy compiler for creating foreign tables.""" @@ -34,7 +36,7 @@ def visit_create_column(self, create, first_pk=False, **kw): return sql -class ForeignTableDialect(sqlalchemy.dialects.postgresql.dialect): +class ForeignTableDialect(): """SQLAlchemy dialect for creating foreign tables. See https://docs.sqlalchemy.org/en/20/dialects/ diff --git a/api/src/db/migrations/env.py b/api/src/db/migrations/env.py index 2612bfeb7..a6e3b6867 100644 --- a/api/src/db/migrations/env.py +++ b/api/src/db/migrations/env.py @@ -46,7 +46,6 @@ def include_object( return False if type_ == "table" and name is not None and name.startswith("foreign_"): - # We create foreign tables to an Oracle database, if we see those locally # just ignore them as they aren't something we want included in Alembic return False diff --git a/api/src/db/migrations/versions/2024_04_30_unique_constraint_on_summary.py b/api/src/db/migrations/versions/2024_04_30_unique_constraint_on_summary.py index b19c0a6d9..78d47e848 100644 --- a/api/src/db/migrations/versions/2024_04_30_unique_constraint_on_summary.py +++ b/api/src/db/migrations/versions/2024_04_30_unique_constraint_on_summary.py @@ -5,7 +5,6 @@ Create Date: 2024-04-30 14:47:35.336878 """ -import sqlalchemy as sa from alembic import op # revision identifiers, used by Alembic. @@ -22,7 +21,7 @@ def upgrade(): "opportunity_summary", ["is_forecast", "revision_number", "opportunity_id"], schema="api", - postgresql_nulls_not_distinct=True + postgresql_nulls_not_distinct=True, ) # ### end Alembic commands ### diff --git a/api/src/db/models/base.py b/api/src/db/models/base.py index ee739fb91..3844a1ad6 100644 --- a/api/src/db/models/base.py +++ b/api/src/db/models/base.py @@ -100,7 +100,7 @@ def __rich_repr__(self) -> Iterable[tuple[str, Any]]: class ApiSchemaTable(Base): __abstract__ = True - __table_args__ = {"schema": Schemas.API} + __table_args__: Any = {"schema": Schemas.API} @declarative_mixin diff --git a/api/src/db/models/opportunity_models.py b/api/src/db/models/opportunity_models.py index b3e4e9091..2f8287298 100644 --- a/api/src/db/models/opportunity_models.py +++ b/api/src/db/models/opportunity_models.py @@ -88,10 +88,12 @@ class OpportunitySummary(ApiSchemaTable, TimestampMixin): __table_args__ = ( # nulls not distinct makes it so nulls work in the unique constraint - UniqueConstraint("is_forecast", "revision_number", "opportunity_id", postgresql_nulls_not_distinct=True), + UniqueConstraint( + "is_forecast", "revision_number", "opportunity_id", postgresql_nulls_not_distinct=True + ), # Need to define the table args like this to inherit whatever we set on the super table # otherwise we end up overwriting things and Alembic remakes the whole table - ApiSchemaTable.__table_args__ + ApiSchemaTable.__table_args__, ) opportunity_summary_id: Mapped[int] = mapped_column(BigInteger, primary_key=True) diff --git a/api/src/db/models/staging/forecast.py b/api/src/db/models/staging/forecast.py index 5b87a1e38..dccb35b3c 100644 --- a/api/src/db/models/staging/forecast.py +++ b/api/src/db/models/staging/forecast.py @@ -8,12 +8,16 @@ class Tforecast(StagingBase, forecast_mixin.TforecastMixin, StagingParamMixin): @property def is_forecast(self) -> bool: return True + + class TforecastHist(StagingBase, forecast_mixin.TforecastHistMixin, StagingParamMixin): __tablename__ = "tforecast_hist" @property def is_forecast(self) -> bool: return True + + class TapplicanttypesForecast( StagingBase, forecast_mixin.TapplicanttypesForecastMixin, StagingParamMixin ): diff --git a/api/src/db/models/staging/opportunity.py b/api/src/db/models/staging/opportunity.py index 7840a71f8..6ad12292a 100644 --- a/api/src/db/models/staging/opportunity.py +++ b/api/src/db/models/staging/opportunity.py @@ -1,5 +1,4 @@ from sqlalchemy.orm import Mapped, relationship - from src.db.legacy_mixin import opportunity_mixin from src.db.models.staging.staging_base import StagingBase, StagingParamMixin @@ -12,6 +11,7 @@ class Topportunity(StagingBase, opportunity_mixin.TopportunityMixin, StagingPara uselist=True, ) + #synopsis: Mapped[Tsynopsis] = relationship(primaryjoin="Topportunity.opportunity_id == foreign(Tsynopsis.opportunity_id)", uselist=False) class TopportunityCfda(StagingBase, opportunity_mixin.TopportunityCfdaMixin, StagingParamMixin): __tablename__ = "topportunity_cfda" diff --git a/api/src/db/models/staging/synopsis.py b/api/src/db/models/staging/synopsis.py index c478bc65e..0c271cbc0 100644 --- a/api/src/db/models/staging/synopsis.py +++ b/api/src/db/models/staging/synopsis.py @@ -1,13 +1,22 @@ +from .opportunity import Topportunity from src.db.legacy_mixin import synopsis_mixin from src.db.models.staging.staging_base import StagingBase, StagingParamMixin - +from sqlalchemy.orm import Mapped, relationship class Tsynopsis(StagingBase, synopsis_mixin.TsynopsisMixin, StagingParamMixin): __tablename__ = "tsynopsis" + opportunity: Mapped[Topportunity | None] = relationship( + Topportunity, + primaryjoin="Tsynopsis.opportunity_id == foreign(Topportunity.opportunity_id)", + uselist=False, + ) + @property def is_forecast(self) -> bool: return False + + class TsynopsisHist(StagingBase, synopsis_mixin.TsynopsisHistMixin, StagingParamMixin): __tablename__ = "tsynopsis_hist" @@ -15,6 +24,7 @@ class TsynopsisHist(StagingBase, synopsis_mixin.TsynopsisHistMixin, StagingParam def is_forecast(self) -> bool: return False + class TapplicanttypesSynopsis( StagingBase, synopsis_mixin.TapplicanttypesSynopsisMixin, StagingParamMixin ): diff --git a/api/src/util/datetime_util.py b/api/src/util/datetime_util.py index 264b19148..097df80be 100644 --- a/api/src/util/datetime_util.py +++ b/api/src/util/datetime_util.py @@ -32,6 +32,8 @@ def adjust_timezone(timestamp: datetime, timezone_str: str) -> datetime: def make_timezone_aware(timestamp: datetime, timezone_str: str) -> datetime: + # TODO - circle back to this and verify it doesn't break everything + timestamp = timestamp.replace(tzinfo=None) new_timezone = pytz.timezone(timezone_str) return new_timezone.localize(timestamp) diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index f7d6e9804..024ec365e 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -33,10 +33,11 @@ def sometimes_none(factory_value, none_chance: float = 0.5): - if random.random() > none_chance: - return factory_value - - return None + return factory.Maybe( + decider=factory.LazyAttribute(lambda s: random.random() > none_chance), + yes_declaration=factory_value, + no_declaration=None, + ) class CustomProvider(BaseProvider): @@ -135,6 +136,8 @@ class CustomProvider(BaseProvider): "{{word}}-###-##", ] + YN_BOOLEAN_VALUES = ["Y", "N"] + def agency(self) -> str: return self.random_element(self.AGENCIES) @@ -170,6 +173,9 @@ def summary_description(self) -> str: pattern = self.random_element(self.SUMMARY_DESCRIPTION_FORMATS) return self.generator.parse(pattern) + def yn_boolean(self) -> str: + return self.random_element(self.YN_BOOLEAN_VALUES) + fake = faker.Faker() fake.add_provider(CustomProvider) @@ -693,6 +699,55 @@ class Params: cfdanumber=None, ) +class StagingTsynopsisFactory(BaseFactory): + class Meta: + model = staging.synopsis.Tsynopsis + + opportunity = factory.SubFactory(StagingTopportunityFactory) + opportunity_id = factory.LazyAttribute(lambda s: s.opportunity.opportunity_id) + + posting_date = factory.Faker("date_between", start_date="-3w", end_date="now") + response_date = factory.Faker("date_between", start_date="+2w", end_date="+3w") + archive_date = factory.Faker("date_between", start_date="+3w", end_date="+4w") + unarchive_date = sometimes_none(factory.Faker("date_between", start_date="+6w", end_date="+7w"), none_chance=0.9) + syn_desc = factory.Faker("summary_description") + oth_cat_fa_desc = sometimes_none(factory.Faker("paragraph", nb_sentences=1)) + + cost_sharing = sometimes_none(factory.Faker("yn_boolean"), none_chance=0.1) + # These int values are stored as strings + # TODO - sometimes_none locks in for an entire run, we need some sort of wrapper approach? + number_of_awards = sometimes_none(factory.LazyFunction(lambda: str(fake.random_int(1, 25))), none_chance=0.1) + est_funding = sometimes_none(factory.LazyFunction(lambda: str(fake.random_int(25_000, 25_000_000, step=5_000))), none_chance=0.1) + award_ceiling = sometimes_none(factory.LazyFunction(lambda: str(fake.random_int(10_000, 25_000, step=5_000))), none_chance=0.1) + award_floor = sometimes_none(factory.LazyFunction(lambda: str(fake.random_int(0, 10_000, step=5_000))), none_chance=0.1) + + fd_link_url = factory.Faker("relevant_url") + fd_link_desc = factory.Faker("additional_info_desc") + agency_contact_desc = factory.Faker("agency_contact_description") + ac_email_addr = factory.Faker("email") + ac_email_desc = factory.LazyAttribute( + lambda s: f"Contact {s.ac_name} via email" + ) + a_sa_code = factory.Faker("agency") + ac_phone_number = Generators.PhoneNumber + ac_name = factory.Faker("agency_name") + + created_date = factory.Faker("date_time_between", start_date="-10y", end_date="-5y") + last_upd_date = sometimes_none( + factory.Faker("date_time_between", start_date="-5y", end_date="now") + ) + create_ts = factory.Faker("date_time_between", start_date="-10y", end_date="-5y") + sendmail = sometimes_none(factory.Faker("yn_boolean")) + response_date_desc = sometimes_none(factory.Faker("paragraph", nb_sentences=2)) + applicant_elig_desc = sometimes_none(factory.Faker("paragraph", nb_sentences=5)) + version_nbr = factory.Faker("random_int", min=0, max=10) + modification_comments = sometimes_none(factory.Faker("paragraph", nb_sentences=1)) + publisheruid = sometimes_none(factory.Faker("first_name")) + publisher_profile_id = sometimes_none(str(fake.random_int(1, 99_999))) + + # Default to being a new insert/update + is_deleted = False + transformed_at = None #################################### # Transfer Table Factories From f81f891e0d505ef3b3e259dc781026dfc2ca1d6b Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Thu, 2 May 2024 16:45:51 -0400 Subject: [PATCH 17/39] More tests and cleanup --- .../transform_oracle_data_task.py | 51 +-- api/src/db/foreign/dialect.py | 5 +- api/src/db/models/staging/forecast.py | 18 + api/src/db/models/staging/opportunity.py | 4 +- api/src/db/models/staging/synopsis.py | 15 +- api/src/util/datetime_util.py | 1 - .../test_transform_oracle_data_task.py | 375 +++++++++++++++++- api/tests/src/db/models/factories.py | 179 ++++++++- 8 files changed, 603 insertions(+), 45 deletions(-) diff --git a/api/src/data_migration/transformation/transform_oracle_data_task.py b/api/src/data_migration/transformation/transform_oracle_data_task.py index 85d3648c9..555651e90 100644 --- a/api/src/data_migration/transformation/transform_oracle_data_task.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -3,7 +3,7 @@ from enum import StrEnum from typing import Sequence, Tuple, Type, TypeAlias, TypeVar, cast -from sqlalchemy import select, and_ +from sqlalchemy import and_, select from src.adapters import db from src.constants.lookup_constants import OpportunityCategory @@ -239,7 +239,11 @@ def process_opportunity_summaries(self) -> None: synopsis_records = self.fetch_with_opportunity( Tsynopsis, OpportunitySummary, - [Tsynopsis.opportunity_id == OpportunitySummary.opportunity_id, OpportunitySummary.is_forecast.is_(False), OpportunitySummary.revision_number.is_(None)], + [ + Tsynopsis.opportunity_id == OpportunitySummary.opportunity_id, + OpportunitySummary.is_forecast.is_(False), + OpportunitySummary.revision_number.is_(None), + ], ) self.process_opportunity_summary_group(synopsis_records) @@ -247,7 +251,9 @@ def process_opportunity_summaries(self) -> None: TsynopsisHist, OpportunitySummary, [ - TsynopsisHist.opportunity_id == OpportunitySummary.opportunity_id, TsynopsisHist.revision_number == OpportunitySummary.revision_number, OpportunitySummary.is_forecast.is_(False) + TsynopsisHist.opportunity_id == OpportunitySummary.opportunity_id, + TsynopsisHist.revision_number == OpportunitySummary.revision_number, + OpportunitySummary.is_forecast.is_(False), ], ) self.process_opportunity_summary_group(synopsis_hist_records) @@ -255,7 +261,11 @@ def process_opportunity_summaries(self) -> None: forecast_records = self.fetch_with_opportunity( Tforecast, OpportunitySummary, - [Tforecast.opportunity_id == OpportunitySummary.opportunity_id, OpportunitySummary.is_forecast.is_(True), OpportunitySummary.revision_number.is_(None)], + [ + Tforecast.opportunity_id == OpportunitySummary.opportunity_id, + OpportunitySummary.is_forecast.is_(True), + OpportunitySummary.revision_number.is_(None), + ], ) self.process_opportunity_summary_group(forecast_records) @@ -264,7 +274,8 @@ def process_opportunity_summaries(self) -> None: OpportunitySummary, [ TforecastHist.opportunity_id == OpportunitySummary.opportunity_id, - TforecastHist.revision_number == OpportunitySummary.revision_number, OpportunitySummary.is_forecast.is_(True) + TforecastHist.revision_number == OpportunitySummary.revision_number, + OpportunitySummary.is_forecast.is_(True), ], ) self.process_opportunity_summary_group(forecast_hist_records) @@ -317,7 +328,7 @@ def process_opportunity_summary( transformed_opportunity_summary = transform_opportunity_summary( source_summary, target_summary ) - self.db_session.add(transformed_opportunity_summary) + self.db_session.merge(transformed_opportunity_summary) if is_insert: self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) @@ -409,11 +420,11 @@ def transform_assistance_listing( def transform_opportunity_summary( - source_summary: SourceSummary, target_summary: OpportunitySummary | None + source_summary: SourceSummary, incoming_summary: OpportunitySummary | None ) -> OpportunitySummary: log_extra = get_log_extra_summary(source_summary) - if target_summary is None: + if incoming_summary is None: logger.info("Creating new opportunity summary record", extra=log_extra) target_summary = OpportunitySummary( opportunity_id=source_summary.opportunity_id, @@ -424,6 +435,13 @@ def transform_opportunity_summary( # Revision number is only found in the historical table if isinstance(source_summary, (TsynopsisHist, TforecastHist)): target_summary.revision_number = source_summary.revision_number + else: + # We create a new summary object and merge it outside this function + # that way if any modifications occur on the object and then it errors + # they aren't actually applied + target_summary = OpportunitySummary( + opportunity_summary_id=incoming_summary.opportunity_summary_id + ) # Fields in all 4 source tables target_summary.version_number = source_summary.version_nbr @@ -586,20 +604,3 @@ def get_log_extra_summary(source_summary: SourceSummary) -> dict: "is_forecast": source_summary.is_forecast, "revision_number": getattr(source_summary, "revision_number", None), } - - - -def main(): - import src.logging - - with src.logging.init("TMP_THING"): - logger.info("starting") - - db_client = db.PostgresDBClient() - - with db_client.get_session() as db_session: - TransformOracleDataTask(db_session).run() - - -main() - diff --git a/api/src/db/foreign/dialect.py b/api/src/db/foreign/dialect.py index 149239335..4e96206f8 100644 --- a/api/src/db/foreign/dialect.py +++ b/api/src/db/foreign/dialect.py @@ -6,8 +6,7 @@ import re import sqlalchemy - - +import sqlalchemy.dialects.postgresql class ForeignTableDDLCompiler(sqlalchemy.sql.compiler.DDLCompiler): @@ -36,7 +35,7 @@ def visit_create_column(self, create, first_pk=False, **kw): return sql -class ForeignTableDialect(): +class ForeignTableDialect(sqlalchemy.dialects.postgresql.dialect): """SQLAlchemy dialect for creating foreign tables. See https://docs.sqlalchemy.org/en/20/dialects/ diff --git a/api/src/db/models/staging/forecast.py b/api/src/db/models/staging/forecast.py index dccb35b3c..f80163302 100644 --- a/api/src/db/models/staging/forecast.py +++ b/api/src/db/models/staging/forecast.py @@ -1,10 +1,21 @@ +from sqlalchemy.orm import Mapped, relationship + from src.db.legacy_mixin import forecast_mixin from src.db.models.staging.staging_base import StagingBase, StagingParamMixin +from .opportunity import Topportunity + class Tforecast(StagingBase, forecast_mixin.TforecastMixin, StagingParamMixin): __tablename__ = "tforecast" + opportunity: Mapped[Topportunity | None] = relationship( + Topportunity, + primaryjoin="Tforecast.opportunity_id == foreign(Topportunity.opportunity_id)", + uselist=False, + overlaps="opportunity", + ) + @property def is_forecast(self) -> bool: return True @@ -13,6 +24,13 @@ def is_forecast(self) -> bool: class TforecastHist(StagingBase, forecast_mixin.TforecastHistMixin, StagingParamMixin): __tablename__ = "tforecast_hist" + opportunity: Mapped[Topportunity | None] = relationship( + Topportunity, + primaryjoin="TforecastHist.opportunity_id == foreign(Topportunity.opportunity_id)", + uselist=False, + overlaps="opportunity", + ) + @property def is_forecast(self) -> bool: return True diff --git a/api/src/db/models/staging/opportunity.py b/api/src/db/models/staging/opportunity.py index 6ad12292a..5a04be46b 100644 --- a/api/src/db/models/staging/opportunity.py +++ b/api/src/db/models/staging/opportunity.py @@ -1,4 +1,5 @@ from sqlalchemy.orm import Mapped, relationship + from src.db.legacy_mixin import opportunity_mixin from src.db.models.staging.staging_base import StagingBase, StagingParamMixin @@ -11,7 +12,8 @@ class Topportunity(StagingBase, opportunity_mixin.TopportunityMixin, StagingPara uselist=True, ) - #synopsis: Mapped[Tsynopsis] = relationship(primaryjoin="Topportunity.opportunity_id == foreign(Tsynopsis.opportunity_id)", uselist=False) + # synopsis: Mapped[Tsynopsis] = relationship(primaryjoin="Topportunity.opportunity_id == foreign(Tsynopsis.opportunity_id)", uselist=False) + class TopportunityCfda(StagingBase, opportunity_mixin.TopportunityCfdaMixin, StagingParamMixin): __tablename__ = "topportunity_cfda" diff --git a/api/src/db/models/staging/synopsis.py b/api/src/db/models/staging/synopsis.py index 0c271cbc0..e1e828045 100644 --- a/api/src/db/models/staging/synopsis.py +++ b/api/src/db/models/staging/synopsis.py @@ -1,7 +1,10 @@ -from .opportunity import Topportunity +from sqlalchemy.orm import Mapped, relationship + from src.db.legacy_mixin import synopsis_mixin from src.db.models.staging.staging_base import StagingBase, StagingParamMixin -from sqlalchemy.orm import Mapped, relationship + +from .opportunity import Topportunity + class Tsynopsis(StagingBase, synopsis_mixin.TsynopsisMixin, StagingParamMixin): __tablename__ = "tsynopsis" @@ -10,6 +13,7 @@ class Tsynopsis(StagingBase, synopsis_mixin.TsynopsisMixin, StagingParamMixin): Topportunity, primaryjoin="Tsynopsis.opportunity_id == foreign(Topportunity.opportunity_id)", uselist=False, + overlaps="opportunity", ) @property @@ -20,6 +24,13 @@ def is_forecast(self) -> bool: class TsynopsisHist(StagingBase, synopsis_mixin.TsynopsisHistMixin, StagingParamMixin): __tablename__ = "tsynopsis_hist" + opportunity: Mapped[Topportunity | None] = relationship( + Topportunity, + primaryjoin="TsynopsisHist.opportunity_id == foreign(Topportunity.opportunity_id)", + uselist=False, + overlaps="opportunity", + ) + @property def is_forecast(self) -> bool: return False diff --git a/api/src/util/datetime_util.py b/api/src/util/datetime_util.py index 097df80be..37fe99276 100644 --- a/api/src/util/datetime_util.py +++ b/api/src/util/datetime_util.py @@ -32,7 +32,6 @@ def adjust_timezone(timestamp: datetime, timezone_str: str) -> datetime: def make_timezone_aware(timestamp: datetime, timezone_str: str) -> datetime: - # TODO - circle back to this and verify it doesn't break everything timestamp = timestamp.replace(tzinfo=None) new_timezone = pytz.timezone(timezone_str) return new_timezone.localize(timestamp) diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py index 8f5f56f5b..9de5b1eed 100644 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -10,14 +10,25 @@ transform_opportunity_category, transform_update_create_timestamp, ) -from src.db.models.opportunity_models import Opportunity, OpportunityAssistanceListing +from src.db.models.opportunity_models import ( + Opportunity, + OpportunityAssistanceListing, + OpportunitySummary, +) +from src.db.models.staging.forecast import TforecastHist from src.db.models.staging.opportunity import Topportunity, TopportunityCfda +from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist from tests.conftest import BaseTestClass from tests.src.db.models.factories import ( OpportunityAssistanceListingFactory, OpportunityFactory, + OpportunitySummaryFactory, + StagingTforecastFactory, + StagingTforecastHistFactory, StagingTopportunityCfdaFactory, StagingTopportunityFactory, + StagingTsynopsisFactory, + StagingTsynopsisHistFactory, ) @@ -85,6 +96,54 @@ def setup_cfda( return source_cfda +def setup_synopsis_forecast( + is_forecast: bool, + revision_number: int | None, + create_existing: bool, + is_delete: bool = False, + is_already_processed: bool = False, + source_values: dict | None = None, + all_fields_null: bool = False, + opportunity: Opportunity | None = None, +): + if source_values is None: + source_values = {} + + if is_forecast: + if revision_number is None: + factory_cls = StagingTforecastFactory + else: + factory_cls = StagingTforecastHistFactory + else: + if revision_number is None: + factory_cls = StagingTsynopsisFactory + else: + factory_cls = StagingTsynopsisHistFactory + + # TODO - don't think we need this added complexity - not a possible scenario here + # If you don't provide an opportunity, you need to provide an ID + if opportunity is not None: + source_values["opportunity_id"] = opportunity.opportunity_id + + if revision_number is not None: + source_values["revision_number"] = revision_number + + source_summary = factory_cls.create( + **source_values, + opportunity=None, # To override the factory trying to create something + is_deleted=is_delete, + already_transformed=is_already_processed, + all_fields_null=all_fields_null, + ) + + if create_existing: + OpportunitySummaryFactory.create( + opportunity=opportunity, is_forecast=is_forecast, revision_number=revision_number + ) + + return source_summary + + def validate_matching_fields( source, destination, fields: list[Tuple[str, str]], expect_all_to_match: bool ): @@ -186,6 +245,97 @@ def validate_assistance_listing( ) +def validate_opportunity_summary( + db_session, source_summary, expect_in_db: bool = True, expect_values_to_match: bool = True +): + revision_number = None + is_forecast = source_summary.is_forecast + if isinstance(source_summary, (TsynopsisHist, TforecastHist)): + revision_number = source_summary.revision_number + + opportunity_summary = ( + db_session.query(OpportunitySummary) + .filter( + OpportunitySummary.opportunity_id == source_summary.opportunity_id, + OpportunitySummary.revision_number == revision_number, + OpportunitySummary.is_forecast == is_forecast, + ) + .one_or_none() + ) + + if not expect_in_db: + assert opportunity_summary is None + return + + matching_fields = [ + ("version_nbr", "version_number"), + ("posting_date", "post_date"), + ("archive_date", "archive_date"), + ("fd_link_url", "additional_info_url"), + ("fd_link_desc", "additional_info_url_description"), + ("modification_comments", "modification_comments"), + ("oth_cat_fa_desc", "funding_category_description"), + ("applicant_elig_desc", "applicant_eligibility_description"), + ("ac_name", "agency_name"), + ("ac_email_addr", "agency_email_address"), + ("ac_email_desc", "agency_email_address_description"), + ("publisher_profile_id", "publisher_profile_id"), + ("publisheruid", "publisher_user_id"), + ("last_upd_id", "updated_by"), + ("creator_id", "created_by"), + ] + + if isinstance(source_summary, (Tsynopsis, TsynopsisHist)): + matching_fields.extend( + [ + ("syn_desc", "summary_description"), + ("a_sa_code", "agency_code"), + ("ac_phone_number", "agency_phone_number"), + ("agency_contact_desc", "agency_contact_description"), + ("response_date", "close_date"), + ("response_date_desc", "close_date_description"), + ("unarchive_date", "unarchive_date"), + ] + ) + else: # Forecast+ForecastHist + matching_fields.extend( + [ + ("forecast_desc", "summary_description"), + ("agency_code", "agency_code"), + ("ac_phone", "agency_phone_number"), + ("est_synopsis_posting_date", "forecasted_post_date"), + ("est_appl_response_date", "forecasted_close_date"), + ("est_appl_response_date_desc", "forecasted_close_date_description"), + ("est_award_date", "forecasted_award_date"), + ("est_project_start_date", "forecasted_project_start_date"), + ("fiscal_year", "fiscal_year"), + ] + ) + + # History only fields + is_deleted = False + if isinstance(source_summary, (TsynopsisHist, TforecastHist)): + matching_fields.extend([("revision_number", "revision_number")]) + + is_deleted = source_summary.action_type == "D" + + assert opportunity_summary is not None + validate_matching_fields( + source_summary, opportunity_summary, matching_fields, expect_values_to_match + ) + + """ + TODO - validate these fields + is_cost_sharing + expected_number_of_awards + estimated_total_program_funding + award_floor + award_ceiling + sendmail + """ + assert opportunity_summary.is_deleted == is_deleted + + class TestTransformOpportunity(BaseTestClass): @pytest.fixture() def transform_oracle_data_task( @@ -431,6 +581,229 @@ def test_process_assistance_listing_delete_but_current_missing( validate_assistance_listing(db_session, delete_but_current_missing, expect_in_db=False) +class TestTransformOpportunitySummary(BaseTestClass): + @pytest.fixture() + def transform_oracle_data_task( + self, db_session, enable_factory_create, truncate_opportunities + ) -> TransformOracleDataTask: + return TransformOracleDataTask(db_session) + + def test_process_opportunity_summaries(self, db_session, transform_oracle_data_task): + # Basic inserts + opportunity1 = OpportunityFactory.create( + no_current_summary=True, opportunity_assistance_listings=[] + ) + forecast_insert1 = setup_synopsis_forecast( + is_forecast=True, revision_number=None, create_existing=False, opportunity=opportunity1 + ) + synopsis_insert1 = setup_synopsis_forecast( + is_forecast=False, revision_number=None, create_existing=False, opportunity=opportunity1 + ) + forecast_hist_insert1 = setup_synopsis_forecast( + is_forecast=True, revision_number=1, create_existing=False, opportunity=opportunity1 + ) + synopsis_hist_insert1 = setup_synopsis_forecast( + is_forecast=False, revision_number=1, create_existing=False, opportunity=opportunity1 + ) + + # Mix of updates and inserts, somewhat resembling what happens when summary objects + # get moved to the historical table (we'd update the synopsis/forecast records, and create new historical) + opportunity2 = OpportunityFactory.create( + no_current_summary=True, opportunity_assistance_listings=[] + ) + forecast_update1 = setup_synopsis_forecast( + is_forecast=True, revision_number=None, create_existing=True, opportunity=opportunity2 + ) + synopsis_update1 = setup_synopsis_forecast( + is_forecast=False, revision_number=None, create_existing=True, opportunity=opportunity2 + ) + forecast_hist_update1 = setup_synopsis_forecast( + is_forecast=True, revision_number=1, create_existing=True, opportunity=opportunity2 + ) + synopsis_hist_update1 = setup_synopsis_forecast( + is_forecast=False, revision_number=1, create_existing=True, opportunity=opportunity2 + ) + forecast_hist_insert2 = setup_synopsis_forecast( + is_forecast=True, revision_number=2, create_existing=False, opportunity=opportunity2 + ) + synopsis_hist_insert2 = setup_synopsis_forecast( + is_forecast=False, revision_number=2, create_existing=False, opportunity=opportunity2 + ) + + # Mix of inserts, updates, and deletes + opportunity3 = OpportunityFactory.create( + no_current_summary=True, opportunity_assistance_listings=[] + ) + forecast_delete1 = setup_synopsis_forecast( + is_forecast=True, + revision_number=None, + create_existing=True, + is_delete=True, + opportunity=opportunity3, + ) + synopsis_delete1 = setup_synopsis_forecast( + is_forecast=False, + revision_number=None, + create_existing=True, + is_delete=True, + opportunity=opportunity3, + ) + forecast_hist_insert3 = setup_synopsis_forecast( + is_forecast=True, revision_number=2, create_existing=False, opportunity=opportunity3 + ) + synopsis_hist_update2 = setup_synopsis_forecast( + is_forecast=False, + revision_number=1, + create_existing=True, + source_values={"action_type": "D"}, + opportunity=opportunity3, + ) + + # A few error scenarios + opportunity4 = OpportunityFactory.create( + no_current_summary=True, opportunity_assistance_listings=[] + ) + forecast_delete_but_current_missing = setup_synopsis_forecast( + is_forecast=True, + revision_number=None, + create_existing=False, + is_delete=True, + opportunity=opportunity4, + ) + synopsis_update_invalid_yn_field = setup_synopsis_forecast( + is_forecast=False, + revision_number=None, + create_existing=True, + source_values={"sendmail": "E"}, + opportunity=opportunity4, + ) + synopsis_hist_insert_invalid_yn_field = setup_synopsis_forecast( + is_forecast=False, + revision_number=1, + create_existing=False, + source_values={"cost_sharing": "1"}, + opportunity=opportunity4, + ) + forecast_hist_update_invalid_action_type = setup_synopsis_forecast( + is_forecast=True, + revision_number=2, + create_existing=True, + source_values={"action_type": "X"}, + opportunity=opportunity4, + ) + + transform_oracle_data_task.process_opportunity_summaries() + + validate_opportunity_summary(db_session, forecast_insert1) + validate_opportunity_summary(db_session, synopsis_insert1) + validate_opportunity_summary(db_session, forecast_hist_insert1) + validate_opportunity_summary(db_session, synopsis_hist_insert1) + validate_opportunity_summary(db_session, forecast_hist_insert2) + validate_opportunity_summary(db_session, synopsis_hist_insert2) + validate_opportunity_summary(db_session, forecast_hist_insert3) + + validate_opportunity_summary(db_session, forecast_update1) + validate_opportunity_summary(db_session, synopsis_update1) + validate_opportunity_summary(db_session, forecast_hist_update1) + validate_opportunity_summary(db_session, synopsis_hist_update1) + validate_opportunity_summary(db_session, synopsis_hist_update2) + + validate_opportunity_summary(db_session, forecast_delete1, expect_in_db=False) + validate_opportunity_summary(db_session, synopsis_delete1, expect_in_db=False) + + validate_opportunity_summary( + db_session, forecast_delete_but_current_missing, expect_in_db=False + ) + validate_opportunity_summary( + db_session, + synopsis_update_invalid_yn_field, + expect_in_db=True, + expect_values_to_match=False, + ) + validate_opportunity_summary( + db_session, synopsis_hist_insert_invalid_yn_field, expect_in_db=False + ) + validate_opportunity_summary( + db_session, + forecast_hist_update_invalid_action_type, + expect_in_db=True, + expect_values_to_match=False, + ) + + metrics = transform_oracle_data_task.metrics + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED] == 18 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_DELETED] == 2 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_INSERTED] == 7 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_UPDATED] == 5 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_ERROR_COUNT] == 4 + + # Rerunning will only attempt to re-process the errors, so total+errors goes up by 4 + transform_oracle_data_task.process_opportunity_summaries() + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED] == 22 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_DELETED] == 2 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_INSERTED] == 7 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_UPDATED] == 5 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_ERROR_COUNT] == 8 + + @pytest.mark.parametrize( + "is_forecast,revision_number", [(True, None), (False, None), (True, 5), (False, 10)] + ) + def test_process_opportunity_summary_delete_but_current_missing( + self, db_session, transform_oracle_data_task, is_forecast, revision_number + ): + opportunity = OpportunityFactory.create( + no_current_summary=True, opportunity_assistance_listings=[] + ) + delete_but_current_missing = setup_synopsis_forecast( + is_forecast=is_forecast, + revision_number=revision_number, + create_existing=False, + is_delete=True, + opportunity=opportunity, + ) + + with pytest.raises( + ValueError, match="Cannot delete opportunity summary as it does not exist" + ): + transform_oracle_data_task.process_opportunity_summary( + delete_but_current_missing, None, opportunity + ) + + @pytest.mark.parametrize( + "is_forecast,revision_number,source_values,expected_error", + [ + (True, None, {"sendmail": "z"}, "Unexpected Y/N bool value: z"), + (False, None, {"cost_sharing": "v"}, "Unexpected Y/N bool value: v"), + (True, 5, {"action_type": "T"}, "Unexpected action type value: T"), + (False, 10, {"action_type": "5"}, "Unexpected action type value: 5"), + ], + ) + def test_process_opportunity_summary_invalid_value_errors( + self, + db_session, + transform_oracle_data_task, + is_forecast, + revision_number, + source_values, + expected_error, + ): + opportunity = OpportunityFactory.create( + no_current_summary=True, opportunity_assistance_listings=[] + ) + source_summary = setup_synopsis_forecast( + is_forecast=is_forecast, + revision_number=revision_number, + create_existing=False, + opportunity=opportunity, + source_values=source_values, + ) + + with pytest.raises(ValueError, match=expected_error): + transform_oracle_data_task.process_opportunity_summary( + source_summary, None, opportunity + ) + + @pytest.mark.parametrize( "value,expected_value", [ diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index 024ec365e..36662a5a3 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -15,6 +15,7 @@ import factory.fuzzy import faker from faker.providers import BaseProvider +from sqlalchemy import func from sqlalchemy.orm import scoped_session import src.adapters.db as db @@ -227,6 +228,20 @@ class OpportunityFactory(BaseFactory): class Meta: model = opportunity_models.Opportunity + @classmethod + def _setup_next_sequence(cls): + try: + value = ( + get_db_session() + .query(func.max(opportunity_models.Opportunity.opportunity_id)) + .scalar() + ) + if value is not None: + return value + 1 + return 1 + except Exception: + return 1 + opportunity_id = factory.Sequence(lambda n: n) opportunity_number = factory.Faker("opportunity_number") @@ -549,8 +564,6 @@ class OpportunityAssistanceListingFactory(BaseFactory): class Meta: model = opportunity_models.OpportunityAssistanceListing - opportunity_assistance_listing_id = factory.Sequence(lambda n: n) - opportunity = factory.SubFactory(OpportunityFactory) opportunity_id = factory.LazyAttribute(lambda a: a.opportunity.opportunity_id) @@ -699,6 +712,7 @@ class Params: cfdanumber=None, ) + class StagingTsynopsisFactory(BaseFactory): class Meta: model = staging.synopsis.Tsynopsis @@ -709,25 +723,34 @@ class Meta: posting_date = factory.Faker("date_between", start_date="-3w", end_date="now") response_date = factory.Faker("date_between", start_date="+2w", end_date="+3w") archive_date = factory.Faker("date_between", start_date="+3w", end_date="+4w") - unarchive_date = sometimes_none(factory.Faker("date_between", start_date="+6w", end_date="+7w"), none_chance=0.9) + unarchive_date = sometimes_none( + factory.Faker("date_between", start_date="+6w", end_date="+7w"), none_chance=0.9 + ) syn_desc = factory.Faker("summary_description") oth_cat_fa_desc = sometimes_none(factory.Faker("paragraph", nb_sentences=1)) cost_sharing = sometimes_none(factory.Faker("yn_boolean"), none_chance=0.1) # These int values are stored as strings - # TODO - sometimes_none locks in for an entire run, we need some sort of wrapper approach? - number_of_awards = sometimes_none(factory.LazyFunction(lambda: str(fake.random_int(1, 25))), none_chance=0.1) - est_funding = sometimes_none(factory.LazyFunction(lambda: str(fake.random_int(25_000, 25_000_000, step=5_000))), none_chance=0.1) - award_ceiling = sometimes_none(factory.LazyFunction(lambda: str(fake.random_int(10_000, 25_000, step=5_000))), none_chance=0.1) - award_floor = sometimes_none(factory.LazyFunction(lambda: str(fake.random_int(0, 10_000, step=5_000))), none_chance=0.1) + number_of_awards = sometimes_none( + factory.LazyFunction(lambda: str(fake.random_int(1, 25))), none_chance=0.1 + ) + est_funding = sometimes_none( + factory.LazyFunction(lambda: str(fake.random_int(25_000, 25_000_000, step=5_000))), + none_chance=0.1, + ) + award_ceiling = sometimes_none( + factory.LazyFunction(lambda: str(fake.random_int(10_000, 25_000, step=5_000))), + none_chance=0.1, + ) + award_floor = sometimes_none( + factory.LazyFunction(lambda: str(fake.random_int(0, 10_000, step=5_000))), none_chance=0.1 + ) fd_link_url = factory.Faker("relevant_url") fd_link_desc = factory.Faker("additional_info_desc") agency_contact_desc = factory.Faker("agency_contact_description") ac_email_addr = factory.Faker("email") - ac_email_desc = factory.LazyAttribute( - lambda s: f"Contact {s.ac_name} via email" - ) + ac_email_desc = factory.LazyAttribute(lambda s: f"Contact {s.ac_name} via email") a_sa_code = factory.Faker("agency") ac_phone_number = Generators.PhoneNumber ac_name = factory.Faker("agency_name") @@ -743,12 +766,144 @@ class Meta: version_nbr = factory.Faker("random_int", min=0, max=10) modification_comments = sometimes_none(factory.Faker("paragraph", nb_sentences=1)) publisheruid = sometimes_none(factory.Faker("first_name")) - publisher_profile_id = sometimes_none(str(fake.random_int(1, 99_999))) + publisher_profile_id = sometimes_none(factory.Faker("random_int", min=1, max=99_999)) # Default to being a new insert/update is_deleted = False transformed_at = None + class Params: + already_transformed = factory.Trait( + transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") + ) + + # Trait to set all nullable fields to None + all_fields_null = factory.Trait( + # TODO + posting_date=None + ) + + +class StagingTsynopsisHistFactory(StagingTsynopsisFactory): + class Meta: + model = staging.synopsis.TsynopsisHist + + revision_number = factory.Faker("random_int", min=1, max=25) + action_type = "U" # Update, put D for deleted + + class Params: + already_transformed = factory.Trait( + transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") + ) + + # Trait to set all nullable fields to None + all_fields_null = factory.Trait( + # TODO + posting_date=None + ) + + +class StagingTforecastFactory(BaseFactory): + class Meta: + model = staging.forecast.Tforecast + + opportunity = factory.SubFactory(StagingTopportunityFactory) + opportunity_id = factory.LazyAttribute(lambda s: s.opportunity.opportunity_id) + + posting_date = factory.Faker("date_between", start_date="-3w", end_date="now") + archive_date = factory.Faker("date_between", start_date="+3w", end_date="+4w") + forecast_desc = factory.Faker("summary_description") + oth_cat_fa_desc = sometimes_none(factory.Faker("paragraph", nb_sentences=1)) + + cost_sharing = sometimes_none(factory.Faker("yn_boolean"), none_chance=0.1) + # These int values are stored as strings + number_of_awards = sometimes_none( + factory.LazyFunction(lambda: str(fake.random_int(1, 25))), none_chance=0.1 + ) + est_funding = sometimes_none( + factory.LazyFunction(lambda: str(fake.random_int(25_000, 25_000_000, step=5_000))), + none_chance=0.1, + ) + award_ceiling = sometimes_none( + factory.LazyFunction(lambda: str(fake.random_int(10_000, 25_000, step=5_000))), + none_chance=0.1, + ) + award_floor = sometimes_none( + factory.LazyFunction(lambda: str(fake.random_int(0, 10_000, step=5_000))), none_chance=0.1 + ) + + fd_link_url = factory.Faker("relevant_url") + fd_link_desc = factory.Faker("additional_info_desc") + ac_email_addr = factory.Faker("email") + ac_email_desc = factory.LazyAttribute(lambda s: f"Contact {s.ac_name} via email") + agency_code = factory.Faker("agency") + ac_phone = Generators.PhoneNumber + ac_name = factory.Faker("agency_name") + + created_date = factory.Faker("date_time_between", start_date="-10y", end_date="-5y") + last_upd_date = sometimes_none( + factory.Faker("date_time_between", start_date="-5y", end_date="now") + ) + create_ts = factory.Faker("date_time_between", start_date="-10y", end_date="-5y") + sendmail = sometimes_none(factory.Faker("yn_boolean")) + applicant_elig_desc = sometimes_none(factory.Faker("paragraph", nb_sentences=5)) + version_nbr = factory.Faker("random_int", min=0, max=10) + modification_comments = sometimes_none(factory.Faker("paragraph", nb_sentences=1)) + publisheruid = sometimes_none(factory.Faker("first_name")) + publisher_profile_id = sometimes_none(factory.Faker("random_int", min=1, max=99_999)) + + est_synopsis_posting_date = sometimes_none( + factory.Faker("date_between", start_date="+2w", end_date="+3w") + ) + est_appl_response_date = sometimes_none( + factory.Faker("date_between", start_date="+4w", end_date="+6w") + ) + est_appl_response_date_desc = sometimes_none(factory.Faker("paragraph", nb_sentences=1)) + est_award_date = sometimes_none( + factory.Faker("date_between", start_date="+26w", end_date="+30w") + ) + est_project_start_date = sometimes_none( + factory.Faker("date_between", start_date="+30w", end_date="+52w") + ) + fiscal_year = factory.LazyAttribute( + lambda f: f.est_project_start_date.year if f.est_project_start_date else None + ) + + # Default to being a new insert/update + is_deleted = False + transformed_at = None + + class Params: + already_transformed = factory.Trait( + transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") + ) + + # Trait to set all nullable fields to None + all_fields_null = factory.Trait( + # TODO + posting_date=None + ) + + +class StagingTforecastHistFactory(StagingTforecastFactory): + class Meta: + model = staging.forecast.TforecastHist + + revision_number = factory.Faker("random_int", min=1, max=25) + action_type = "U" # Update, put D for deleted + + class Params: + already_transformed = factory.Trait( + transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") + ) + + # Trait to set all nullable fields to None + all_fields_null = factory.Trait( + # TODO + posting_date=None + ) + + #################################### # Transfer Table Factories #################################### From f73b97f0fd732d8617f33583e24b491563205f1e Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Thu, 2 May 2024 17:19:13 -0400 Subject: [PATCH 18/39] WIP --- .../transformation/transform_oracle_data_task.py | 10 ++++++++++ api/src/util/datetime_util.py | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/api/src/data_migration/transformation/transform_oracle_data_task.py b/api/src/data_migration/transformation/transform_oracle_data_task.py index 555651e90..5c5707574 100644 --- a/api/src/data_migration/transformation/transform_oracle_data_task.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -604,3 +604,13 @@ def get_log_extra_summary(source_summary: SourceSummary) -> dict: "is_forecast": source_summary.is_forecast, "revision_number": getattr(source_summary, "revision_number", None), } + + +def main(): + import src.logging + with src.logging.init("tmp"): + db_client = db.PostgresDBClient() + with db_client.get_session() as session: + TransformOracleDataTask(session).run() + +main() \ No newline at end of file diff --git a/api/src/util/datetime_util.py b/api/src/util/datetime_util.py index 37fe99276..3d8dd5c0a 100644 --- a/api/src/util/datetime_util.py +++ b/api/src/util/datetime_util.py @@ -1,3 +1,4 @@ +import zoneinfo from datetime import date, datetime, timezone from typing import Optional @@ -32,9 +33,8 @@ def adjust_timezone(timestamp: datetime, timezone_str: str) -> datetime: def make_timezone_aware(timestamp: datetime, timezone_str: str) -> datetime: - timestamp = timestamp.replace(tzinfo=None) - new_timezone = pytz.timezone(timezone_str) - return new_timezone.localize(timestamp) + new_timezone = zoneinfo.ZoneInfo(timezone_str) + return timestamp.replace(tzinfo=new_timezone) def get_now_us_eastern_datetime() -> datetime: From a60d72e4181d87625541507403484b10b1210970 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Fri, 3 May 2024 11:46:24 -0400 Subject: [PATCH 19/39] Cleanup --- .../data_migration/transformation/__init__.py | 6 + .../transform_oracle_data_task.py | 310 ++---------------- .../transformation/transform_util.py | 283 ++++++++++++++++ .../test_transform_oracle_data_task.py | 82 +---- .../transformation/test_transform_util.py | 78 +++++ 5 files changed, 388 insertions(+), 371 deletions(-) create mode 100644 api/src/data_migration/transformation/transform_util.py create mode 100644 api/tests/src/data_migration/transformation/test_transform_util.py diff --git a/api/src/data_migration/transformation/__init__.py b/api/src/data_migration/transformation/__init__.py index e69de29bb..d6ff9946f 100644 --- a/api/src/data_migration/transformation/__init__.py +++ b/api/src/data_migration/transformation/__init__.py @@ -0,0 +1,6 @@ +from typing import TypeAlias + +from src.db.models.staging.forecast import Tforecast, TforecastHist +from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist + +SourceSummary: TypeAlias = Tforecast | Tsynopsis | TforecastHist | TsynopsisHist diff --git a/api/src/data_migration/transformation/transform_oracle_data_task.py b/api/src/data_migration/transformation/transform_oracle_data_task.py index 30f13c3f5..a7ec6cf35 100644 --- a/api/src/data_migration/transformation/transform_oracle_data_task.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -1,12 +1,13 @@ import logging from datetime import datetime from enum import StrEnum -from typing import Sequence, Tuple, Type, TypeAlias, TypeVar, cast +from typing import Sequence, Tuple, Type, TypeVar, cast + from sqlalchemy import and_, select from src.adapters import db -from src.constants.lookup_constants import OpportunityCategory -from src.db.models.base import ApiSchemaTable, TimestampMixin +from src.data_migration.transformation import transform_util +from src.db.models.base import ApiSchemaTable from src.db.models.opportunity_models import ( Opportunity, OpportunityAssistanceListing, @@ -14,17 +15,16 @@ ) from src.db.models.staging.forecast import Tforecast, TforecastHist from src.db.models.staging.opportunity import Topportunity, TopportunityCfda -from src.db.models.staging.staging_base import StagingBase, StagingParamMixin +from src.db.models.staging.staging_base import StagingParamMixin from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist - from src.task.task import Task from src.util import datetime_util +from . import SourceSummary + S = TypeVar("S", bound=StagingParamMixin) D = TypeVar("D", bound=ApiSchemaTable) -SourceSummary: TypeAlias = Tforecast | Tsynopsis | TforecastHist | TsynopsisHist - logger = logging.getLogger(__name__) @@ -138,7 +138,9 @@ def process_opportunity( is_insert = target_opportunity is None logger.info("Transforming and upserting opportunity", extra=extra) - transformed_opportunity = transform_opportunity(source_opportunity, target_opportunity) + transformed_opportunity = transform_util.transform_opportunity( + source_opportunity, target_opportunity + ) self.db_session.merge(transformed_opportunity) if is_insert: @@ -219,7 +221,7 @@ def process_assistance_listing( is_insert = target_assistance_listing is None logger.info("Transforming and upserting assistance listing", extra=extra) - transformed_assistance_listing = transform_assistance_listing( + transformed_assistance_listing = transform_util.transform_assistance_listing( source_assistance_listing, target_assistance_listing ) self.db_session.merge(transformed_assistance_listing) @@ -233,7 +235,8 @@ def process_assistance_listing( source_assistance_listing.transformed_at = self.transform_time def process_opportunity_summaries(self) -> None: - logger.info("Starting processing of opportunity summaries") + logger.info("Processing opportunity summaries") + logger.info("Processing synopsis records") synopsis_records = self.fetch_with_opportunity( Tsynopsis, OpportunitySummary, @@ -245,6 +248,7 @@ def process_opportunity_summaries(self) -> None: ) self.process_opportunity_summary_group(synopsis_records) + logger.info("Processing synopsis hist records") synopsis_hist_records = self.fetch_with_opportunity( TsynopsisHist, OpportunitySummary, @@ -256,6 +260,7 @@ def process_opportunity_summaries(self) -> None: ) self.process_opportunity_summary_group(synopsis_hist_records) + logger.info("Processing forecast records") forecast_records = self.fetch_with_opportunity( Tforecast, OpportunitySummary, @@ -267,6 +272,7 @@ def process_opportunity_summaries(self) -> None: ) self.process_opportunity_summary_group(forecast_records) + logger.info("Processing forecast hist records") forecast_hist_records = self.fetch_with_opportunity( TforecastHist, OpportunitySummary, @@ -288,7 +294,7 @@ def process_opportunity_summary_group( self.increment(self.Metrics.TOTAL_ERROR_COUNT) logger.exception( "Failed to process opportunity summary", - extra=get_log_extra_summary(source_summary), + extra=transform_util.get_log_extra_summary(source_summary), ) def process_opportunity_summary( @@ -298,7 +304,7 @@ def process_opportunity_summary( opportunity: Opportunity | None, ) -> None: self.increment(self.Metrics.TOTAL_RECORDS_PROCESSED) - extra = get_log_extra_summary(source_summary) + extra = transform_util.get_log_extra_summary(source_summary) logger.info("Processing opportunity summary", extra=extra) if opportunity is None: @@ -323,7 +329,7 @@ def process_opportunity_summary( is_insert = target_summary is None logger.info("Transforming and upserting opportunity summary", extra=extra) - transformed_opportunity_summary = transform_opportunity_summary( + transformed_opportunity_summary = transform_util.transform_opportunity_summary( source_summary, target_summary ) self.db_session.merge(transformed_opportunity_summary) @@ -339,281 +345,3 @@ def process_opportunity_summary( def process_one_to_many_lookup_tables(self) -> None: # TODO - https://github.com/HHS/simpler-grants-gov/issues/1749 pass - - -############################### -# Transformations -############################### - - -def transform_opportunity( - source_opportunity: Topportunity, existing_opportunity: Opportunity | None -) -> Opportunity: - log_extra = {"opportunity_id": source_opportunity.opportunity_id} - - if existing_opportunity is None: - logger.info("Creating new opportunity record", extra=log_extra) - - # We always create a new opportunity record here and merge it in the calling function - # this way if there is any error doing the transformation, we don't modify the existing one. - target_opportunity = Opportunity(opportunity_id=source_opportunity.opportunity_id) - - target_opportunity.opportunity_number = source_opportunity.oppnumber - target_opportunity.opportunity_title = source_opportunity.opptitle - target_opportunity.agency = source_opportunity.owningagency - target_opportunity.category = transform_opportunity_category(source_opportunity.oppcategory) - target_opportunity.category_explanation = source_opportunity.category_explanation - target_opportunity.revision_number = source_opportunity.revision_number - target_opportunity.modified_comments = source_opportunity.modified_comments - target_opportunity.publisher_user_id = source_opportunity.publisheruid - target_opportunity.publisher_profile_id = source_opportunity.publisher_profile_id - - # The legacy system doesn't actually have this value as a boolean. There are several - # different letter codes. However, their API implementation also does this for their draft flag. - target_opportunity.is_draft = source_opportunity.is_draft != "N" - transform_update_create_timestamp(source_opportunity, target_opportunity, log_extra=log_extra) - - return target_opportunity - - -OPPORTUNITY_CATEGORY_MAP = { - "D": OpportunityCategory.DISCRETIONARY, - "M": OpportunityCategory.MANDATORY, - "C": OpportunityCategory.CONTINUATION, - "E": OpportunityCategory.EARMARK, - "O": OpportunityCategory.OTHER, -} - - -def transform_opportunity_category(value: str | None) -> OpportunityCategory | None: - if value is None or value == "": - return None - - if value not in OPPORTUNITY_CATEGORY_MAP: - raise ValueError("Unrecognized opportunity category: %s" % value) - - return OPPORTUNITY_CATEGORY_MAP[value] - - -def transform_assistance_listing( - source_assistance_listing: TopportunityCfda, - existing_assistance_listing: OpportunityAssistanceListing | None, -) -> OpportunityAssistanceListing: - log_extra = {"opportunity_assistance_listing_id": source_assistance_listing.opp_cfda_id} - - if existing_assistance_listing is None: - logger.info("Creating new assistance listing record", extra=log_extra) - - # We always create a new assistance listing record here and merge it in the calling function - # this way if there is any error doing the transformation, we don't modify the existing one. - target_assistance_listing = OpportunityAssistanceListing( - opportunity_assistance_listing_id=source_assistance_listing.opp_cfda_id, - opportunity_id=source_assistance_listing.opportunity_id, - ) - - target_assistance_listing.assistance_listing_number = source_assistance_listing.cfdanumber - target_assistance_listing.program_title = source_assistance_listing.programtitle - - transform_update_create_timestamp( - source_assistance_listing, target_assistance_listing, log_extra=log_extra - ) - - return target_assistance_listing - - -def transform_opportunity_summary( - source_summary: SourceSummary, incoming_summary: OpportunitySummary | None -) -> OpportunitySummary: - log_extra = get_log_extra_summary(source_summary) - - if incoming_summary is None: - logger.info("Creating new opportunity summary record", extra=log_extra) - target_summary = OpportunitySummary( - opportunity_id=source_summary.opportunity_id, - is_forecast=source_summary.is_forecast, - revision_number=None, - ) - - # Revision number is only found in the historical table - if isinstance(source_summary, (TsynopsisHist, TforecastHist)): - target_summary.revision_number = source_summary.revision_number - else: - # We create a new summary object and merge it outside this function - # that way if any modifications occur on the object and then it errors - # they aren't actually applied - target_summary = OpportunitySummary( - opportunity_summary_id=incoming_summary.opportunity_summary_id - ) - - # Fields in all 4 source tables - target_summary.version_number = source_summary.version_nbr - target_summary.is_cost_sharing = convert_yn_bool(source_summary.cost_sharing) - target_summary.post_date = source_summary.posting_date - target_summary.archive_date = source_summary.archive_date - target_summary.expected_number_of_awards = convert_numeric_str_to_int( - source_summary.number_of_awards - ) - target_summary.estimated_total_program_funding = convert_numeric_str_to_int( - source_summary.est_funding - ) - target_summary.award_floor = convert_numeric_str_to_int(source_summary.award_floor) - target_summary.award_ceiling = convert_numeric_str_to_int(source_summary.award_ceiling) - target_summary.additional_info_url = source_summary.fd_link_url - target_summary.additional_info_url_description = source_summary.fd_link_desc - target_summary.modification_comments = source_summary.modification_comments - target_summary.funding_category_description = source_summary.oth_cat_fa_desc - target_summary.applicant_eligibility_description = source_summary.applicant_elig_desc - target_summary.agency_name = source_summary.ac_name - target_summary.agency_email_address = source_summary.ac_email_addr - target_summary.agency_email_address_description = source_summary.ac_email_desc - target_summary.can_send_mail = convert_yn_bool(source_summary.sendmail) - target_summary.publisher_profile_id = source_summary.publisher_profile_id - target_summary.publisher_user_id = source_summary.publisheruid - target_summary.updated_by = source_summary.last_upd_id - target_summary.created_by = source_summary.creator_id - - # Some fields either are named different in synopsis/forecast - # or only come from one of those tables, so handle those here - if isinstance(source_summary, (Tsynopsis, TsynopsisHist)): - target_summary.summary_description = source_summary.syn_desc - target_summary.agency_code = source_summary.a_sa_code - target_summary.agency_phone_number = source_summary.ac_phone_number - - # Synopsis only fields - target_summary.agency_contact_description = source_summary.agency_contact_desc - target_summary.close_date = source_summary.response_date - target_summary.close_date_description = source_summary.response_date_desc - target_summary.unarchive_date = source_summary.unarchive_date - - else: # TForecast & TForecastHist - target_summary.summary_description = source_summary.forecast_desc - target_summary.agency_code = source_summary.agency_code - target_summary.agency_phone_number = source_summary.ac_phone - - # Forecast only fields - target_summary.forecasted_post_date = source_summary.est_synopsis_posting_date - target_summary.forecasted_close_date = source_summary.est_appl_response_date - target_summary.forecasted_close_date_description = ( - source_summary.est_appl_response_date_desc - ) - target_summary.forecasted_award_date = source_summary.est_award_date - target_summary.forecasted_project_start_date = source_summary.est_project_start_date - target_summary.fiscal_year = source_summary.fiscal_year - - # Historical only - if isinstance(source_summary, (TsynopsisHist, TforecastHist)): - target_summary.is_deleted = convert_action_type_to_is_deleted(source_summary.action_type) - else: - target_summary.is_deleted = False - - transform_update_create_timestamp(source_summary, target_summary, log_extra=log_extra) - - return target_summary - - -def convert_est_timestamp_to_utc(timestamp: datetime | None) -> datetime | None: - if timestamp is None: - return None - - # The timestamps we get from the legacy system have no timezone info - # but we know the database uses US Eastern timezone by default - # - # First add the America/New_York timezone without any other modification - aware_timestamp = datetime_util.make_timezone_aware(timestamp, "US/Eastern") - # Then adjust the timezone to UTC this will handle any DST or other conversion complexities - return datetime_util.adjust_timezone(aware_timestamp, "UTC") - - -def transform_update_create_timestamp( - source: StagingBase, target: TimestampMixin, log_extra: dict | None = None -) -> None: - # Convert the source timestamps to UTC - # Note: the type ignores are because created_date/last_upd_date are added - # on the individual class definitions, not the base class - due to how - # we need to maintain the column order of the legacy system. - # Every legacy table does have these columns. - created_timestamp = convert_est_timestamp_to_utc(source.created_date) # type: ignore[attr-defined] - updated_timestamp = convert_est_timestamp_to_utc(source.last_upd_date) # type: ignore[attr-defined] - - if created_timestamp is not None: - target.created_at = created_timestamp - else: - # This is incredibly rare, but possible - because our system requires - # we set something, we'll default to the current time and log a warning. - if log_extra is None: - log_extra = {} - - logger.warning( - f"{source.__class__} does not have a created_date timestamp set, setting value to now.", - extra=log_extra, - ) - target.created_at = datetime_util.utcnow() - - if updated_timestamp is not None: - target.updated_at = updated_timestamp - else: - # In the legacy system, they don't set whether something was updated - # until it receives an update. We always set the value, and on initial insert - # want it to be the same as the created_at. - target.updated_at = target.created_at - - -def convert_yn_bool(value: str | None) -> bool | None: - # Booleans in the Oracle database are stored as varchar/char - # columns with the values as Y/N - if value is None or value == "": - return None - - if value == "Y": - return True - - if value == "N": - return False - - # Just in case the column isn't actually a boolean - raise ValueError("Unexpected Y/N bool value: %s" % value) - - -def convert_action_type_to_is_deleted(value: str | None) -> bool | None: - if value is None or value == "": - return None - - if value == "D": # D = Delete - return True - - if value == "U": # U = Update - return False - - raise ValueError("Unexpected action type value: %s" % value) - - -def convert_numeric_str_to_int(value: str | None) -> int | None: - if value is None or value == "": - return None - - if value.isnumeric(): - return int(value) - - # From what we've found in the legacy data, some of these numeric strings - # are written out as "none", "not available", "n/a" or similar. All of these - # we're fine with collectively treating as null-equivalent - return None - - -def get_log_extra_summary(source_summary: SourceSummary) -> dict: - return { - "opportunity_id": source_summary.opportunity_id, - "is_forecast": source_summary.is_forecast, - "revision_number": getattr(source_summary, "revision_number", None), - } - - -def main(): - import src.logging - with src.logging.init("tmp"): - db_client = db.PostgresDBClient() - with db_client.get_session() as session: - TransformOracleDataTask(session).run() - -main() - diff --git a/api/src/data_migration/transformation/transform_util.py b/api/src/data_migration/transformation/transform_util.py new file mode 100644 index 000000000..b37576fec --- /dev/null +++ b/api/src/data_migration/transformation/transform_util.py @@ -0,0 +1,283 @@ +import logging +from datetime import datetime + +from src.constants.lookup_constants import OpportunityCategory +from src.db.models.base import TimestampMixin +from src.db.models.opportunity_models import ( + Opportunity, + OpportunityAssistanceListing, + OpportunitySummary, +) +from src.db.models.staging.forecast import TforecastHist +from src.db.models.staging.opportunity import Topportunity, TopportunityCfda +from src.db.models.staging.staging_base import StagingBase +from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist +from src.util import datetime_util + +from . import SourceSummary + +logger = logging.getLogger(__name__) + +OPPORTUNITY_CATEGORY_MAP = { + "D": OpportunityCategory.DISCRETIONARY, + "M": OpportunityCategory.MANDATORY, + "C": OpportunityCategory.CONTINUATION, + "E": OpportunityCategory.EARMARK, + "O": OpportunityCategory.OTHER, +} + + +def transform_opportunity( + source_opportunity: Topportunity, existing_opportunity: Opportunity | None +) -> Opportunity: + log_extra = {"opportunity_id": source_opportunity.opportunity_id} + + if existing_opportunity is None: + logger.info("Creating new opportunity record", extra=log_extra) + + # We always create a new opportunity record here and merge it in the calling function + # this way if there is any error doing the transformation, we don't modify the existing one. + target_opportunity = Opportunity(opportunity_id=source_opportunity.opportunity_id) + + target_opportunity.opportunity_number = source_opportunity.oppnumber + target_opportunity.opportunity_title = source_opportunity.opptitle + target_opportunity.agency = source_opportunity.owningagency + target_opportunity.category = transform_opportunity_category(source_opportunity.oppcategory) + target_opportunity.category_explanation = source_opportunity.category_explanation + target_opportunity.revision_number = source_opportunity.revision_number + target_opportunity.modified_comments = source_opportunity.modified_comments + target_opportunity.publisher_user_id = source_opportunity.publisheruid + target_opportunity.publisher_profile_id = source_opportunity.publisher_profile_id + + # The legacy system doesn't actually have this value as a boolean. There are several + # different letter codes. However, their API implementation also does this for their draft flag. + target_opportunity.is_draft = source_opportunity.is_draft != "N" + transform_update_create_timestamp(source_opportunity, target_opportunity, log_extra=log_extra) + + return target_opportunity + + +def transform_opportunity_category(value: str | None) -> OpportunityCategory | None: + if value is None or value == "": + return None + + if value not in OPPORTUNITY_CATEGORY_MAP: + raise ValueError("Unrecognized opportunity category: %s" % value) + + return OPPORTUNITY_CATEGORY_MAP[value] + + +def transform_assistance_listing( + source_assistance_listing: TopportunityCfda, + existing_assistance_listing: OpportunityAssistanceListing | None, +) -> OpportunityAssistanceListing: + log_extra = {"opportunity_assistance_listing_id": source_assistance_listing.opp_cfda_id} + + if existing_assistance_listing is None: + logger.info("Creating new assistance listing record", extra=log_extra) + + # We always create a new assistance listing record here and merge it in the calling function + # this way if there is any error doing the transformation, we don't modify the existing one. + target_assistance_listing = OpportunityAssistanceListing( + opportunity_assistance_listing_id=source_assistance_listing.opp_cfda_id, + opportunity_id=source_assistance_listing.opportunity_id, + ) + + target_assistance_listing.assistance_listing_number = source_assistance_listing.cfdanumber + target_assistance_listing.program_title = source_assistance_listing.programtitle + + transform_update_create_timestamp( + source_assistance_listing, target_assistance_listing, log_extra=log_extra + ) + + return target_assistance_listing + + +def transform_opportunity_summary( + source_summary: SourceSummary, incoming_summary: OpportunitySummary | None +) -> OpportunitySummary: + log_extra = get_log_extra_summary(source_summary) + + if incoming_summary is None: + logger.info("Creating new opportunity summary record", extra=log_extra) + target_summary = OpportunitySummary( + opportunity_id=source_summary.opportunity_id, + is_forecast=source_summary.is_forecast, + revision_number=None, + ) + + # Revision number is only found in the historical table + if isinstance(source_summary, (TsynopsisHist, TforecastHist)): + target_summary.revision_number = source_summary.revision_number + else: + # We create a new summary object and merge it outside this function + # that way if any modifications occur on the object and then it errors + # they aren't actually applied + target_summary = OpportunitySummary( + opportunity_summary_id=incoming_summary.opportunity_summary_id + ) + + # Fields in all 4 source tables + target_summary.version_number = source_summary.version_nbr + target_summary.is_cost_sharing = convert_yn_bool(source_summary.cost_sharing) + target_summary.post_date = source_summary.posting_date + target_summary.archive_date = source_summary.archive_date + target_summary.expected_number_of_awards = convert_numeric_str_to_int( + source_summary.number_of_awards + ) + target_summary.estimated_total_program_funding = convert_numeric_str_to_int( + source_summary.est_funding + ) + target_summary.award_floor = convert_numeric_str_to_int(source_summary.award_floor) + target_summary.award_ceiling = convert_numeric_str_to_int(source_summary.award_ceiling) + target_summary.additional_info_url = source_summary.fd_link_url + target_summary.additional_info_url_description = source_summary.fd_link_desc + target_summary.modification_comments = source_summary.modification_comments + target_summary.funding_category_description = source_summary.oth_cat_fa_desc + target_summary.applicant_eligibility_description = source_summary.applicant_elig_desc + target_summary.agency_name = source_summary.ac_name + target_summary.agency_email_address = source_summary.ac_email_addr + target_summary.agency_email_address_description = source_summary.ac_email_desc + target_summary.can_send_mail = convert_yn_bool(source_summary.sendmail) + target_summary.publisher_profile_id = source_summary.publisher_profile_id + target_summary.publisher_user_id = source_summary.publisheruid + target_summary.updated_by = source_summary.last_upd_id + target_summary.created_by = source_summary.creator_id + + # Some fields either are named different in synopsis/forecast + # or only come from one of those tables, so handle those here + if isinstance(source_summary, (Tsynopsis, TsynopsisHist)): + target_summary.summary_description = source_summary.syn_desc + target_summary.agency_code = source_summary.a_sa_code + target_summary.agency_phone_number = source_summary.ac_phone_number + + # Synopsis only fields + target_summary.agency_contact_description = source_summary.agency_contact_desc + target_summary.close_date = source_summary.response_date + target_summary.close_date_description = source_summary.response_date_desc + target_summary.unarchive_date = source_summary.unarchive_date + + else: # TForecast & TForecastHist + target_summary.summary_description = source_summary.forecast_desc + target_summary.agency_code = source_summary.agency_code + target_summary.agency_phone_number = source_summary.ac_phone + + # Forecast only fields + target_summary.forecasted_post_date = source_summary.est_synopsis_posting_date + target_summary.forecasted_close_date = source_summary.est_appl_response_date + target_summary.forecasted_close_date_description = ( + source_summary.est_appl_response_date_desc + ) + target_summary.forecasted_award_date = source_summary.est_award_date + target_summary.forecasted_project_start_date = source_summary.est_project_start_date + target_summary.fiscal_year = source_summary.fiscal_year + + # Historical only + if isinstance(source_summary, (TsynopsisHist, TforecastHist)): + target_summary.is_deleted = convert_action_type_to_is_deleted(source_summary.action_type) + else: + target_summary.is_deleted = False + + transform_update_create_timestamp(source_summary, target_summary, log_extra=log_extra) + + return target_summary + + +def convert_est_timestamp_to_utc(timestamp: datetime | None) -> datetime | None: + if timestamp is None: + return None + + # The timestamps we get from the legacy system have no timezone info + # but we know the database uses US Eastern timezone by default + # + # First add the America/New_York timezone without any other modification + aware_timestamp = datetime_util.make_timezone_aware(timestamp, "US/Eastern") + # Then adjust the timezone to UTC this will handle any DST or other conversion complexities + return datetime_util.adjust_timezone(aware_timestamp, "UTC") + + +def transform_update_create_timestamp( + source: StagingBase, target: TimestampMixin, log_extra: dict | None = None +) -> None: + # Convert the source timestamps to UTC + # Note: the type ignores are because created_date/last_upd_date are added + # on the individual class definitions, not the base class - due to how + # we need to maintain the column order of the legacy system. + # Every legacy table does have these columns. + created_timestamp = convert_est_timestamp_to_utc(source.created_date) # type: ignore[attr-defined] + updated_timestamp = convert_est_timestamp_to_utc(source.last_upd_date) # type: ignore[attr-defined] + + if created_timestamp is not None: + target.created_at = created_timestamp + else: + # This is incredibly rare, but possible - because our system requires + # we set something, we'll default to the current time and log a warning. + if log_extra is None: + log_extra = {} + + logger.warning( + f"{source.__class__} does not have a created_date timestamp set, setting value to now.", + extra=log_extra, + ) + target.created_at = datetime_util.utcnow() + + if updated_timestamp is not None: + target.updated_at = updated_timestamp + else: + # In the legacy system, they don't set whether something was updated + # until it receives an update. We always set the value, and on initial insert + # want it to be the same as the created_at. + target.updated_at = target.created_at + + +def convert_yn_bool(value: str | None) -> bool | None: + # Booleans in the Oracle database are stored as varchar/char + # columns with the values as Y/N + if value is None or value == "": + return None + + if value == "Y": + return True + + if value == "N": + return False + + # Just in case the column isn't actually a boolean + raise ValueError("Unexpected Y/N bool value: %s" % value) + + +def convert_action_type_to_is_deleted(value: str | None) -> bool | None: + if value is None or value == "": + return None + + if value == "D": # D = Delete + return True + + if value == "U": # U = Update + return False + + raise ValueError("Unexpected action type value: %s" % value) + + +def convert_numeric_str_to_int(value: str | None) -> int | None: + if value is None or value == "": + return None + + if value.isnumeric(): + return int(value) + + # From what we've found in the legacy data, some of these numeric strings + # are written out as "none", "not available", "n/a" or similar. All of these + # we're fine with collectively treating as null-equivalent + return None + + +def get_log_extra_summary(source_summary: SourceSummary) -> dict: + return { + "opportunity_id": source_summary.opportunity_id, + "is_forecast": source_summary.is_forecast, + # This value only exists on non-historical records + # use getattr instead of an isinstance if/else for simplicity + "revision_number": getattr(source_summary, "revision_number", None), + "table_name": source_summary.__tablename__, + } diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py index 102307a98..381788bce 100644 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -1,15 +1,8 @@ -from datetime import datetime from typing import Tuple import pytest -from freezegun import freeze_time -from src.constants.lookup_constants import OpportunityCategory -from src.data_migration.transformation.transform_oracle_data_task import ( - TransformOracleDataTask, - transform_opportunity_category, - transform_update_create_timestamp, -) +from src.data_migration.transformation.transform_oracle_data_task import TransformOracleDataTask from src.db.models.opportunity_models import ( Opportunity, OpportunityAssistanceListing, @@ -29,8 +22,6 @@ StagingTopportunityFactory, StagingTsynopsisFactory, StagingTsynopsisHistFactory, - StagingTopportunityCfdaFactory, - StagingTopportunityFactory, ) @@ -337,6 +328,7 @@ def validate_opportunity_summary( """ assert opportunity_summary.is_deleted == is_deleted + class TestTransformOpportunity(BaseTestClass): @pytest.fixture() def transform_oracle_data_task( @@ -803,73 +795,3 @@ def test_process_opportunity_summary_invalid_value_errors( transform_oracle_data_task.process_opportunity_summary( source_summary, None, opportunity ) - - -@pytest.mark.parametrize( - "value,expected_value", - [ - # Just check a few - ("D", OpportunityCategory.DISCRETIONARY), - ("M", OpportunityCategory.MANDATORY), - ("O", OpportunityCategory.OTHER), - (None, None), - ("", None), - ], -) -def test_transform_opportunity_category(value, expected_value): - assert transform_opportunity_category(value) == expected_value - - -@pytest.mark.parametrize("value", ["A", "B", "mandatory", "other", "hello"]) -def test_transform_opportunity_category_unexpected_value(value): - with pytest.raises(ValueError, match="Unrecognized opportunity category"): - transform_opportunity_category(value) - - -@pytest.mark.parametrize( - "created_date,last_upd_date,expected_created_at,expected_updated_at", - [ - ### Using string timestamps rather than defining the dates directly for readability - # A few happy scenarios - ( - "2020-01-01T12:00:00", - "2020-06-01T12:00:00", - "2020-01-01T17:00:00+00:00", - "2020-06-01T16:00:00+00:00", - ), - ( - "2021-01-31T21:30:15", - "2021-12-31T23:59:59", - "2021-02-01T02:30:15+00:00", - "2022-01-01T04:59:59+00:00", - ), - # Leap year handling - ( - "2024-02-28T23:00:59", - "2024-02-29T19:10:10", - "2024-02-29T04:00:59+00:00", - "2024-03-01T00:10:10+00:00", - ), - # last_upd_date is None, created_date is used for both - ("2020-05-31T16:32:08", None, "2020-05-31T20:32:08+00:00", "2020-05-31T20:32:08+00:00"), - ("2020-07-15T20:00:00", None, "2020-07-16T00:00:00+00:00", "2020-07-16T00:00:00+00:00"), - # both input values are None, the current time is used (which we set for the purposes of this test below) - (None, None, "2023-05-10T12:00:00+00:00", "2023-05-10T12:00:00+00:00"), - ], -) -@freeze_time("2023-05-10 12:00:00", tz_offset=0) -def test_transform_update_create_timestamp( - created_date, last_upd_date, expected_created_at, expected_updated_at -): - created_datetime = datetime.fromisoformat(created_date) if created_date is not None else None - last_upd_datetime = datetime.fromisoformat(last_upd_date) if last_upd_date is not None else None - - source = StagingTopportunityFactory.build( - created_date=created_datetime, last_upd_date=last_upd_datetime - ) - destination = OpportunityFactory.build() - - transform_update_create_timestamp(source, destination) - - assert destination.created_at == datetime.fromisoformat(expected_created_at) - assert destination.updated_at == datetime.fromisoformat(expected_updated_at) diff --git a/api/tests/src/data_migration/transformation/test_transform_util.py b/api/tests/src/data_migration/transformation/test_transform_util.py new file mode 100644 index 000000000..d9d3ba3a4 --- /dev/null +++ b/api/tests/src/data_migration/transformation/test_transform_util.py @@ -0,0 +1,78 @@ +from datetime import datetime + +import pytest +from freezegun import freeze_time + +from src.constants.lookup_constants import OpportunityCategory +from src.data_migration.transformation import transform_util +from tests.src.db.models.factories import OpportunityFactory, StagingTopportunityFactory + + +@pytest.mark.parametrize( + "value,expected_value", + [ + # Just check a few + ("D", OpportunityCategory.DISCRETIONARY), + ("M", OpportunityCategory.MANDATORY), + ("O", OpportunityCategory.OTHER), + (None, None), + ("", None), + ], +) +def test_transform_opportunity_category(value, expected_value): + assert transform_util.transform_opportunity_category(value) == expected_value + + +@pytest.mark.parametrize("value", ["A", "B", "mandatory", "other", "hello"]) +def test_transform_opportunity_category_unexpected_value(value): + with pytest.raises(ValueError, match="Unrecognized opportunity category"): + transform_util.transform_opportunity_category(value) + + +@pytest.mark.parametrize( + "created_date,last_upd_date,expected_created_at,expected_updated_at", + [ + ### Using string timestamps rather than defining the dates directly for readability + # A few happy scenarios + ( + "2020-01-01T12:00:00", + "2020-06-01T12:00:00", + "2020-01-01T17:00:00+00:00", + "2020-06-01T16:00:00+00:00", + ), + ( + "2021-01-31T21:30:15", + "2021-12-31T23:59:59", + "2021-02-01T02:30:15+00:00", + "2022-01-01T04:59:59+00:00", + ), + # Leap year handling + ( + "2024-02-28T23:00:59", + "2024-02-29T19:10:10", + "2024-02-29T04:00:59+00:00", + "2024-03-01T00:10:10+00:00", + ), + # last_upd_date is None, created_date is used for both + ("2020-05-31T16:32:08", None, "2020-05-31T20:32:08+00:00", "2020-05-31T20:32:08+00:00"), + ("2020-07-15T20:00:00", None, "2020-07-16T00:00:00+00:00", "2020-07-16T00:00:00+00:00"), + # both input values are None, the current time is used (which we set for the purposes of this test below) + (None, None, "2023-05-10T12:00:00+00:00", "2023-05-10T12:00:00+00:00"), + ], +) +@freeze_time("2023-05-10 12:00:00", tz_offset=0) +def test_transform_update_create_timestamp( + created_date, last_upd_date, expected_created_at, expected_updated_at +): + created_datetime = datetime.fromisoformat(created_date) if created_date is not None else None + last_upd_datetime = datetime.fromisoformat(last_upd_date) if last_upd_date is not None else None + + source = StagingTopportunityFactory.build( + created_date=created_datetime, last_upd_date=last_upd_datetime + ) + destination = OpportunityFactory.build() + + transform_util.transform_update_create_timestamp(source, destination) + + assert destination.created_at == datetime.fromisoformat(expected_created_at) + assert destination.updated_at == datetime.fromisoformat(expected_updated_at) From 52e253487f76ee3d3df25d8afcb36eb573ea9166 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Fri, 3 May 2024 12:10:25 -0400 Subject: [PATCH 20/39] Adding a few final tests --- .../transformation/transform_util.py | 12 ++--- .../test_transform_oracle_data_task.py | 2 - .../transformation/test_transform_util.py | 44 +++++++++++++++++++ api/tests/src/db/models/factories.py | 24 ---------- 4 files changed, 50 insertions(+), 32 deletions(-) diff --git a/api/src/data_migration/transformation/transform_util.py b/api/src/data_migration/transformation/transform_util.py index b37576fec..675fc677a 100644 --- a/api/src/data_migration/transformation/transform_util.py +++ b/api/src/data_migration/transformation/transform_util.py @@ -263,13 +263,13 @@ def convert_numeric_str_to_int(value: str | None) -> int | None: if value is None or value == "": return None - if value.isnumeric(): + try: return int(value) - - # From what we've found in the legacy data, some of these numeric strings - # are written out as "none", "not available", "n/a" or similar. All of these - # we're fine with collectively treating as null-equivalent - return None + except ValueError: + # From what we've found in the legacy data, some of these numeric strings + # are written out as "none", "not available", "n/a" or similar. All of these + # we're fine with collectively treating as null-equivalent + return None def get_log_extra_summary(source_summary: SourceSummary) -> dict: diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py index 381788bce..ef9043ea1 100644 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -96,7 +96,6 @@ def setup_synopsis_forecast( is_delete: bool = False, is_already_processed: bool = False, source_values: dict | None = None, - all_fields_null: bool = False, opportunity: Opportunity | None = None, ): if source_values is None: @@ -126,7 +125,6 @@ def setup_synopsis_forecast( opportunity=None, # To override the factory trying to create something is_deleted=is_delete, already_transformed=is_already_processed, - all_fields_null=all_fields_null, ) if create_existing: diff --git a/api/tests/src/data_migration/transformation/test_transform_util.py b/api/tests/src/data_migration/transformation/test_transform_util.py index d9d3ba3a4..98bfcdb68 100644 --- a/api/tests/src/data_migration/transformation/test_transform_util.py +++ b/api/tests/src/data_migration/transformation/test_transform_util.py @@ -76,3 +76,47 @@ def test_transform_update_create_timestamp( assert destination.created_at == datetime.fromisoformat(expected_created_at) assert destination.updated_at == datetime.fromisoformat(expected_updated_at) + + +@pytest.mark.parametrize( + "value,expected_value", [("Y", True), ("N", False), ("", None), (None, None)] +) +def test_convert_yn_boolean(value, expected_value): + assert transform_util.convert_yn_bool(value) == expected_value + + +@pytest.mark.parametrize("value", ["X", "Z", "y", "n", "1", "0"]) +def test_convert_yn_boolean_unexpected_value(value): + with pytest.raises(ValueError, match="Unexpected Y/N bool value"): + transform_util.convert_yn_bool(value) + + +@pytest.mark.parametrize( + "value,expected_value", [("D", True), ("U", False), ("", None), (None, None)] +) +def test_convert_action_type_to_is_deleted(value, expected_value): + assert transform_util.convert_action_type_to_is_deleted(value) == expected_value + + +@pytest.mark.parametrize("value", ["A", "B", "d", "u"]) +def test_convert_action_type_to_is_deleted_unexpected_value(value): + with pytest.raises(ValueError, match="Unexpected action type value"): + transform_util.convert_action_type_to_is_deleted(value) + + +@pytest.mark.parametrize( + "value,expected_value", + [ + ("1", 1), + ("0", 0), + ("123123123", 123123123), + ("-5", -5), + ("", None), + (None, None), + ("words", None), + ("zero", None), + ("n/a", None), + ], +) +def test_convert_numeric_str_to_int(value, expected_value): + assert transform_util.convert_numeric_str_to_int(value) == expected_value diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index f68a69854..86e2a9990 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -783,12 +783,6 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) - # Trait to set all nullable fields to None - all_fields_null = factory.Trait( - # TODO - posting_date=None - ) - class StagingTsynopsisHistFactory(StagingTsynopsisFactory): class Meta: @@ -802,12 +796,6 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) - # Trait to set all nullable fields to None - all_fields_null = factory.Trait( - # TODO - posting_date=None - ) - class StagingTforecastFactory(BaseFactory): class Meta: @@ -884,12 +872,6 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) - # Trait to set all nullable fields to None - all_fields_null = factory.Trait( - # TODO - posting_date=None - ) - class StagingTforecastHistFactory(StagingTforecastFactory): class Meta: @@ -903,12 +885,6 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) - # Trait to set all nullable fields to None - all_fields_null = factory.Trait( - # TODO - posting_date=None - ) - #################################### # Transfer Table Factories From 4bba72bc5224d946758f59411f7cf9a6ce606ed9 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Fri, 3 May 2024 12:13:36 -0400 Subject: [PATCH 21/39] remove extra code --- .../transformation/test_transform_oracle_data_task.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py index ef9043ea1..85d62d06b 100644 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -93,10 +93,10 @@ def setup_synopsis_forecast( is_forecast: bool, revision_number: int | None, create_existing: bool, + opportunity: Opportunity, is_delete: bool = False, is_already_processed: bool = False, source_values: dict | None = None, - opportunity: Opportunity | None = None, ): if source_values is None: source_values = {} @@ -112,17 +112,13 @@ def setup_synopsis_forecast( else: factory_cls = StagingTsynopsisHistFactory - # TODO - don't think we need this added complexity - not a possible scenario here - # If you don't provide an opportunity, you need to provide an ID - if opportunity is not None: - source_values["opportunity_id"] = opportunity.opportunity_id - if revision_number is not None: source_values["revision_number"] = revision_number source_summary = factory_cls.create( **source_values, opportunity=None, # To override the factory trying to create something + opportunity_id=opportunity.opportunity_id, is_deleted=is_delete, already_transformed=is_already_processed, ) From fdb33c4c1317ba8f94c325ef6c1045b2f7466c17 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Fri, 3 May 2024 12:18:54 -0400 Subject: [PATCH 22/39] remove unused bit --- .../transformation/test_transform_oracle_data_task.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py index 85d62d06b..2449c23f6 100644 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -311,15 +311,6 @@ def validate_opportunity_summary( source_summary, opportunity_summary, matching_fields, expect_values_to_match ) - """ - TODO - validate these fields - is_cost_sharing - expected_number_of_awards - estimated_total_program_funding - award_floor - award_ceiling - sendmail - """ assert opportunity_summary.is_deleted == is_deleted From c2f2966c74c1579a6b7128e411dd7e780edae81c Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Tue, 7 May 2024 10:05:39 -0400 Subject: [PATCH 23/39] [Issue #1749] Add transformations for the one-to-many lookup tables --- .../data_migration/transformation/__init__.py | 10 +- .../transform_oracle_data_task.py | 268 +++++++++++++++++- .../transformation/transform_util.py | 193 ++++++++++++- api/src/db/models/opportunity_models.py | 9 + api/src/db/models/staging/forecast.py | 32 +++ api/src/db/models/staging/synopsis.py | 31 ++ .../test_transform_oracle_data_task.py | 154 +++++++++- api/tests/src/db/models/factories.py | 237 +++++++++++++++- 8 files changed, 915 insertions(+), 19 deletions(-) diff --git a/api/src/data_migration/transformation/__init__.py b/api/src/data_migration/transformation/__init__.py index d6ff9946f..6a9ad16a0 100644 --- a/api/src/data_migration/transformation/__init__.py +++ b/api/src/data_migration/transformation/__init__.py @@ -1,6 +1,12 @@ from typing import TypeAlias -from src.db.models.staging.forecast import Tforecast, TforecastHist -from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist +from src.db.models.staging.forecast import Tforecast, TforecastHist, TapplicanttypesForecast, TapplicanttypesForecastHist, TfundinstrForecastHist, TfundinstrForecast, TfundactcatForecastHist, TfundactcatForecast +from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist, TapplicanttypesSynopsis, TapplicanttypesSynopsisHist, TfundinstrSynopsisHist, TfundinstrSynopsis, TfundactcatSynopsisHist, TfundactcatSynopsis SourceSummary: TypeAlias = Tforecast | Tsynopsis | TforecastHist | TsynopsisHist + +SourceApplicantType: TypeAlias = TapplicanttypesForecast | TapplicanttypesForecastHist | TapplicanttypesSynopsis | TapplicanttypesSynopsisHist + +SourceFundingCategory: TypeAlias = TfundactcatForecast | TfundactcatForecastHist | TfundactcatSynopsis | TfundactcatSynopsisHist + +SourceFundingInstrument: TypeAlias = TfundinstrForecastHist | TfundinstrForecast | TfundinstrSynopsisHist | TfundinstrSynopsis \ No newline at end of file diff --git a/api/src/data_migration/transformation/transform_oracle_data_task.py b/api/src/data_migration/transformation/transform_oracle_data_task.py index a7ec6cf35..9f5088f6b 100644 --- a/api/src/data_migration/transformation/transform_oracle_data_task.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -11,16 +11,18 @@ from src.db.models.opportunity_models import ( Opportunity, OpportunityAssistanceListing, - OpportunitySummary, + OpportunitySummary, LinkOpportunitySummaryApplicantType, LinkOpportunitySummaryFundingCategory, LinkOpportunitySummaryFundingInstrument, ) -from src.db.models.staging.forecast import Tforecast, TforecastHist +from src.db.models.staging.forecast import Tforecast, TforecastHist, TapplicanttypesForecast, TapplicanttypesForecastHist, TfundactcatForecast, TfundactcatForecastHist, TfundinstrForecast, \ + TfundinstrForecastHist from src.db.models.staging.opportunity import Topportunity, TopportunityCfda from src.db.models.staging.staging_base import StagingParamMixin -from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist +from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist, TapplicanttypesSynopsis, TapplicanttypesSynopsisHist, TfundactcatSynopsis, TfundactcatSynopsisHist, TfundinstrSynopsis, \ + TfundinstrSynopsisHist from src.task.task import Task from src.util import datetime_util -from . import SourceSummary +from . import SourceSummary, SourceApplicantType, SourceFundingInstrument, SourceFundingCategory S = TypeVar("S", bound=StagingParamMixin) D = TypeVar("D", bound=ApiSchemaTable) @@ -97,6 +99,32 @@ def fetch_with_opportunity( ), ) + def fetch_with_opportunity_summary(self, source_model: Type[S], destination_model: Type[D], join_clause: Sequence, is_forecast: bool, is_historical_table: bool) -> list[Tuple[S, D | None, OpportunitySummary | None]]: + # setup the join clause for getting the opportunity summary + + if is_historical_table: + opportunity_summary_join_clause = [ + source_model.opportunity_id == OpportunitySummary.opportunity_id, + OpportunitySummary.is_forecast.is_(is_forecast), + source_model.revision_number == OpportunitySummary.revision_number, + ] + else: + # TODO - can simplify this a bit + opportunity_summary_join_clause = [ + source_model.opportunity_id == OpportunitySummary.opportunity_id, + OpportunitySummary.is_forecast.is_(is_forecast), + OpportunitySummary.revision_number.is_(None), + ] + + + return self.db_session.execute( + select(source_model, destination_model, OpportunitySummary) + .join(OpportunitySummary, and_(*opportunity_summary_join_clause), isouter=True) + .join(destination_model, and_(*join_clause), isouter=True) + .where(source_model.transformed_at.is_(None)) + .execution_options(yield_per=5000) + ) + def process_opportunities(self) -> None: # Fetch all opportunities that were modified # Alongside that, grab the existing opportunity record @@ -343,5 +371,233 @@ def process_opportunity_summary( source_summary.transformed_at = self.transform_time def process_one_to_many_lookup_tables(self) -> None: - # TODO - https://github.com/HHS/simpler-grants-gov/issues/1749 - pass + self.process_link_applicant_types() + self.process_link_funding_categories() + self.process_link_funding_categories() + + def process_link_applicant_types(self) -> None: + forecast_applicant_type_records = self.fetch_with_opportunity_summary(TapplicanttypesForecast, LinkOpportunitySummaryApplicantType, [ + TapplicanttypesForecast.at_frcst_id == LinkOpportunitySummaryApplicantType.legacy_applicant_type_id, + OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryApplicantType.opportunity_summary_id + ], is_forecast=True, is_historical_table=False) + self.process_link_applicant_types_group(forecast_applicant_type_records) + + forecast_applicant_type_hist_records = self.fetch_with_opportunity_summary(TapplicanttypesForecastHist, LinkOpportunitySummaryApplicantType, [ + TapplicanttypesForecastHist.at_frcst_id == LinkOpportunitySummaryApplicantType.legacy_applicant_type_id, + OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryApplicantType.opportunity_summary_id + ], is_forecast=True, is_historical_table=True) + self.process_link_applicant_types_group(forecast_applicant_type_hist_records) + + synopsis_applicant_type_records = self.fetch_with_opportunity_summary(TapplicanttypesSynopsis, LinkOpportunitySummaryApplicantType, [ + TapplicanttypesSynopsis.at_syn_id == LinkOpportunitySummaryApplicantType.legacy_applicant_type_id, + OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryApplicantType.opportunity_summary_id + ], is_forecast=False, is_historical_table=False) + self.process_link_applicant_types_group(synopsis_applicant_type_records) + + synopsis_applicant_type_hist_records = self.fetch_with_opportunity_summary(TapplicanttypesSynopsisHist, LinkOpportunitySummaryApplicantType, [ + TapplicanttypesSynopsisHist.at_syn_id == LinkOpportunitySummaryApplicantType.legacy_applicant_type_id, + OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryApplicantType.opportunity_summary_id + ], is_forecast=False, is_historical_table=True) + self.process_link_applicant_types_group(synopsis_applicant_type_hist_records) + + def process_link_applicant_types_group(self, records: Sequence[Tuple[SourceApplicantType, LinkOpportunitySummaryApplicantType | None, OpportunitySummary | None]]) -> None: + for source_applicant_type, target_applicant_type, opportunity_summary in records: + try: + self.process_link_applicant_type(source_applicant_type, target_applicant_type, opportunity_summary) + except ValueError: + self.increment(self.Metrics.TOTAL_ERROR_COUNT) + logger.exception( + "Failed to process opportunity summary applicant type", + extra=transform_util.get_log_extra_applicant_type(source_applicant_type), + ) + + + def process_link_applicant_type(self, source_applicant_type: SourceApplicantType, target_applicant_type: LinkOpportunitySummaryApplicantType | None, opportunity_summary: OpportunitySummary | None) -> None: + self.increment(self.Metrics.TOTAL_RECORDS_PROCESSED) + extra = transform_util.get_log_extra_applicant_type(source_applicant_type) + logger.info("Processing applicant type", extra=extra) + + if opportunity_summary is None: + # This shouldn't be possible as the incoming data has foreign keys, but as a safety net + # we'll make sure the opportunity actually exists + raise ValueError( + "Applicant type record cannot be processed as the opportunity summary for it does not exist" + ) + + if source_applicant_type.is_deleted: + logger.info("Deleting applicant type", extra=extra) + + if target_applicant_type is None: + raise ValueError("Cannot delete applicant type as it does not exist") + + self.increment(self.Metrics.TOTAL_RECORDS_DELETED) + self.db_session.delete(target_applicant_type) + else: + # To avoid incrementing metrics for records we fail to transform, record + # here whether it's an insert/update and we'll increment after transforming + is_insert = target_applicant_type is None + + logger.info("Transforming and upserting applicant type", extra=extra) + transformed_applicant_type = transform_util.convert_opportunity_summary_applicant_type( + source_applicant_type, target_applicant_type, opportunity_summary + ) + self.db_session.merge(transformed_applicant_type) + + if is_insert: + self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + else: + self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) + + logger.info("Processed applicant type", extra=extra) + source_applicant_type.transformed_at = self.transform_time + + def process_link_funding_categories(self) -> None: + forecast_funding_category_records = self.fetch_with_opportunity_summary(TfundactcatForecast, LinkOpportunitySummaryFundingCategory, [ + TfundactcatForecast.fac_frcst_id == LinkOpportunitySummaryFundingCategory.legacy_funding_category_id, + OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryFundingCategory.opportunity_summary_id + ], is_forecast=True, is_historical_table=False) + self.process_link_funding_categories_group(forecast_funding_category_records) + + forecast_funding_category_hist_records = self.fetch_with_opportunity_summary(TfundactcatForecastHist, LinkOpportunitySummaryFundingCategory, [ + TfundactcatForecastHist.fac_frcst_id == LinkOpportunitySummaryFundingCategory.legacy_funding_category_id, + OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryFundingCategory.opportunity_summary_id + ], is_forecast=True, is_historical_table=True) + self.process_link_funding_categories_group(forecast_funding_category_hist_records) + + synopsis_funding_category_records = self.fetch_with_opportunity_summary(TfundactcatSynopsis, LinkOpportunitySummaryFundingCategory, [ + TfundactcatSynopsis.fac_syn_id == LinkOpportunitySummaryFundingCategory.legacy_funding_category_id, + OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryFundingCategory.opportunity_summary_id + ], is_forecast=False, is_historical_table=False) + self.process_link_funding_categories_group(synopsis_funding_category_records) + + synopsis_funding_category_hist_records = self.fetch_with_opportunity_summary(TfundactcatSynopsisHist, LinkOpportunitySummaryFundingCategory, [ + TfundactcatSynopsisHist.fac_syn_id == LinkOpportunitySummaryFundingCategory.legacy_funding_category_id, + OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryFundingCategory.opportunity_summary_id + ], is_forecast=False, is_historical_table=True) + self.process_link_funding_categories_group(synopsis_funding_category_hist_records) + + def process_link_funding_categories_group(self, records: Sequence[Tuple[SourceFundingCategory, LinkOpportunitySummaryFundingCategory | None, OpportunitySummary | None]]) -> None: + for source_funding_category, target_funding_category, opportunity_summary in records: + try: + self.process_link_funding_category(source_funding_category, target_funding_category, opportunity_summary) + except ValueError: + self.increment(self.Metrics.TOTAL_ERROR_COUNT) + logger.exception( + "Failed to process opportunity summary funding category", + extra=transform_util.get_log_extra_funding_category(source_funding_category), + ) + + + def process_link_funding_category(self, source_funding_category: SourceFundingCategory, target_funding_category: LinkOpportunitySummaryFundingCategory | None, opportunity_summary: OpportunitySummary | None) -> None: + self.increment(self.Metrics.TOTAL_RECORDS_PROCESSED) + extra = transform_util.get_log_extra_funding_category(source_funding_category) + logger.info("Processing funding category", extra=extra) + + if opportunity_summary is None: + # This shouldn't be possible as the incoming data has foreign keys, but as a safety net + # we'll make sure the opportunity actually exists + raise ValueError( + "Funding category record cannot be processed as the opportunity summary for it does not exist" + ) + + if source_funding_category.is_deleted: + logger.info("Deleting funding category", extra=extra) + + if target_funding_category is None: + raise ValueError("Cannot delete funding category as it does not exist") + + self.increment(self.Metrics.TOTAL_RECORDS_DELETED) + self.db_session.delete(target_funding_category) + else: + # To avoid incrementing metrics for records we fail to transform, record + # here whether it's an insert/update and we'll increment after transforming + is_insert = source_funding_category is None + + logger.info("Transforming and upserting funding category", extra=extra) + transformed_funding_category = transform_util.convert_opportunity_summary_funding_category( + source_funding_category, target_funding_category, opportunity_summary + ) + self.db_session.merge(transformed_funding_category) + + if is_insert: + self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + else: + self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) + + logger.info("Processed funding category", extra=extra) + source_funding_category.transformed_at = self.transform_time + + def process_link_funding_instruments(self) -> None: + forecast_funding_instrument_records = self.fetch_with_opportunity_summary(TfundinstrForecast, LinkOpportunitySummaryFundingInstrument, [ + TfundinstrForecast.fi_frcst_id == LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id, + OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryFundingInstrument.opportunity_summary_id + ], is_forecast=True, is_historical_table=False) + self.process_link_funding_instruments_group(forecast_funding_instrument_records) + + forecast_funding_instrument_hist_records = self.fetch_with_opportunity_summary(TfundinstrForecastHist, LinkOpportunitySummaryFundingInstrument, [ + TfundinstrForecastHist.fi_frcst_id == LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id, + OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryFundingInstrument.opportunity_summary_id + ], is_forecast=True, is_historical_table=True) + self.process_link_funding_instruments_group(forecast_funding_instrument_hist_records) + + synopsis_funding_instrument_records = self.fetch_with_opportunity_summary(TfundinstrSynopsis, LinkOpportunitySummaryFundingInstrument, [ + TfundinstrSynopsis.fi_syn_id == LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id, + OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryFundingInstrument.opportunity_summary_id + ], is_forecast=False, is_historical_table=False) + self.process_link_funding_instruments_group(synopsis_funding_instrument_records) + + synopsis_funding_instrument_hist_records = self.fetch_with_opportunity_summary(TfundinstrSynopsisHist, LinkOpportunitySummaryFundingInstrument, [ + TfundinstrSynopsisHist.fi_syn_id == LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id, + OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryFundingInstrument.opportunity_summary_id + ], is_forecast=False, is_historical_table=True) + self.process_link_funding_instruments_group(synopsis_funding_instrument_hist_records) + + def process_link_funding_instruments_group(self, records: Sequence[Tuple[SourceFundingInstrument, LinkOpportunitySummaryFundingInstrument | None, OpportunitySummary | None]]) -> None: + for source_funding_instrument, target_funding_instrument, opportunity_summary in records: + try: + self.process_link_funding_category(source_funding_instrument, target_funding_instrument, opportunity_summary) + except ValueError: + self.increment(self.Metrics.TOTAL_ERROR_COUNT) + logger.exception( + "Failed to process opportunity summary funding instrument", + extra=transform_util.get_log_extra_funding_category(source_funding_instrument), + ) + + def process_link_funding_instrument(self, source_funding_instrument: SourceFundingInstrument, target_funding_instrument: LinkOpportunitySummaryFundingInstrument | None, opportunity_summary: OpportunitySummary | None) -> None: + self.increment(self.Metrics.TOTAL_RECORDS_PROCESSED) + extra = transform_util.get_log_extra_funding_instrument(source_funding_instrument) + logger.info("Processing funding instrument", extra=extra) + + if opportunity_summary is None: + # This shouldn't be possible as the incoming data has foreign keys, but as a safety net + # we'll make sure the opportunity actually exists + raise ValueError( + "Funding instrument record cannot be processed as the opportunity summary for it does not exist" + ) + + if source_funding_instrument.is_deleted: + logger.info("Deleting funding instrument", extra=extra) + + if target_funding_instrument is None: + raise ValueError("Cannot delete funding instrument as it does not exist") + + self.increment(self.Metrics.TOTAL_RECORDS_DELETED) + self.db_session.delete(target_funding_instrument) + else: + # To avoid incrementing metrics for records we fail to transform, record + # here whether it's an insert/update and we'll increment after transforming + is_insert = source_funding_instrument is None + + logger.info("Transforming and upserting funding instrument", extra=extra) + transformed_funding_instrument = transform_util.convert_opportunity_summary_funding_instrument( + source_funding_instrument, target_funding_instrument, opportunity_summary + ) + self.db_session.merge(transformed_funding_instrument) + + if is_insert: + self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + else: + self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) + + logger.info("Processed funding instrument", extra=extra) + source_funding_instrument.transformed_at = self.transform_time \ No newline at end of file diff --git a/api/src/data_migration/transformation/transform_util.py b/api/src/data_migration/transformation/transform_util.py index 675fc677a..c08e88fbb 100644 --- a/api/src/data_migration/transformation/transform_util.py +++ b/api/src/data_migration/transformation/transform_util.py @@ -1,20 +1,20 @@ import logging from datetime import datetime -from src.constants.lookup_constants import OpportunityCategory +from src.constants.lookup_constants import OpportunityCategory, ApplicantType, FundingInstrument, FundingCategory from src.db.models.base import TimestampMixin from src.db.models.opportunity_models import ( Opportunity, OpportunityAssistanceListing, - OpportunitySummary, + OpportunitySummary, LinkOpportunitySummaryApplicantType, LinkOpportunitySummaryFundingInstrument, LinkOpportunitySummaryFundingCategory, ) -from src.db.models.staging.forecast import TforecastHist +from src.db.models.staging.forecast import TforecastHist, TapplicanttypesForecast, TapplicanttypesForecastHist, TfundactcatForecastHist, TfundactcatForecast, TfundinstrForecastHist, TfundinstrForecast from src.db.models.staging.opportunity import Topportunity, TopportunityCfda from src.db.models.staging.staging_base import StagingBase from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist from src.util import datetime_util -from . import SourceSummary +from . import SourceSummary, SourceApplicantType, SourceFundingCategory, SourceFundingInstrument logger = logging.getLogger(__name__) @@ -26,6 +26,62 @@ "O": OpportunityCategory.OTHER, } +APPLICANT_TYPE_MAP = { + "00": ApplicantType.STATE_GOVERNMENTS, + "01": ApplicantType.COUNTY_GOVERNMENTS, + "02": ApplicantType.CITY_OR_TOWNSHIP_GOVERNMENTS, + "04": ApplicantType.SPECIAL_DISTRICT_GOVERNMENTS, + "05": ApplicantType.INDEPENDENT_SCHOOL_DISTRICTS, + "06": ApplicantType.PUBLIC_AND_STATE_INSTITUTIONS_OF_HIGHER_EDUCATION, + "07": ApplicantType.FEDERALLY_RECOGNIZED_NATIVE_AMERICAN_TRIBAL_GOVERNMENTS, + "08": ApplicantType.PUBLIC_AND_INDIAN_HOUSING_AUTHORITIES, + "11": ApplicantType.OTHER_NATIVE_AMERICAN_TRIBAL_ORGANIZATIONS, + "12": ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITH_501C3, + "13": ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITHOUT_501C3, + "20": ApplicantType.PRIVATE_INSTITUTIONS_OF_HIGHER_EDUCATION, + "21": ApplicantType.INDIVIDUALS, + "22": ApplicantType.FOR_PROFIT_ORGANIZATIONS_OTHER_THAN_SMALL_BUSINESSES, + "23": ApplicantType.SMALL_BUSINESSES, + "25": ApplicantType.OTHER, + "99": ApplicantType.UNRESTRICTED +} + +FUNDING_CATEGORY_MAP = { + "RA": FundingCategory.RECOVERY_ACT, + "AG": FundingCategory.AGRICULTURE, + "AR": FundingCategory.ARTS, + "BC": FundingCategory.BUSINESS_AND_COMMERCE, + "CD": FundingCategory.COMMUNITY_DEVELOPMENT, + "CP": FundingCategory.CONSUMER_PROTECTION, + "DPR": FundingCategory.DISASTER_PREVENTION_AND_RELIEF, + "ED": FundingCategory.EDUCATION, + "ELT": FundingCategory.EMPLOYMENT_LABOR_AND_TRAINING, + "EN": FundingCategory.ENERGY, + "ENV": FundingCategory.ENVIRONMENT, + "FN": FundingCategory.FOOD_AND_NUTRITION, + "HL": FundingCategory.HEALTH, + "HO": FundingCategory.HOUSING, + "HU": FundingCategory.HUMANITIES, + "IIJ": FundingCategory.INFRASTRUCTURE_INVESTMENT_AND_JOBS_ACT, + "IS": FundingCategory.INFORMATION_AND_STATISTICS, + "ISS": FundingCategory.INCOME_SECURITY_AND_SOCIAL_SERVICES, + "LJL": FundingCategory.LAW_JUSTICE_AND_LEGAL_SERVICES, + "NR": FundingCategory.NATURAL_RESOURCES, + "OZ": FundingCategory.OPPORTUNITY_ZONE_BENEFITS, + "RD": FundingCategory.REGIONAL_DEVELOPMENT, + "ST": FundingCategory.SCIENCE_TECHNOLOGY_AND_OTHER_RESEARCH_AND_DEVELOPMENT, + "T": FundingCategory.TRANSPORTATION, + "ACA": FundingCategory.AFFORDABLE_CARE_ACT, + "O": FundingCategory.OTHER, +} + +FUNDING_INSTRUMENT_MAP = { + "CA": FundingInstrument.COOPERATIVE_AGREEMENT, + "G": FundingInstrument.GRANT, + "PC": FundingInstrument.PROCUREMENT_CONTRACT, + "O": FundingInstrument.OTHER, +} + def transform_opportunity( source_opportunity: Topportunity, existing_opportunity: Opportunity | None @@ -67,6 +123,35 @@ def transform_opportunity_category(value: str | None) -> OpportunityCategory | N return OPPORTUNITY_CATEGORY_MAP[value] +def transform_applicant_type(value: str | None) -> ApplicantType | None: + if value is None or value == "": + return None + + if value not in APPLICANT_TYPE_MAP: + raise ValueError("Unrecognized applicant type: %s" % value) + + return APPLICANT_TYPE_MAP[value] + +def transform_funding_category(value: str | None) -> FundingCategory | None: + if value is None or value == "": + return None + + if value not in FUNDING_CATEGORY_MAP: + raise ValueError("Unrecognized funding category: %s" % value) + + return FUNDING_CATEGORY_MAP[value] + + +def transform_funding_instrument(value: str | None) -> FundingInstrument | None: + if value is None or value == "": + return None + + if value not in FUNDING_INSTRUMENT_MAP: + raise ValueError("Unrecognized funding instrument: %s" % value) + + return FUNDING_INSTRUMENT_MAP[value] + + def transform_assistance_listing( source_assistance_listing: TopportunityCfda, existing_assistance_listing: OpportunityAssistanceListing | None, @@ -183,6 +268,79 @@ def transform_opportunity_summary( return target_summary +def convert_opportunity_summary_applicant_type(source_applicant_type: SourceApplicantType, existing_applicant_type: LinkOpportunitySummaryApplicantType | None, opportunity_summary: OpportunitySummary) -> LinkOpportunitySummaryApplicantType: + log_extra = get_log_extra_applicant_type(source_applicant_type) + + # NOTE: The columns we're working with here are mostly the primary keys + # While we do support updates, that's really only going to affect + # the last update user + timestamps. From checking the prod data + # there are basically zero updates to this data (~5 occurred 10+ years ago) + if existing_applicant_type is None: + logger.info("Creating new applicant type record", extra=log_extra) + + applicant_type = transform_applicant_type(source_applicant_type.at_id) + + # The legacy ID is named differently in the forecast/synopsis tables + if isinstance(source_applicant_type, (TapplicanttypesForecast, TapplicanttypesForecastHist)): + legacy_applicant_type_id = source_applicant_type.at_frcst_id + else: + legacy_applicant_type_id = source_applicant_type.at_syn_id + + target_applicant_type = LinkOpportunitySummaryApplicantType( + opportunity_summary_id=opportunity_summary.opportunity_summary_id, + legacy_applicant_type_id=legacy_applicant_type_id, + applicant_type=applicant_type + ) + + target_applicant_type.updated_by = source_applicant_type.last_upd_id + target_applicant_type.created_by = source_applicant_type.creator_id + + return target_applicant_type + +def convert_opportunity_summary_funding_instrument(source_funding_instrument: SourceFundingInstrument, existing_funding_instrument: LinkOpportunitySummaryFundingInstrument | None, opportunity_summary: OpportunitySummary) -> LinkOpportunitySummaryFundingInstrument: + log_extra = get_log_extra_funding_instrument(source_funding_instrument) + + if existing_funding_instrument is None: + logger.info("Creating new funding instrument record", extra=log_extra) + + target_funding_instrument = LinkOpportunitySummaryFundingInstrument( + opportunity_summary_id=opportunity_summary.opportunity_summary_id, + ) + + target_funding_instrument.funding_instrument = transform_funding_instrument(source_funding_instrument.fi_id) + target_funding_instrument.updated_by = source_funding_instrument.last_upd_id + target_funding_instrument.created_by = source_funding_instrument.creator_id + + # The legacy ID is named differently in the forecast/synopsis tables + if isinstance(target_funding_instrument, (TfundinstrForecast, TfundinstrForecastHist)): + target_funding_instrument.legacy_funding_instrument_id = source_funding_instrument.fi_syn_id + else: + target_funding_instrument.legacy_funding_instrument_id = source_funding_instrument.fi_frcst_id + + return target_funding_instrument + +def convert_opportunity_summary_funding_category(source_funding_category: SourceFundingCategory, existing_funding_category: LinkOpportunitySummaryFundingCategory | None, opportunity_summary: OpportunitySummary) -> LinkOpportunitySummaryFundingCategory: + log_extra = get_log_extra_funding_category(source_funding_category) + + if existing_funding_category is None: + logger.info("Creating new funding category record", extra=log_extra) + + target_funding_category = LinkOpportunitySummaryFundingCategory( + opportunity_summary_id=opportunity_summary.opportunity_summary_id, + ) + + target_funding_category.funding_category = transform_funding_category(source_funding_category.fac_id) + target_funding_category.updated_by = source_funding_category.last_upd_id + target_funding_category.created_by = source_funding_category.creator_id + + # The legacy ID is named differently in the forecast/synopsis tables + if isinstance(target_funding_category, (TfundactcatForecast, TfundactcatForecastHist)): + target_funding_category.legacy_funding_category_id = source_funding_category.fac_frcst_id + else: + target_funding_category.legacy_funding_category_id = source_funding_category.fac_syn_id + + return target_funding_category + def convert_est_timestamp_to_utc(timestamp: datetime | None) -> datetime | None: if timestamp is None: return None @@ -281,3 +439,30 @@ def get_log_extra_summary(source_summary: SourceSummary) -> dict: "revision_number": getattr(source_summary, "revision_number", None), "table_name": source_summary.__tablename__, } + +def get_log_extra_applicant_type(source_applicant_type: SourceApplicantType) -> dict: + return { + "opportunity_id": source_applicant_type.opportunity_id, + "at_frcst_id": getattr(source_applicant_type, "at_frcst_id", None), + "at_syn_id": getattr(source_applicant_type, "at_syn_id", None), + "revision_number": getattr(source_applicant_type, "revision_number", None), + "table_name": source_applicant_type.__tablename__, + } + +def get_log_extra_funding_category(source_funding_category: SourceFundingCategory) -> dict: + return { + "opportunity_id": source_funding_category.opportunity_id, + "fac_frcst_id": getattr(source_funding_category, "fac_frcst_id", None), + "fac_syn_id": getattr(source_funding_category, "fac_syn_id", None), + "revision_number": getattr(source_funding_category, "revision_number", None), + "table_name": source_funding_category.__tablename__, + } + +def get_log_extra_funding_instrument(source_funding_instrument: SourceFundingInstrument) -> dict: + return { + "opportunity_id": source_funding_instrument.opportunity_id, + "fi_frcst_id": getattr(source_funding_instrument, "fi_frcst_id", None), + "fi_syn_id": getattr(source_funding_instrument, "fi_syn_id", None), + "revision_number": getattr(source_funding_instrument, "revision_number", None), + "table_name": source_funding_instrument.__tablename__, + } \ No newline at end of file diff --git a/api/src/db/models/opportunity_models.py b/api/src/db/models/opportunity_models.py index 2f8287298..423583d8f 100644 --- a/api/src/db/models/opportunity_models.py +++ b/api/src/db/models/opportunity_models.py @@ -262,6 +262,15 @@ class LinkOpportunitySummaryFundingCategory(ApiSchemaTable, TimestampMixin): class LinkOpportunitySummaryApplicantType(ApiSchemaTable, TimestampMixin): __tablename__ = "link_opportunity_summary_applicant_type" + __table_args__ = ( + UniqueConstraint( + "opportunity_summary_id", "legacy_applicant_type_id" + ), + # Need to define the table args like this to inherit whatever we set on the super table + # otherwise we end up overwriting things and Alembic remakes the whole table + ApiSchemaTable.__table_args__, + ) + opportunity_summary_id: Mapped[int] = mapped_column( BigInteger, ForeignKey(OpportunitySummary.opportunity_summary_id), diff --git a/api/src/db/models/staging/forecast.py b/api/src/db/models/staging/forecast.py index f80163302..ab8111360 100644 --- a/api/src/db/models/staging/forecast.py +++ b/api/src/db/models/staging/forecast.py @@ -42,27 +42,59 @@ class TapplicanttypesForecast( __tablename__ = "tapplicanttypes_forecast" + forecast: Mapped[Tforecast | None] = relationship( + Tforecast, + primaryjoin="TapplicanttypesForecast.opportunity_id == foreign(Tforecast.opportunity_id)", + uselist=False, + ) + class TapplicanttypesForecastHist( StagingBase, forecast_mixin.TapplicanttypesForecastHistMixin, StagingParamMixin ): __tablename__ = "tapplicanttypes_forecast_hist" + forecast: Mapped[TforecastHist | None] = relationship( + TforecastHist, + primaryjoin="and_(TapplicanttypesForecastHist.opportunity_id == foreign(TforecastHist.opportunity_id), TapplicanttypesForecastHist.revision_number == foreign(TforecastHist.revision_number))", + uselist=False, + ) class TfundactcatForecast(StagingBase, forecast_mixin.TfundactcatForecastMixin, StagingParamMixin): __tablename__ = "tfundactcat_forecast" + forecast: Mapped[Tforecast | None] = relationship( + Tforecast, + primaryjoin="TfundactcatForecast.opportunity_id == foreign(Tforecast.opportunity_id)", + uselist=False, + ) class TfundactcatForecastHist( StagingBase, forecast_mixin.TfundactcatForecastHistMixin, StagingParamMixin ): __tablename__ = "tfundactcat_forecast_hist" + forecast: Mapped[TforecastHist | None] = relationship( + TforecastHist, + primaryjoin="and_(TfundactcatForecastHist.opportunity_id == foreign(TforecastHist.opportunity_id), TfundactcatForecastHist.revision_number == foreign(TforecastHist.revision_number))", + uselist=False, + ) class TfundinstrForecast(StagingBase, forecast_mixin.TfundinstrForecastMixin, StagingParamMixin): __tablename__ = "tfundinstr_forecast" + forecast: Mapped[Tforecast | None] = relationship( + Tforecast, + primaryjoin="TfundinstrForecast.opportunity_id == foreign(Tforecast.opportunity_id)", + uselist=False, + ) class TfundinstrForecastHist( StagingBase, forecast_mixin.TfundinstrForecastHistMixin, StagingParamMixin ): __tablename__ = "tfundinstr_forecast_hist" + + forecast: Mapped[TforecastHist | None] = relationship( + TforecastHist, + primaryjoin="and_(TfundinstrForecastHist.opportunity_id == foreign(TforecastHist.opportunity_id), TfundinstrForecastHist.revision_number == foreign(TforecastHist.revision_number))", + uselist=False, + ) diff --git a/api/src/db/models/staging/synopsis.py b/api/src/db/models/staging/synopsis.py index e1e828045..b570ccff2 100644 --- a/api/src/db/models/staging/synopsis.py +++ b/api/src/db/models/staging/synopsis.py @@ -41,28 +41,59 @@ class TapplicanttypesSynopsis( ): __tablename__ = "tapplicanttypes_synopsis" + synopsis: Mapped[Tsynopsis | None] = relationship( + Tsynopsis, + primaryjoin="TapplicanttypesSynopsis.opportunity_id == foreign(Tsynopsis.opportunity_id)", + uselist=False, + ) class TapplicanttypesSynopsisHist( StagingBase, synopsis_mixin.TapplicanttypesSynopsisHistMixin, StagingParamMixin ): __tablename__ = "tapplicanttypes_synopsis_hist" + synopsis: Mapped[TsynopsisHist | None] = relationship( + TsynopsisHist, + primaryjoin="and_(TapplicanttypesSynopsisHist.opportunity_id == foreign(TsynopsisHist.opportunity_id), TapplicanttypesSynopsisHist.revision_number == foreign(TsynopsisHist.revision_number))", + uselist=False, + ) class TfundactcatSynopsis(StagingBase, synopsis_mixin.TfundactcatSynopsisMixin, StagingParamMixin): __tablename__ = "tfundactcat_synopsis" + synopsis: Mapped[Tsynopsis | None] = relationship( + Tsynopsis, + primaryjoin="TfundactcatSynopsis.opportunity_id == foreign(Tsynopsis.opportunity_id)", + uselist=False, + ) class TfundactcatSynopsisHist( StagingBase, synopsis_mixin.TfundactcatSynopsisHistMixin, StagingParamMixin ): __tablename__ = "tfundactcat_synopsis_hist" + synopsis: Mapped[TsynopsisHist | None] = relationship( + TsynopsisHist, + primaryjoin="and_(TfundactcatSynopsisHist.opportunity_id == foreign(TsynopsisHist.opportunity_id), TfundactcatSynopsisHist.revision_number == foreign(TsynopsisHist.revision_number))", + uselist=False, + ) class TfundinstrSynopsis(StagingBase, synopsis_mixin.TfundinstrSynopsisMixin, StagingParamMixin): __tablename__ = "tfundinstr_synopsis" + synopsis: Mapped[Tsynopsis | None] = relationship( + Tsynopsis, + primaryjoin="TfundinstrSynopsis.opportunity_id == foreign(Tsynopsis.opportunity_id)", + uselist=False, + ) class TfundinstrSynopsisHist( StagingBase, synopsis_mixin.TfundinstrSynopsisHistMixin, StagingParamMixin ): __tablename__ = "tfundinstr_synopsis_hist" + + synopsis: Mapped[TsynopsisHist | None] = relationship( + TsynopsisHist, + primaryjoin="and_(TfundinstrSynopsisHist.opportunity_id == foreign(TsynopsisHist.opportunity_id), TfundinstrSynopsisHist.revision_number == foreign(TsynopsisHist.revision_number))", + uselist=False, + ) \ No newline at end of file diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py index 2449c23f6..e3c90f6ca 100644 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -2,15 +2,16 @@ import pytest +from src.constants.lookup_constants import ApplicantType from src.data_migration.transformation.transform_oracle_data_task import TransformOracleDataTask from src.db.models.opportunity_models import ( Opportunity, OpportunityAssistanceListing, - OpportunitySummary, + OpportunitySummary, LinkOpportunitySummaryApplicantType, ) -from src.db.models.staging.forecast import TforecastHist +from src.db.models.staging.forecast import TforecastHist, TapplicanttypesForecastHist from src.db.models.staging.opportunity import Topportunity, TopportunityCfda -from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist +from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist, TapplicanttypesSynopsis, TapplicanttypesSynopsisHist from tests.conftest import BaseTestClass from tests.src.db.models.factories import ( OpportunityAssistanceListingFactory, @@ -21,8 +22,10 @@ StagingTopportunityCfdaFactory, StagingTopportunityFactory, StagingTsynopsisFactory, - StagingTsynopsisHistFactory, + StagingTsynopsisHistFactory, StagingTapplicanttypesForecastFactory, StagingTapplicanttypesForecastHistFactory, LinkOpportunitySummaryApplicantTypeFactory, StagingTapplicanttypesSynopsisFactory, + StagingTapplicanttypesSynopsisHistFactory, ) +from sqlalchemy import and_ def setup_opportunity( @@ -131,6 +134,58 @@ def setup_synopsis_forecast( return source_summary +def setup_applicant_type( + create_existing: bool, + opportunity_summary: OpportunitySummary, + legacy_lookup_value: str, + applicant_type: ApplicantType | None = None, + is_delete: bool = False, + is_already_processed: bool = False, + source_values: dict | None = None, +): + if create_existing and is_delete is False and applicant_type is None: + raise Exception("If create_existing is True, is_delete is False - must provide the properly converted / mapped value for applicant_type") + + if source_values is None: + source_values = {} + + if opportunity_summary.is_forecast: + source_values["forecast"] = None + if opportunity_summary.revision_number is None: + factory_cls = StagingTapplicanttypesForecastFactory + else: + factory_cls = StagingTapplicanttypesForecastHistFactory + source_values["revision_number"] = opportunity_summary.revision_number + else: + source_values["synopsis"] = None + if opportunity_summary.revision_number is None: + factory_cls = StagingTapplicanttypesSynopsisFactory + else: + factory_cls = StagingTapplicanttypesSynopsisHistFactory + source_values["revision_number"] = opportunity_summary.revision_number + + source_applicant_type = factory_cls.create( + **source_values, + opportunity_id=opportunity_summary.opportunity_id, + is_deleted=is_delete, + already_transformed=is_already_processed, + at_id=legacy_lookup_value, + ) + + if create_existing: + if opportunity_summary.is_forecast: + legacy_id = source_applicant_type.at_frcst_id + else: + legacy_id = source_applicant_type.at_syn_id + + LinkOpportunitySummaryApplicantTypeFactory.create( + opportunity_summary=opportunity_summary, + legacy_applicant_type_id=legacy_id, + applicant_type=applicant_type + ) + + return source_applicant_type + def validate_matching_fields( source, destination, fields: list[Tuple[str, str]], expect_all_to_match: bool ): @@ -313,6 +368,41 @@ def validate_opportunity_summary( assert opportunity_summary.is_deleted == is_deleted +def validate_applicant_type(db_session, source_applicant_type, expect_in_db: bool = True, expected_applicant_type: ApplicantType | None = None): + if isinstance(source_applicant_type, (TapplicanttypesSynopsis, TapplicanttypesSynopsisHist)): + is_forecast = False + legacy_id = source_applicant_type.at_syn_id + else: + is_forecast = True + legacy_id = source_applicant_type.at_frcst_id + + revision_number = None + if isinstance(source_applicant_type, (TapplicanttypesSynopsisHist, TapplicanttypesForecastHist)): + revision_number = source_applicant_type.revision_number + + # In order to properly find the link table value, need to first determine + # the opportunity summary in a subquery + opportunity_summary_id = db_session.query(OpportunitySummary.opportunity_summary_id).filter( + OpportunitySummary.revision_number == revision_number, OpportunitySummary.is_forecast == is_forecast, OpportunitySummary.opportunity_id == source_applicant_type.opportunity_id + ).scalar() + + link_applicant_type = ( + db_session.query(LinkOpportunitySummaryApplicantType) + .filter( + LinkOpportunitySummaryApplicantType.legacy_applicant_type_id == legacy_id, + LinkOpportunitySummaryApplicantType.opportunity_summary_id == opportunity_summary_id + ) + .one_or_none() + ) + + if not expect_in_db: + assert link_applicant_type is None + return + + assert link_applicant_type is not None + assert link_applicant_type.applicant_type == expected_applicant_type + + class TestTransformOpportunity(BaseTestClass): @pytest.fixture() @@ -780,3 +870,59 @@ def test_process_opportunity_summary_invalid_value_errors( transform_oracle_data_task.process_opportunity_summary( source_summary, None, opportunity ) + + +class TestTransformApplicantType(BaseTestClass): + @pytest.fixture() + def transform_oracle_data_task( + self, db_session, enable_factory_create, truncate_opportunities + ) -> TransformOracleDataTask: + return TransformOracleDataTask(db_session) + + def test_process_applicant_types(self, db_session, transform_oracle_data_task): + opportunity_summary_forecast = OpportunitySummaryFactory.create(is_forecast=True, revision_number=None, no_link_values=True) + forecast_insert1 = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="00") + forecast_update1 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="01", applicant_type=ApplicantType.COUNTY_GOVERNMENTS) + forecast_update2 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="02", applicant_type=ApplicantType.CITY_OR_TOWNSHIP_GOVERNMENTS) + forecast_delete1 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="04", applicant_type=ApplicantType.SPECIAL_DISTRICT_GOVERNMENTS) + forecast_delete2 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="05", applicant_type=ApplicantType.INDEPENDENT_SCHOOL_DISTRICTS) + forecast_update_already_processed = setup_applicant_type(create_existing=True, is_already_processed=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="06", applicant_type=ApplicantType.PUBLIC_AND_STATE_INSTITUTIONS_OF_HIGHER_EDUCATION) + + + opportunity_summary_forecast_hist = OpportunitySummaryFactory.create(is_forecast=True, revision_number=3, no_link_values=True) + forecast_hist_insert1 = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="07") + forecast_hist_update1 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="08", applicant_type=ApplicantType.PUBLIC_AND_INDIAN_HOUSING_AUTHORITIES) + forecast_hist_update2 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="11", applicant_type=ApplicantType.OTHER_NATIVE_AMERICAN_TRIBAL_ORGANIZATIONS) + forecast_hist_delete1 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="12", applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITH_501C3) + forecast_hist_already_processed = setup_applicant_type(create_existing=False, is_delete=True, is_already_processed=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="13", applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITHOUT_501C3) + + opportunity_summary_syn = OpportunitySummaryFactory.create(is_forecast=False, revision_number=None, no_link_values=True) + syn_insert1 = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="20") + syn_insert2 = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="21") + syn_update1 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="22", applicant_type=ApplicantType.FOR_PROFIT_ORGANIZATIONS_OTHER_THAN_SMALL_BUSINESSES) + syn_update2 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="23", applicant_type=ApplicantType.SMALL_BUSINESSES) + syn_delete1 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="25", applicant_type=ApplicantType.OTHER) + syn_delete2 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="99", applicant_type=ApplicantType.UNRESTRICTED) + syn_delete_but_current_missing = setup_applicant_type(create_existing=False, is_delete=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="07", applicant_type=ApplicantType.FEDERALLY_RECOGNIZED_NATIVE_AMERICAN_TRIBAL_GOVERNMENTS) + syn_update_already_processed = setup_applicant_type(create_existing=True, is_already_processed=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="08", applicant_type=ApplicantType.PUBLIC_AND_INDIAN_HOUSING_AUTHORITIES) + + opportunity_summary_syn_hist = OpportunitySummaryFactory.create(is_forecast=False, revision_number=21, no_link_values=True) + syn_hist_insert1 = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="11") + syn_hist_update1 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="12", applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITH_501C3) + syn_hist_update2 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="13", applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITHOUT_501C3) + syn_hist_delete1 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="25", applicant_type=ApplicantType.OTHER) + syn_hist_delete2 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="99", applicant_type=ApplicantType.UNRESTRICTED) + # TODO - note this one + syn_hist_insert_invalid_type = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="X", applicant_type=ApplicantType.STATE_GOVERNMENTS) + + transform_oracle_data_task.process_link_applicant_types() + print(transform_oracle_data_task.metrics) + + validate_applicant_type(db_session, forecast_insert1, expected_applicant_type=ApplicantType.STATE_GOVERNMENTS) + validate_applicant_type(db_session, forecast_hist_insert1, expected_applicant_type=ApplicantType.FEDERALLY_RECOGNIZED_NATIVE_AMERICAN_TRIBAL_GOVERNMENTS) + validate_applicant_type(db_session, syn_insert1, expected_applicant_type=ApplicantType.PRIVATE_INSTITUTIONS_OF_HIGHER_EDUCATION) + validate_applicant_type(db_session, syn_insert2, expected_applicant_type=ApplicantType.INDIVIDUALS) + validate_applicant_type(db_session, syn_hist_insert1, expected_applicant_type=ApplicantType.OTHER_NATIVE_AMERICAN_TRIBAL_ORGANIZATIONS) + + validate_applicant_type(db_session, forecast_update1, expected_applicant_type=ApplicantType.COUNTY_GOVERNMENTS) + validate_applicant_type(db_session, forecast_update2, expected_applicant_type=ApplicantType.CITY_OR_TOWNSHIP_GOVERNMENTS) diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index 86e2a9990..4a03ebef9 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -320,7 +320,7 @@ class OpportunitySummaryFactory(BaseFactory): class Meta: model = opportunity_models.OpportunitySummary - opportunity = factory.SubFactory(OpportunityFactory) + opportunity = factory.SubFactory(OpportunityFactory, current_opportunity_summary=None) opportunity_id = factory.LazyAttribute(lambda s: s.opportunity.opportunity_id) summary_description = factory.Faker("summary_description") @@ -568,8 +568,6 @@ class OpportunityAssistanceListingFactory(BaseFactory): class Meta: model = opportunity_models.OpportunityAssistanceListing - opportunity_assistance_listing_id = factory.Sequence(lambda n: n) - opportunity = factory.SubFactory(OpportunityFactory) opportunity_id = factory.LazyAttribute(lambda a: a.opportunity.opportunity_id) @@ -885,6 +883,239 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) +# TODO - move these to the top? +APPLICANT_TYPE_IDS = ["00", "01", "02", "04", "05", "06", "07", "08", "11", "12", "13", "20", "21", "22", "23", "25", "99"] +FUNDING_CATEGORY_IDS = ["RA", "AG", "AR", "BC", "CD", "CP", "DPR", "ED", "ELT", "EN", "ENV", "FN", "HL", "HO", "HU", "IIJ", "IS", "ISS", "LJL", "NR", "OZ", "RD", "ST", "T", "ACA", "O"] +FUNDING_INSTRUMENT_IDS = ["CA", "G", "PC", "O"] + +class StagingTapplicanttypesForecastFactory(BaseFactory): + class Meta: + model = staging.forecast.TapplicanttypesForecast + + at_frcst_id = factory.Sequence(lambda n: n) + + at_id = factory.Iterator(APPLICANT_TYPE_IDS) + + forecast = factory.SubFactory(StagingTforecastFactory) + opportunity_id = factory.LazyAttribute(lambda s: s.forecast.opportunity_id) + + # Default to being a new insert/update + is_deleted = False + transformed_at = None + + created_date = factory.Faker("date_time_between", start_date="-10y", end_date="-5y") + last_upd_date = sometimes_none( + factory.Faker("date_time_between", start_date="-5y", end_date="now") + ) + + class Params: + already_transformed = factory.Trait( + transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") + ) + +class StagingTapplicanttypesForecastHistFactory(StagingTapplicanttypesForecastFactory): + class Meta: + model = staging.forecast.TapplicanttypesForecastHist + + forecast = factory.SubFactory(StagingTforecastHistFactory) + opportunity_id = factory.LazyAttribute(lambda s: s.forecast.opportunity_id) + revision_number = factory.LazyAttribute(lambda s: s.forecast.revision_number) + + class Params: + already_transformed = factory.Trait( + transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") + ) + +class StagingTapplicanttypesSynopsisFactory(BaseFactory): + class Meta: + model = staging.synopsis.TapplicanttypesSynopsis + + at_syn_id = factory.Sequence(lambda n: n) + + at_id = factory.Iterator(APPLICANT_TYPE_IDS) + + synopsis = factory.SubFactory(StagingTsynopsisFactory) + opportunity_id = factory.LazyAttribute(lambda s: s.synopsis.opportunity_id) + + # Default to being a new insert/update + is_deleted = False + transformed_at = None + + created_date = factory.Faker("date_time_between", start_date="-10y", end_date="-5y") + last_upd_date = sometimes_none( + factory.Faker("date_time_between", start_date="-5y", end_date="now") + ) + + class Params: + already_transformed = factory.Trait( + transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") + ) + +class StagingTapplicanttypesSynopsisHistFactory(StagingTapplicanttypesSynopsisFactory): + class Meta: + model = staging.synopsis.TapplicanttypesSynopsisHist + + synopsis = factory.SubFactory(StagingTsynopsisHistFactory) + opportunity_id = factory.LazyAttribute(lambda s: s.synopsis.opportunity_id) + revision_number = factory.LazyAttribute(lambda s: s.synopsis.revision_number) + + class Params: + already_transformed = factory.Trait( + transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") + ) + + +class StagingTfundactcatForecastFactory(BaseFactory): + class Meta: + model = staging.forecast.TfundactcatForecast + + fac_frcst_id = factory.Sequence(lambda n: n) + + fac_id = factory.Iterator(FUNDING_CATEGORY_IDS) + + forecast = factory.SubFactory(StagingTforecastFactory) + opportunity_id = factory.LazyAttribute(lambda s: s.forecast.opportunity_id) + + # Default to being a new insert/update + is_deleted = False + transformed_at = None + + created_date = factory.Faker("date_time_between", start_date="-10y", end_date="-5y") + last_upd_date = sometimes_none( + factory.Faker("date_time_between", start_date="-5y", end_date="now") + ) + + class Params: + already_transformed = factory.Trait( + transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") + ) + +class StagingTfundactcatForecastHistFactory(StagingTfundactcatForecastFactory): + class Meta: + model = staging.forecast.TfundactcatForecastHist + + forecast = factory.SubFactory(StagingTforecastHistFactory) + opportunity_id = factory.LazyAttribute(lambda s: s.forecast.opportunity_id) + revision_number = factory.LazyAttribute(lambda s: s.forecast.revision_number) + + class Params: + already_transformed = factory.Trait( + transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") + ) + +class StagingTfundactcatSynopsisFactory(BaseFactory): + class Meta: + model = staging.synopsis.TfundactcatSynopsis + + fac_syn_id = factory.Sequence(lambda n: n) + + fac_id = factory.Iterator(FUNDING_CATEGORY_IDS) + + synopsis = factory.SubFactory(StagingTsynopsisFactory) + opportunity_id = factory.LazyAttribute(lambda s: s.synopsis.opportunity_id) + + # Default to being a new insert/update + is_deleted = False + transformed_at = None + + created_date = factory.Faker("date_time_between", start_date="-10y", end_date="-5y") + last_upd_date = sometimes_none( + factory.Faker("date_time_between", start_date="-5y", end_date="now") + ) + + class Params: + already_transformed = factory.Trait( + transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") + ) + +class StagingTfundactcatSynopsisHistFactory(StagingTfundactcatSynopsisFactory): + class Meta: + model = staging.synopsis.TfundactcatSynopsisHist + + synopsis = factory.SubFactory(StagingTsynopsisHistFactory) + opportunity_id = factory.LazyAttribute(lambda s: s.synopsis.opportunity_id) + revision_number = factory.LazyAttribute(lambda s: s.synopsis.revision_number) + + class Params: + already_transformed = factory.Trait( + transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") + ) + +class StagingTfundinstrForecastFactory(BaseFactory): + class Meta: + model = staging.forecast.TfundinstrForecast + + fi_frcst_id = factory.Sequence(lambda n: n) + + fi_id = factory.Iterator(FUNDING_INSTRUMENT_IDS) + + forecast = factory.SubFactory(StagingTforecastFactory) + opportunity_id = factory.LazyAttribute(lambda s: s.forecast.opportunity_id) + + # Default to being a new insert/update + is_deleted = False + transformed_at = None + + created_date = factory.Faker("date_time_between", start_date="-10y", end_date="-5y") + last_upd_date = sometimes_none( + factory.Faker("date_time_between", start_date="-5y", end_date="now") + ) + + class Params: + already_transformed = factory.Trait( + transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") + ) + +class StagingTfundinstrForecastHistFactory(StagingTfundinstrForecastFactory): + class Meta: + model = staging.forecast.TfundinstrForecastHist + + forecast = factory.SubFactory(StagingTforecastHistFactory) + opportunity_id = factory.LazyAttribute(lambda s: s.forecast.opportunity_id) + revision_number = factory.LazyAttribute(lambda s: s.forecast.revision_number) + + class Params: + already_transformed = factory.Trait( + transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") + ) + +class StagingTfundinstrSynopsisFactory(BaseFactory): + class Meta: + model = staging.synopsis.TfundinstrSynopsis + + fi_syn_id = factory.Sequence(lambda n: n) + + fi_id = factory.Iterator(FUNDING_INSTRUMENT_IDS) + + synopsis = factory.SubFactory(StagingTsynopsisFactory) + opportunity_id = factory.LazyAttribute(lambda s: s.synopsis.opportunity_id) + + # Default to being a new insert/update + is_deleted = False + transformed_at = None + + created_date = factory.Faker("date_time_between", start_date="-10y", end_date="-5y") + last_upd_date = sometimes_none( + factory.Faker("date_time_between", start_date="-5y", end_date="now") + ) + + class Params: + already_transformed = factory.Trait( + transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") + ) + +class StagingTfundinstrSynopsisHistFactory(StagingTfundinstrSynopsisFactory): + class Meta: + model = staging.synopsis.TfundinstrSynopsisHist + + synopsis = factory.SubFactory(StagingTsynopsisHistFactory) + opportunity_id = factory.LazyAttribute(lambda s: s.synopsis.opportunity_id) + revision_number = factory.LazyAttribute(lambda s: s.synopsis.revision_number) + + class Params: + already_transformed = factory.Trait( + transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") + ) #################################### # Transfer Table Factories From 14e09ac6ffd79639d11a2441d53075d4657abd8e Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Tue, 7 May 2024 12:58:54 -0400 Subject: [PATCH 24/39] WIP --- .../transformation/transform_util.py | 60 ++-- api/src/db/models/staging/forecast.py | 6 + api/src/db/models/staging/synopsis.py | 6 + .../test_transform_oracle_data_task.py | 296 +++++++++++++++++- api/tests/src/db/models/factories.py | 18 ++ 5 files changed, 353 insertions(+), 33 deletions(-) diff --git a/api/src/data_migration/transformation/transform_util.py b/api/src/data_migration/transformation/transform_util.py index b3d7ffea4..9b21321cc 100644 --- a/api/src/data_migration/transformation/transform_util.py +++ b/api/src/data_migration/transformation/transform_util.py @@ -287,55 +287,71 @@ def convert_opportunity_summary_applicant_type(source_applicant_type: SourceAppl target_applicant_type = LinkOpportunitySummaryApplicantType( opportunity_summary_id=opportunity_summary.opportunity_summary_id, legacy_applicant_type_id=legacy_applicant_type_id, - applicant_type=applicant_type + applicant_type=applicant_type, + updated_by=source_applicant_type.last_upd_id, + created_by=source_applicant_type.creator_id ) - - target_applicant_type.updated_by = source_applicant_type.last_upd_id - target_applicant_type.created_by = source_applicant_type.creator_id + transform_update_create_timestamp(source_applicant_type, target_applicant_type, log_extra=log_extra) return target_applicant_type def convert_opportunity_summary_funding_instrument(source_funding_instrument: SourceFundingInstrument, existing_funding_instrument: LinkOpportunitySummaryFundingInstrument | None, opportunity_summary: OpportunitySummary) -> LinkOpportunitySummaryFundingInstrument: log_extra = get_log_extra_funding_instrument(source_funding_instrument) + # NOTE: The columns we're working with here are mostly the primary keys + # While we do support updates, that's really only going to affect + # the last update user + timestamps. From checking the prod data + # there are basically zero updates to this data (~5 occurred 10+ years ago) if existing_funding_instrument is None: logger.info("Creating new funding instrument record", extra=log_extra) + funding_instrument = transform_funding_instrument(source_funding_instrument.fi_id) + + # The legacy ID is named differently in the forecast/synopsis tables + if isinstance(source_funding_instrument, (TfundinstrForecast, TfundinstrForecastHist)): + legacy_funding_instrument_id = source_funding_instrument.fi_syn_id + else: + legacy_funding_instrument_id = source_funding_instrument.fi_frcst_id + target_funding_instrument = LinkOpportunitySummaryFundingInstrument( opportunity_summary_id=opportunity_summary.opportunity_summary_id, + legacy_funding_instrument_id=legacy_funding_instrument_id, + funding_instrument=funding_instrument, + updated_by=source_funding_instrument.last_upd_id, + created_by=source_funding_instrument.creator_id ) - target_funding_instrument.funding_instrument = transform_funding_instrument(source_funding_instrument.fi_id) - target_funding_instrument.updated_by = source_funding_instrument.last_upd_id - target_funding_instrument.created_by = source_funding_instrument.creator_id - - # The legacy ID is named differently in the forecast/synopsis tables - if isinstance(target_funding_instrument, (TfundinstrForecast, TfundinstrForecastHist)): - target_funding_instrument.legacy_funding_instrument_id = source_funding_instrument.fi_syn_id - else: - target_funding_instrument.legacy_funding_instrument_id = source_funding_instrument.fi_frcst_id + transform_update_create_timestamp(source_funding_instrument, target_funding_instrument, log_extra=log_extra) return target_funding_instrument def convert_opportunity_summary_funding_category(source_funding_category: SourceFundingCategory, existing_funding_category: LinkOpportunitySummaryFundingCategory | None, opportunity_summary: OpportunitySummary) -> LinkOpportunitySummaryFundingCategory: log_extra = get_log_extra_funding_category(source_funding_category) + # NOTE: The columns we're working with here are mostly the primary keys + # While we do support updates, that's really only going to affect + # the last update user + timestamps. From checking the prod data + # there are basically zero updates to this data (~5 occurred 10+ years ago) if existing_funding_category is None: logger.info("Creating new funding category record", extra=log_extra) + funding_category = transform_funding_category(source_funding_category.fac_id) + + # The legacy ID is named differently in the forecast/synopsis tables + if isinstance(source_funding_category, (TfundactcatForecast, TfundactcatForecastHist)): + legacy_funding_category_id = source_funding_category.fac_frcst_id + else: + legacy_funding_category_id = source_funding_category.fac_syn_id + target_funding_category = LinkOpportunitySummaryFundingCategory( opportunity_summary_id=opportunity_summary.opportunity_summary_id, + legacy_funding_category_id=legacy_funding_category_id, + funding_category=funding_category, + updated_by=source_funding_category.last_upd_id, + created_by=source_funding_category.creator_id ) - target_funding_category.funding_category = transform_funding_category(source_funding_category.fac_id) - target_funding_category.updated_by = source_funding_category.last_upd_id - target_funding_category.created_by = source_funding_category.creator_id - - # The legacy ID is named differently in the forecast/synopsis tables - if isinstance(target_funding_category, (TfundactcatForecast, TfundactcatForecastHist)): - target_funding_category.legacy_funding_category_id = source_funding_category.fac_frcst_id - else: - target_funding_category.legacy_funding_category_id = source_funding_category.fac_syn_id + transform_update_create_timestamp(source_funding_category, target_funding_category, log_extra=log_extra) return target_funding_category diff --git a/api/src/db/models/staging/forecast.py b/api/src/db/models/staging/forecast.py index ab8111360..1a028aa2e 100644 --- a/api/src/db/models/staging/forecast.py +++ b/api/src/db/models/staging/forecast.py @@ -46,6 +46,7 @@ class TapplicanttypesForecast( Tforecast, primaryjoin="TapplicanttypesForecast.opportunity_id == foreign(Tforecast.opportunity_id)", uselist=False, + overlaps="forecast" ) class TapplicanttypesForecastHist( @@ -57,6 +58,7 @@ class TapplicanttypesForecastHist( TforecastHist, primaryjoin="and_(TapplicanttypesForecastHist.opportunity_id == foreign(TforecastHist.opportunity_id), TapplicanttypesForecastHist.revision_number == foreign(TforecastHist.revision_number))", uselist=False, + overlaps="forecast" ) class TfundactcatForecast(StagingBase, forecast_mixin.TfundactcatForecastMixin, StagingParamMixin): @@ -66,6 +68,7 @@ class TfundactcatForecast(StagingBase, forecast_mixin.TfundactcatForecastMixin, Tforecast, primaryjoin="TfundactcatForecast.opportunity_id == foreign(Tforecast.opportunity_id)", uselist=False, + overlaps="forecast" ) class TfundactcatForecastHist( @@ -77,6 +80,7 @@ class TfundactcatForecastHist( TforecastHist, primaryjoin="and_(TfundactcatForecastHist.opportunity_id == foreign(TforecastHist.opportunity_id), TfundactcatForecastHist.revision_number == foreign(TforecastHist.revision_number))", uselist=False, + overlaps="forecast" ) class TfundinstrForecast(StagingBase, forecast_mixin.TfundinstrForecastMixin, StagingParamMixin): @@ -86,6 +90,7 @@ class TfundinstrForecast(StagingBase, forecast_mixin.TfundinstrForecastMixin, St Tforecast, primaryjoin="TfundinstrForecast.opportunity_id == foreign(Tforecast.opportunity_id)", uselist=False, + overlaps="forecast" ) class TfundinstrForecastHist( @@ -97,4 +102,5 @@ class TfundinstrForecastHist( TforecastHist, primaryjoin="and_(TfundinstrForecastHist.opportunity_id == foreign(TforecastHist.opportunity_id), TfundinstrForecastHist.revision_number == foreign(TforecastHist.revision_number))", uselist=False, + overlaps="forecast" ) diff --git a/api/src/db/models/staging/synopsis.py b/api/src/db/models/staging/synopsis.py index b570ccff2..ba824ab36 100644 --- a/api/src/db/models/staging/synopsis.py +++ b/api/src/db/models/staging/synopsis.py @@ -45,6 +45,7 @@ class TapplicanttypesSynopsis( Tsynopsis, primaryjoin="TapplicanttypesSynopsis.opportunity_id == foreign(Tsynopsis.opportunity_id)", uselist=False, + overlaps="synopsis" ) class TapplicanttypesSynopsisHist( @@ -56,6 +57,7 @@ class TapplicanttypesSynopsisHist( TsynopsisHist, primaryjoin="and_(TapplicanttypesSynopsisHist.opportunity_id == foreign(TsynopsisHist.opportunity_id), TapplicanttypesSynopsisHist.revision_number == foreign(TsynopsisHist.revision_number))", uselist=False, + overlaps="synopsis" ) class TfundactcatSynopsis(StagingBase, synopsis_mixin.TfundactcatSynopsisMixin, StagingParamMixin): @@ -65,6 +67,7 @@ class TfundactcatSynopsis(StagingBase, synopsis_mixin.TfundactcatSynopsisMixin, Tsynopsis, primaryjoin="TfundactcatSynopsis.opportunity_id == foreign(Tsynopsis.opportunity_id)", uselist=False, + overlaps="synopsis" ) class TfundactcatSynopsisHist( @@ -76,6 +79,7 @@ class TfundactcatSynopsisHist( TsynopsisHist, primaryjoin="and_(TfundactcatSynopsisHist.opportunity_id == foreign(TsynopsisHist.opportunity_id), TfundactcatSynopsisHist.revision_number == foreign(TsynopsisHist.revision_number))", uselist=False, + overlaps="synopsis" ) class TfundinstrSynopsis(StagingBase, synopsis_mixin.TfundinstrSynopsisMixin, StagingParamMixin): @@ -85,6 +89,7 @@ class TfundinstrSynopsis(StagingBase, synopsis_mixin.TfundinstrSynopsisMixin, St Tsynopsis, primaryjoin="TfundinstrSynopsis.opportunity_id == foreign(Tsynopsis.opportunity_id)", uselist=False, + overlaps="synopsis" ) class TfundinstrSynopsisHist( @@ -96,4 +101,5 @@ class TfundinstrSynopsisHist( TsynopsisHist, primaryjoin="and_(TfundinstrSynopsisHist.opportunity_id == foreign(TsynopsisHist.opportunity_id), TfundinstrSynopsisHist.revision_number == foreign(TsynopsisHist.revision_number))", uselist=False, + overlaps="synopsis" ) \ No newline at end of file diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py index cd6db78f8..59252e8dd 100644 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -2,16 +2,17 @@ import pytest -from src.constants.lookup_constants import ApplicantType +from src.constants.lookup_constants import ApplicantType, FundingInstrument, FundingCategory from src.data_migration.transformation.transform_oracle_data_task import TransformOracleDataTask from src.db.models.opportunity_models import ( Opportunity, OpportunityAssistanceListing, - OpportunitySummary, LinkOpportunitySummaryApplicantType, + OpportunitySummary, LinkOpportunitySummaryApplicantType, LinkOpportunitySummaryFundingInstrument, LinkOpportunitySummaryFundingCategory, ) -from src.db.models.staging.forecast import TforecastHist, TapplicanttypesForecastHist +from src.db.models.staging.forecast import TforecastHist, TapplicanttypesForecastHist, TfundinstrForecastHist, TfundactcatForecastHist from src.db.models.staging.opportunity import Topportunity, TopportunityCfda -from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist, TapplicanttypesSynopsis, TapplicanttypesSynopsisHist +from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist, TapplicanttypesSynopsis, TapplicanttypesSynopsisHist, TfundinstrSynopsis, TfundinstrSynopsisHist, TfundactcatSynopsis, \ + TfundactcatSynopsisHist from tests.conftest import BaseTestClass from tests.src.db.models.factories import ( OpportunityAssistanceListingFactory, @@ -23,7 +24,9 @@ StagingTopportunityFactory, StagingTsynopsisFactory, StagingTsynopsisHistFactory, StagingTapplicanttypesForecastFactory, StagingTapplicanttypesForecastHistFactory, LinkOpportunitySummaryApplicantTypeFactory, StagingTapplicanttypesSynopsisFactory, - StagingTapplicanttypesSynopsisHistFactory, + StagingTapplicanttypesSynopsisHistFactory, StagingTfundinstrForecastFactory, StagingTfundinstrForecastHistFactory, StagingTfundinstrSynopsisFactory, StagingTfundinstrSynopsisHistFactory, + LinkOpportunitySummaryFundingInstrumentFactory, StagingTfundactcatForecastFactory, StagingTfundactcatForecastHistFactory, StagingTfundactcatSynopsisFactory, StagingTfundactcatSynopsisHistFactory, + LinkOpportunitySummaryFundingCategoryFactory, ) from sqlalchemy import and_ @@ -186,6 +189,108 @@ def setup_applicant_type( return source_applicant_type +def setup_funding_instrument( + create_existing: bool, + opportunity_summary: OpportunitySummary, + legacy_lookup_value: str, + funding_instrument: FundingInstrument | None = None, + is_delete: bool = False, + is_already_processed: bool = False, + source_values: dict | None = None, +): + if create_existing and is_delete is False and funding_instrument is None: + raise Exception("If create_existing is True, is_delete is False - must provide the properly converted / mapped value for funding_instrument") + + if opportunity_summary.is_forecast: + source_values["forecast"] = None + if opportunity_summary.revision_number is None: + factory_cls = StagingTfundinstrForecastFactory + else: + factory_cls = StagingTfundinstrForecastHistFactory + source_values["revision_number"] = opportunity_summary.revision_number + else: + source_values["synopsis"] = None + if opportunity_summary.revision_number is None: + factory_cls = StagingTfundinstrSynopsisFactory + else: + factory_cls = StagingTfundinstrSynopsisHistFactory + source_values["revision_number"] = opportunity_summary.revision_number + + source_funding_instrument = factory_cls.create( + **source_values, + opportunity_id=opportunity_summary.opportunity_id, + is_deleted=is_delete, + already_transformed=is_already_processed, + fi_id=legacy_lookup_value, + ) + + if create_existing: + if opportunity_summary.is_forecast: + legacy_id = source_funding_instrument.fi_frcst_id + else: + legacy_id = source_funding_instrument.fi_syn_id + + LinkOpportunitySummaryFundingInstrumentFactory.create( + opportunity_summary=opportunity_summary, + legacy_funding_instrument_id=legacy_id, + funding_instrument=funding_instrument + ) + + return source_funding_instrument + + +def setup_funding_category( + create_existing: bool, + opportunity_summary: OpportunitySummary, + legacy_lookup_value: str, + funding_category: FundingCategory | None = None, + is_delete: bool = False, + is_already_processed: bool = False, + source_values: dict | None = None, +): + if create_existing and is_delete is False and funding_category is None: + raise Exception("If create_existing is True, is_delete is False - must provide the properly converted / mapped value for funding_category") + + if source_values is None: + source_values = {} + + if opportunity_summary.is_forecast: + source_values["forecast"] = None + if opportunity_summary.revision_number is None: + factory_cls = StagingTfundactcatForecastFactory + else: + factory_cls = StagingTfundactcatForecastHistFactory + source_values["revision_number"] = opportunity_summary.revision_number + else: + source_values["synopsis"] = None + if opportunity_summary.revision_number is None: + factory_cls = StagingTfundactcatSynopsisFactory + else: + factory_cls = StagingTfundactcatSynopsisHistFactory + source_values["revision_number"] = opportunity_summary.revision_number + + source_funding_category = factory_cls.create( + **source_values, + opportunity_id=opportunity_summary.opportunity_id, + is_deleted=is_delete, + already_transformed=is_already_processed, + fac_id=legacy_lookup_value, + ) + + if create_existing: + if opportunity_summary.is_forecast: + legacy_id = source_funding_category.fac_frcst_id + else: + legacy_id = source_funding_category.fac_syn_id + + LinkOpportunitySummaryFundingCategoryFactory.create( + opportunity_summary=opportunity_summary, + legacy_funding_category_id=legacy_id, + funding_category=funding_category + ) + + return funding_category + def validate_matching_fields( source, destination, fields: list[Tuple[str, str]], expect_all_to_match: bool ): @@ -368,7 +473,9 @@ def validate_opportunity_summary( assert opportunity_summary.is_deleted == is_deleted -def validate_applicant_type(db_session, source_applicant_type, expect_in_db: bool = True, expected_applicant_type: ApplicantType | None = None): +def validate_applicant_type(db_session, source_applicant_type, expect_in_db: bool = True, expected_applicant_type: ApplicantType | None = None, was_processed: bool = True, expect_values_to_match: bool = True): + assert (source_applicant_type.transformed_at is not None) == was_processed + if isinstance(source_applicant_type, (TapplicanttypesSynopsis, TapplicanttypesSynopsisHist)): is_forecast = False legacy_id = source_applicant_type.at_syn_id @@ -402,6 +509,84 @@ def validate_applicant_type(db_session, source_applicant_type, expect_in_db: boo assert link_applicant_type is not None assert link_applicant_type.applicant_type == expected_applicant_type + validate_matching_fields(source_applicant_type, link_applicant_type, [("creator_id", "created_by"), ("last_upd_id", "updated_by")], expect_values_to_match) + +def validate_funding_instrument(db_session, source_funding_instrument, expect_in_db: bool = True, expected_funding_instrument: FundingInstrument | None = None, was_processed: bool = True, expect_values_to_match: bool = True): + assert (source_funding_instrument.transformed_at is not None) == was_processed + + if isinstance(source_funding_instrument, (TfundinstrSynopsis, TfundinstrSynopsisHist)): + is_forecast = False + legacy_id = source_funding_instrument.fi_syn_id + else: + is_forecast = True + legacy_id = source_funding_instrument.fi_frcst_id + + revision_number = None + if isinstance(source_funding_instrument, (TfundinstrSynopsisHist, TfundinstrForecastHist)): + revision_number = source_funding_instrument.revision_number + + # In order to properly find the link table value, need to first determine + # the opportunity summary in a subquery + opportunity_summary_id = db_session.query(OpportunitySummary.opportunity_summary_id).filter( + OpportunitySummary.revision_number == revision_number, OpportunitySummary.is_forecast == is_forecast, OpportunitySummary.opportunity_id == source_funding_instrument.opportunity_id + ).scalar() + + link_funding_instrument = ( + db_session.query(LinkOpportunitySummaryFundingInstrument) + .filter( + LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id == legacy_id, + LinkOpportunitySummaryFundingInstrument.opportunity_summary_id == opportunity_summary_id + ) + .one_or_none() + ) + + if not expect_in_db: + assert link_funding_instrument is None + return + + assert link_funding_instrument is not None + assert link_funding_instrument.funding_instrument == expected_funding_instrument + + validate_matching_fields(source_funding_instrument, link_funding_instrument, [("creator_id", "created_by"), ("last_upd_id", "updated_by")], expect_values_to_match) + +def validate_funding_category(db_session, source_funding_category, expect_in_db: bool = True, expected_funding_category: FundingCategory | None = None, was_processed: bool = True, expect_values_to_match: bool = True): + assert (source_funding_category.transformed_at is not None) == was_processed + + if isinstance(source_funding_category, (TfundactcatSynopsis, TfundactcatSynopsisHist)): + is_forecast = False + legacy_id = source_funding_category.fac_syn_id + else: + is_forecast = True + legacy_id = source_funding_category.fac_frcst_id + + revision_number = None + if isinstance(source_funding_category, (TfundactcatSynopsisHist, TfundactcatForecastHist)): + revision_number = source_funding_category.revision_number + + # In order to properly find the link table value, need to first determine + # the opportunity summary in a subquery + opportunity_summary_id = db_session.query(OpportunitySummary.opportunity_summary_id).filter( + OpportunitySummary.revision_number == revision_number, OpportunitySummary.is_forecast == is_forecast, OpportunitySummary.opportunity_id == source_funding_category.opportunity_id + ).scalar() + + link_funding_category = ( + db_session.query(LinkOpportunitySummaryFundingCategory) + .filter( + LinkOpportunitySummaryFundingCategory.legacy_funding_category_id == legacy_id, + LinkOpportunitySummaryFundingCategory.opportunity_summary_id == opportunity_summary_id + ) + .one_or_none() + ) + + if not expect_in_db: + assert link_funding_category is None + return + + assert link_funding_category is not None + assert link_funding_category.funding_category == expected_funding_category + + validate_matching_fields(source_funding_category, link_funding_category, [("creator_id", "created_by"), ("last_upd_id", "updated_by")], expect_values_to_match) + class TestTransformOpportunity(BaseTestClass): @pytest.fixture() @@ -887,13 +1072,12 @@ def test_process_applicant_types(self, db_session, transform_oracle_data_task): forecast_delete2 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="05", applicant_type=ApplicantType.INDEPENDENT_SCHOOL_DISTRICTS) forecast_update_already_processed = setup_applicant_type(create_existing=True, is_already_processed=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="06", applicant_type=ApplicantType.PUBLIC_AND_STATE_INSTITUTIONS_OF_HIGHER_EDUCATION) - opportunity_summary_forecast_hist = OpportunitySummaryFactory.create(is_forecast=True, revision_number=3, no_link_values=True) forecast_hist_insert1 = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="07") forecast_hist_update1 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="08", applicant_type=ApplicantType.PUBLIC_AND_INDIAN_HOUSING_AUTHORITIES) forecast_hist_update2 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="11", applicant_type=ApplicantType.OTHER_NATIVE_AMERICAN_TRIBAL_ORGANIZATIONS) forecast_hist_delete1 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="12", applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITH_501C3) - forecast_hist_already_processed = setup_applicant_type(create_existing=False, is_delete=True, is_already_processed=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="13", applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITHOUT_501C3) + forecast_hist_delete_already_processed = setup_applicant_type(create_existing=False, is_delete=True, is_already_processed=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="13") opportunity_summary_syn = OpportunitySummaryFactory.create(is_forecast=False, revision_number=None, no_link_values=True) syn_insert1 = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="20") @@ -902,7 +1086,7 @@ def test_process_applicant_types(self, db_session, transform_oracle_data_task): syn_update2 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="23", applicant_type=ApplicantType.SMALL_BUSINESSES) syn_delete1 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="25", applicant_type=ApplicantType.OTHER) syn_delete2 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="99", applicant_type=ApplicantType.UNRESTRICTED) - syn_delete_but_current_missing = setup_applicant_type(create_existing=False, is_delete=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="07", applicant_type=ApplicantType.FEDERALLY_RECOGNIZED_NATIVE_AMERICAN_TRIBAL_GOVERNMENTS) + syn_delete_but_current_missing = setup_applicant_type(create_existing=False, is_delete=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="07") syn_update_already_processed = setup_applicant_type(create_existing=True, is_already_processed=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="08", applicant_type=ApplicantType.PUBLIC_AND_INDIAN_HOUSING_AUTHORITIES) opportunity_summary_syn_hist = OpportunitySummaryFactory.create(is_forecast=False, revision_number=21, no_link_values=True) @@ -911,11 +1095,9 @@ def test_process_applicant_types(self, db_session, transform_oracle_data_task): syn_hist_update2 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="13", applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITHOUT_501C3) syn_hist_delete1 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="25", applicant_type=ApplicantType.OTHER) syn_hist_delete2 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="99", applicant_type=ApplicantType.UNRESTRICTED) - # TODO - note this one syn_hist_insert_invalid_type = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="X", applicant_type=ApplicantType.STATE_GOVERNMENTS) transform_oracle_data_task.process_link_applicant_types() - print(transform_oracle_data_task.metrics) validate_applicant_type(db_session, forecast_insert1, expected_applicant_type=ApplicantType.STATE_GOVERNMENTS) validate_applicant_type(db_session, forecast_hist_insert1, expected_applicant_type=ApplicantType.FEDERALLY_RECOGNIZED_NATIVE_AMERICAN_TRIBAL_GOVERNMENTS) @@ -925,4 +1107,96 @@ def test_process_applicant_types(self, db_session, transform_oracle_data_task): validate_applicant_type(db_session, forecast_update1, expected_applicant_type=ApplicantType.COUNTY_GOVERNMENTS) validate_applicant_type(db_session, forecast_update2, expected_applicant_type=ApplicantType.CITY_OR_TOWNSHIP_GOVERNMENTS) + validate_applicant_type(db_session, forecast_hist_update1, expected_applicant_type=ApplicantType.PUBLIC_AND_INDIAN_HOUSING_AUTHORITIES) + validate_applicant_type(db_session, forecast_hist_update2, expected_applicant_type=ApplicantType.OTHER_NATIVE_AMERICAN_TRIBAL_ORGANIZATIONS) + validate_applicant_type(db_session, syn_update1, expected_applicant_type=ApplicantType.FOR_PROFIT_ORGANIZATIONS_OTHER_THAN_SMALL_BUSINESSES) + validate_applicant_type(db_session, syn_update2, expected_applicant_type=ApplicantType.SMALL_BUSINESSES) + validate_applicant_type(db_session, syn_hist_update1, expected_applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITH_501C3) + validate_applicant_type(db_session, syn_hist_update2, expected_applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITHOUT_501C3) + + validate_applicant_type(db_session, forecast_delete1, expect_in_db=False) + validate_applicant_type(db_session, forecast_delete2, expect_in_db=False) + validate_applicant_type(db_session, forecast_hist_delete1, expect_in_db=False) + validate_applicant_type(db_session, syn_delete1, expect_in_db=False) + validate_applicant_type(db_session, syn_delete2, expect_in_db=False) + validate_applicant_type(db_session, syn_hist_delete1, expect_in_db=False) + validate_applicant_type(db_session, syn_hist_delete2, expect_in_db=False) + + validate_applicant_type(db_session, forecast_update_already_processed, expected_applicant_type=ApplicantType.PUBLIC_AND_STATE_INSTITUTIONS_OF_HIGHER_EDUCATION, expect_values_to_match=False) + validate_applicant_type(db_session, forecast_hist_delete_already_processed, expect_in_db=False) + validate_applicant_type(db_session, syn_update_already_processed, expected_applicant_type=ApplicantType.PUBLIC_AND_INDIAN_HOUSING_AUTHORITIES, expect_values_to_match=False) + + validate_applicant_type(db_session, syn_delete_but_current_missing, expect_in_db=False, was_processed=False) + validate_applicant_type(db_session, syn_hist_insert_invalid_type, expect_in_db=False, was_processed=False) + + metrics = transform_oracle_data_task.metrics + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED] == 22 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_DELETED] == 7 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_INSERTED] == 5 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_UPDATED] == 8 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_ERROR_COUNT] == 2 + + # Rerunning will only attempt to re-process the errors, so total+errors goes up by 2 + transform_oracle_data_task.process_link_applicant_types() + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED] == 24 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_DELETED] == 7 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_INSERTED] == 5 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_UPDATED] == 8 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_ERROR_COUNT] == 4 + + @pytest.mark.parametrize( + "is_forecast,revision_number", [(True, None), (False, None), (True, 5), (False, 10)] + ) + def test_process_applicant_types_but_current_missing(self, db_session, transform_oracle_data_task, is_forecast, revision_number): + opportunity_summary = OpportunitySummaryFactory.create(is_forecast=is_forecast, revision_number=revision_number, no_link_values=True) + delete_but_current_missing = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary, legacy_lookup_value="00", is_delete=True) + + with pytest.raises(ValueError, match="Cannot delete applicant type as it does not exist"): + transform_oracle_data_task.process_link_applicant_type(delete_but_current_missing, None, opportunity_summary) + + @pytest.mark.parametrize( + "is_forecast,revision_number,legacy_lookup_value", [(True, None, "90"), (False, None, "xx"), (True, 5, "50"), (False, 10, "1")] + ) + def test_process_applicant_types_but_invalid_lookup_value(self, db_session, transform_oracle_data_task, is_forecast, revision_number, legacy_lookup_value): + opportunity_summary = OpportunitySummaryFactory.create(is_forecast=is_forecast, revision_number=revision_number, no_link_values=True) + insert_but_invalid_value = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary, legacy_lookup_value=legacy_lookup_value) + + with pytest.raises(ValueError, match="Unrecognized applicant type"): + transform_oracle_data_task.process_link_applicant_type(insert_but_invalid_value, None, opportunity_summary) + + +class TestTransformFundingInstrument(BaseTestClass): + @pytest.fixture() + def transform_oracle_data_task( + self, db_session, enable_factory_create, truncate_opportunities + ) -> TransformOracleDataTask: + return TransformOracleDataTask(db_session) + + def test_process_funding_instruments(self, db_session, transform_oracle_data_task): + opportunity_summary_forecast = OpportunitySummaryFactory.create(is_forecast=True, revision_number=None, no_link_values=True) + forecast_insert1 = setup_funding_instrument(create_existing=False, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="CA") + forecast_update1 = setup_funding_instrument(create_existing=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="G", funding_instrument=FundingInstrument.GRANT) + forecast_delete1 = setup_funding_instrument(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="PC", funding_instrument=FundingInstrument.PROCUREMENT_CONTRACT) + forecast_update_already_processed = setup_funding_instrument(create_existing=True, is_already_processed=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="O", funding_instrument=FundingInstrument.OTHER) + + opportunity_summary_forecast_hist = OpportunitySummaryFactory.create(is_forecast=True, revision_number=3, no_link_values=True) + forecast_hist_insert1 = setup_funding_instrument(create_existing=False, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="G") + forecast_hist_delete1 = setup_funding_instrument(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="CA", funding_instrument=FundingInstrument.COOPERATIVE_AGREEMENT) + forecast_hist_delete_already_processed = setup_funding_instrument(create_existing=False, is_delete=True, is_already_processed=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="O") + syn_delete_but_current_missing = setup_funding_instrument(create_existing=False, is_delete=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="PC") + + + opportunity_summary_syn = OpportunitySummaryFactory.create(is_forecast=False, revision_number=None, no_link_values=True) + syn_insert1 = setup_funding_instrument(create_existing=False, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="20") + syn_insert2 = setup_funding_instrument(create_existing=False, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="21") + syn_delete1 = setup_funding_instrument(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="25", funding_instrument=FundingInstrument.GRANT) + syn_update_already_processed = setup_funding_instrument(create_existing=True, is_already_processed=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="08", funding_instrument=FundingInstrument.GRANT) + + opportunity_summary_syn_hist = OpportunitySummaryFactory.create(is_forecast=False, revision_number=21, no_link_values=True) + syn_hist_insert1 = setup_funding_instrument(create_existing=False, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="11") + syn_hist_update1 = setup_funding_instrument(create_existing=True, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="12", funding_instrument=FundingInstrument.GRANT) + syn_hist_delete1 = setup_funding_instrument(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="25", funding_instrument=FundingInstrument.GRANT) + syn_hist_delete2 = setup_funding_instrument(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="99", funding_instrument=FundingInstrument.GRANT) + syn_hist_insert_invalid_type = setup_funding_instrument(create_existing=False, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="X", funding_instrument=FundingInstrument.GRANT) + transform_oracle_data_task.process_link_funding_instruments() diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index 92fc2034a..62028157c 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -909,6 +909,9 @@ class Meta: factory.Faker("date_time_between", start_date="-5y", end_date="now") ) + last_upd_id = factory.Faker("first_name") + creator_id = factory.Faker("first_name") + class Params: already_transformed = factory.Trait( transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") @@ -947,6 +950,9 @@ class Meta: factory.Faker("date_time_between", start_date="-5y", end_date="now") ) + creator_id = factory.Faker("first_name") + last_upd_id = factory.Faker("first_name") + class Params: already_transformed = factory.Trait( transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") @@ -986,6 +992,9 @@ class Meta: factory.Faker("date_time_between", start_date="-5y", end_date="now") ) + creator_id = factory.Faker("first_name") + last_upd_id = factory.Faker("first_name") + class Params: already_transformed = factory.Trait( transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") @@ -1024,6 +1033,9 @@ class Meta: factory.Faker("date_time_between", start_date="-5y", end_date="now") ) + creator_id = factory.Faker("first_name") + last_upd_id = factory.Faker("first_name") + class Params: already_transformed = factory.Trait( transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") @@ -1062,6 +1074,9 @@ class Meta: factory.Faker("date_time_between", start_date="-5y", end_date="now") ) + creator_id = factory.Faker("first_name") + last_upd_id = factory.Faker("first_name") + class Params: already_transformed = factory.Trait( transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") @@ -1100,6 +1115,9 @@ class Meta: factory.Faker("date_time_between", start_date="-5y", end_date="now") ) + creator_id = factory.Faker("first_name") + last_upd_id = factory.Faker("first_name") + class Params: already_transformed = factory.Trait( transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") From ee5ab9967905cf49bf6b0e35457fe276fa24bbd3 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Tue, 7 May 2024 14:42:17 -0400 Subject: [PATCH 25/39] Cleanup and final tests --- .../data_migration/transformation/__init__.py | 38 +- .../transform_oracle_data_task.py | 356 +++-- .../transformation/transform_util.py | 74 +- ...dd_unique_constraint_link_table_legacy_.py | 61 + api/src/db/models/opportunity_models.py | 21 +- api/src/db/models/staging/forecast.py | 18 +- api/src/db/models/staging/synopsis.py | 19 +- .../test_transform_oracle_data_task.py | 1197 ++++++++++++++--- api/tests/src/db/models/factories.py | 60 +- 9 files changed, 1535 insertions(+), 309 deletions(-) create mode 100644 api/src/db/migrations/versions/2024_05_07_add_unique_constraint_link_table_legacy_.py diff --git a/api/src/data_migration/transformation/__init__.py b/api/src/data_migration/transformation/__init__.py index 2e288a4b9..c3c7751b1 100644 --- a/api/src/data_migration/transformation/__init__.py +++ b/api/src/data_migration/transformation/__init__.py @@ -1,11 +1,39 @@ from typing import TypeAlias -from src.db.models.staging.forecast import Tforecast, TforecastHist, TapplicanttypesForecast, TapplicanttypesForecastHist, TfundinstrForecastHist, TfundinstrForecast, TfundactcatForecastHist, TfundactcatForecast -from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist, TapplicanttypesSynopsis, TapplicanttypesSynopsisHist, TfundinstrSynopsisHist, TfundinstrSynopsis, TfundactcatSynopsisHist, TfundactcatSynopsis + +from src.db.models.staging.forecast import ( + TapplicanttypesForecast, + TapplicanttypesForecastHist, + Tforecast, + TforecastHist, + TfundactcatForecast, + TfundactcatForecastHist, + TfundinstrForecast, + TfundinstrForecastHist, +) +from src.db.models.staging.synopsis import ( + TapplicanttypesSynopsis, + TapplicanttypesSynopsisHist, + TfundactcatSynopsis, + TfundactcatSynopsisHist, + TfundinstrSynopsis, + TfundinstrSynopsisHist, + Tsynopsis, + TsynopsisHist, +) SourceSummary: TypeAlias = Tforecast | Tsynopsis | TforecastHist | TsynopsisHist -SourceApplicantType: TypeAlias = TapplicanttypesForecast | TapplicanttypesForecastHist | TapplicanttypesSynopsis | TapplicanttypesSynopsisHist +SourceApplicantType: TypeAlias = ( + TapplicanttypesForecast + | TapplicanttypesForecastHist + | TapplicanttypesSynopsis + | TapplicanttypesSynopsisHist +) -SourceFundingCategory: TypeAlias = TfundactcatForecast | TfundactcatForecastHist | TfundactcatSynopsis | TfundactcatSynopsisHist +SourceFundingCategory: TypeAlias = ( + TfundactcatForecast | TfundactcatForecastHist | TfundactcatSynopsis | TfundactcatSynopsisHist +) -SourceFundingInstrument: TypeAlias = TfundinstrForecastHist | TfundinstrForecast | TfundinstrSynopsisHist | TfundinstrSynopsis +SourceFundingInstrument: TypeAlias = ( + TfundinstrForecastHist | TfundinstrForecast | TfundinstrSynopsisHist | TfundinstrSynopsis +) diff --git a/api/src/data_migration/transformation/transform_oracle_data_task.py b/api/src/data_migration/transformation/transform_oracle_data_task.py index 045840dc2..4fb8b0a2a 100644 --- a/api/src/data_migration/transformation/transform_oracle_data_task.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -9,20 +9,39 @@ from src.data_migration.transformation import transform_util from src.db.models.base import ApiSchemaTable from src.db.models.opportunity_models import ( + LinkOpportunitySummaryApplicantType, + LinkOpportunitySummaryFundingCategory, + LinkOpportunitySummaryFundingInstrument, Opportunity, OpportunityAssistanceListing, - OpportunitySummary, LinkOpportunitySummaryApplicantType, LinkOpportunitySummaryFundingCategory, LinkOpportunitySummaryFundingInstrument, + OpportunitySummary, +) +from src.db.models.staging.forecast import ( + TapplicanttypesForecast, + TapplicanttypesForecastHist, + Tforecast, + TforecastHist, + TfundactcatForecast, + TfundactcatForecastHist, + TfundinstrForecast, + TfundinstrForecastHist, ) -from src.db.models.staging.forecast import Tforecast, TforecastHist, TapplicanttypesForecast, TapplicanttypesForecastHist, TfundactcatForecast, TfundactcatForecastHist, TfundinstrForecast, \ - TfundinstrForecastHist from src.db.models.staging.opportunity import Topportunity, TopportunityCfda from src.db.models.staging.staging_base import StagingParamMixin -from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist, TapplicanttypesSynopsis, TapplicanttypesSynopsisHist, TfundactcatSynopsis, TfundactcatSynopsisHist, TfundinstrSynopsis, \ - TfundinstrSynopsisHist +from src.db.models.staging.synopsis import ( + TapplicanttypesSynopsis, + TapplicanttypesSynopsisHist, + TfundactcatSynopsis, + TfundactcatSynopsisHist, + TfundinstrSynopsis, + TfundinstrSynopsisHist, + Tsynopsis, + TsynopsisHist, +) from src.task.task import Task from src.util import datetime_util -from . import SourceSummary, SourceApplicantType, SourceFundingInstrument, SourceFundingCategory +from . import SourceApplicantType, SourceFundingCategory, SourceFundingInstrument, SourceSummary S = TypeVar("S", bound=StagingParamMixin) D = TypeVar("D", bound=ApiSchemaTable) @@ -99,30 +118,37 @@ def fetch_with_opportunity( ), ) - def fetch_with_opportunity_summary(self, source_model: Type[S], destination_model: Type[D], join_clause: Sequence, is_forecast: bool, is_historical_table: bool) -> list[Tuple[S, D | None, OpportunitySummary | None]]: + def fetch_with_opportunity_summary( + self, + source_model: Type[S], + destination_model: Type[D], + join_clause: Sequence, + is_forecast: bool, + is_historical_table: bool, + ) -> list[Tuple[S, D | None, OpportunitySummary | None]]: # setup the join clause for getting the opportunity summary + opportunity_summary_join_clause = [ + source_model.opportunity_id == OpportunitySummary.opportunity_id, # type: ignore[attr-defined] + OpportunitySummary.is_forecast.is_(is_forecast), + ] + if is_historical_table: - opportunity_summary_join_clause = [ - source_model.opportunity_id == OpportunitySummary.opportunity_id, - OpportunitySummary.is_forecast.is_(is_forecast), - source_model.revision_number == OpportunitySummary.revision_number, - ] + opportunity_summary_join_clause.append( + source_model.revision_number == OpportunitySummary.revision_number # type: ignore[attr-defined] + ) else: - # TODO - can simplify this a bit - opportunity_summary_join_clause = [ - source_model.opportunity_id == OpportunitySummary.opportunity_id, - OpportunitySummary.is_forecast.is_(is_forecast), - OpportunitySummary.revision_number.is_(None), - ] + opportunity_summary_join_clause.append(OpportunitySummary.revision_number.is_(None)) - - return self.db_session.execute( - select(source_model, destination_model, OpportunitySummary) - .join(OpportunitySummary, and_(*opportunity_summary_join_clause), isouter=True) - .join(destination_model, and_(*join_clause), isouter=True) - .where(source_model.transformed_at.is_(None)) - .execution_options(yield_per=5000) + return cast( + list[Tuple[S, D | None, OpportunitySummary | None]], + self.db_session.execute( + select(source_model, destination_model, OpportunitySummary) + .join(OpportunitySummary, and_(*opportunity_summary_join_clause), isouter=True) + .join(destination_model, and_(*join_clause), isouter=True) + .where(source_model.transformed_at.is_(None)) + .execution_options(yield_per=5000) + ), ) def process_opportunities(self) -> None: @@ -376,34 +402,77 @@ def process_one_to_many_lookup_tables(self) -> None: self.process_link_funding_categories() def process_link_applicant_types(self) -> None: - forecast_applicant_type_records = self.fetch_with_opportunity_summary(TapplicanttypesForecast, LinkOpportunitySummaryApplicantType, [ - TapplicanttypesForecast.at_frcst_id == LinkOpportunitySummaryApplicantType.legacy_applicant_type_id, - OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryApplicantType.opportunity_summary_id - ], is_forecast=True, is_historical_table=False) + forecast_applicant_type_records = self.fetch_with_opportunity_summary( + TapplicanttypesForecast, + LinkOpportunitySummaryApplicantType, + [ + TapplicanttypesForecast.at_frcst_id + == LinkOpportunitySummaryApplicantType.legacy_applicant_type_id, + OpportunitySummary.opportunity_summary_id + == LinkOpportunitySummaryApplicantType.opportunity_summary_id, + ], + is_forecast=True, + is_historical_table=False, + ) self.process_link_applicant_types_group(forecast_applicant_type_records) - forecast_applicant_type_hist_records = self.fetch_with_opportunity_summary(TapplicanttypesForecastHist, LinkOpportunitySummaryApplicantType, [ - TapplicanttypesForecastHist.at_frcst_id == LinkOpportunitySummaryApplicantType.legacy_applicant_type_id, - OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryApplicantType.opportunity_summary_id - ], is_forecast=True, is_historical_table=True) + forecast_applicant_type_hist_records = self.fetch_with_opportunity_summary( + TapplicanttypesForecastHist, + LinkOpportunitySummaryApplicantType, + [ + TapplicanttypesForecastHist.at_frcst_id + == LinkOpportunitySummaryApplicantType.legacy_applicant_type_id, + OpportunitySummary.opportunity_summary_id + == LinkOpportunitySummaryApplicantType.opportunity_summary_id, + ], + is_forecast=True, + is_historical_table=True, + ) self.process_link_applicant_types_group(forecast_applicant_type_hist_records) - synopsis_applicant_type_records = self.fetch_with_opportunity_summary(TapplicanttypesSynopsis, LinkOpportunitySummaryApplicantType, [ - TapplicanttypesSynopsis.at_syn_id == LinkOpportunitySummaryApplicantType.legacy_applicant_type_id, - OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryApplicantType.opportunity_summary_id - ], is_forecast=False, is_historical_table=False) + synopsis_applicant_type_records = self.fetch_with_opportunity_summary( + TapplicanttypesSynopsis, + LinkOpportunitySummaryApplicantType, + [ + TapplicanttypesSynopsis.at_syn_id + == LinkOpportunitySummaryApplicantType.legacy_applicant_type_id, + OpportunitySummary.opportunity_summary_id + == LinkOpportunitySummaryApplicantType.opportunity_summary_id, + ], + is_forecast=False, + is_historical_table=False, + ) self.process_link_applicant_types_group(synopsis_applicant_type_records) - synopsis_applicant_type_hist_records = self.fetch_with_opportunity_summary(TapplicanttypesSynopsisHist, LinkOpportunitySummaryApplicantType, [ - TapplicanttypesSynopsisHist.at_syn_id == LinkOpportunitySummaryApplicantType.legacy_applicant_type_id, - OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryApplicantType.opportunity_summary_id - ], is_forecast=False, is_historical_table=True) + synopsis_applicant_type_hist_records = self.fetch_with_opportunity_summary( + TapplicanttypesSynopsisHist, + LinkOpportunitySummaryApplicantType, + [ + TapplicanttypesSynopsisHist.at_syn_id + == LinkOpportunitySummaryApplicantType.legacy_applicant_type_id, + OpportunitySummary.opportunity_summary_id + == LinkOpportunitySummaryApplicantType.opportunity_summary_id, + ], + is_forecast=False, + is_historical_table=True, + ) self.process_link_applicant_types_group(synopsis_applicant_type_hist_records) - def process_link_applicant_types_group(self, records: Sequence[Tuple[SourceApplicantType, LinkOpportunitySummaryApplicantType | None, OpportunitySummary | None]]) -> None: + def process_link_applicant_types_group( + self, + records: Sequence[ + Tuple[ + SourceApplicantType, + LinkOpportunitySummaryApplicantType | None, + OpportunitySummary | None, + ] + ], + ) -> None: for source_applicant_type, target_applicant_type, opportunity_summary in records: try: - self.process_link_applicant_type(source_applicant_type, target_applicant_type, opportunity_summary) + self.process_link_applicant_type( + source_applicant_type, target_applicant_type, opportunity_summary + ) except ValueError: self.increment(self.Metrics.TOTAL_ERROR_COUNT) logger.exception( @@ -411,8 +480,12 @@ def process_link_applicant_types_group(self, records: Sequence[Tuple[SourceAppli extra=transform_util.get_log_extra_applicant_type(source_applicant_type), ) - - def process_link_applicant_type(self, source_applicant_type: SourceApplicantType, target_applicant_type: LinkOpportunitySummaryApplicantType | None, opportunity_summary: OpportunitySummary | None) -> None: + def process_link_applicant_type( + self, + source_applicant_type: SourceApplicantType, + target_applicant_type: LinkOpportunitySummaryApplicantType | None, + opportunity_summary: OpportunitySummary | None, + ) -> None: self.increment(self.Metrics.TOTAL_RECORDS_PROCESSED) extra = transform_util.get_log_extra_applicant_type(source_applicant_type) logger.info("Processing applicant type", extra=extra) @@ -452,34 +525,77 @@ def process_link_applicant_type(self, source_applicant_type: SourceApplicantType source_applicant_type.transformed_at = self.transform_time def process_link_funding_categories(self) -> None: - forecast_funding_category_records = self.fetch_with_opportunity_summary(TfundactcatForecast, LinkOpportunitySummaryFundingCategory, [ - TfundactcatForecast.fac_frcst_id == LinkOpportunitySummaryFundingCategory.legacy_funding_category_id, - OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryFundingCategory.opportunity_summary_id - ], is_forecast=True, is_historical_table=False) + forecast_funding_category_records = self.fetch_with_opportunity_summary( + TfundactcatForecast, + LinkOpportunitySummaryFundingCategory, + [ + TfundactcatForecast.fac_frcst_id + == LinkOpportunitySummaryFundingCategory.legacy_funding_category_id, + OpportunitySummary.opportunity_summary_id + == LinkOpportunitySummaryFundingCategory.opportunity_summary_id, + ], + is_forecast=True, + is_historical_table=False, + ) self.process_link_funding_categories_group(forecast_funding_category_records) - forecast_funding_category_hist_records = self.fetch_with_opportunity_summary(TfundactcatForecastHist, LinkOpportunitySummaryFundingCategory, [ - TfundactcatForecastHist.fac_frcst_id == LinkOpportunitySummaryFundingCategory.legacy_funding_category_id, - OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryFundingCategory.opportunity_summary_id - ], is_forecast=True, is_historical_table=True) + forecast_funding_category_hist_records = self.fetch_with_opportunity_summary( + TfundactcatForecastHist, + LinkOpportunitySummaryFundingCategory, + [ + TfundactcatForecastHist.fac_frcst_id + == LinkOpportunitySummaryFundingCategory.legacy_funding_category_id, + OpportunitySummary.opportunity_summary_id + == LinkOpportunitySummaryFundingCategory.opportunity_summary_id, + ], + is_forecast=True, + is_historical_table=True, + ) self.process_link_funding_categories_group(forecast_funding_category_hist_records) - synopsis_funding_category_records = self.fetch_with_opportunity_summary(TfundactcatSynopsis, LinkOpportunitySummaryFundingCategory, [ - TfundactcatSynopsis.fac_syn_id == LinkOpportunitySummaryFundingCategory.legacy_funding_category_id, - OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryFundingCategory.opportunity_summary_id - ], is_forecast=False, is_historical_table=False) + synopsis_funding_category_records = self.fetch_with_opportunity_summary( + TfundactcatSynopsis, + LinkOpportunitySummaryFundingCategory, + [ + TfundactcatSynopsis.fac_syn_id + == LinkOpportunitySummaryFundingCategory.legacy_funding_category_id, + OpportunitySummary.opportunity_summary_id + == LinkOpportunitySummaryFundingCategory.opportunity_summary_id, + ], + is_forecast=False, + is_historical_table=False, + ) self.process_link_funding_categories_group(synopsis_funding_category_records) - synopsis_funding_category_hist_records = self.fetch_with_opportunity_summary(TfundactcatSynopsisHist, LinkOpportunitySummaryFundingCategory, [ - TfundactcatSynopsisHist.fac_syn_id == LinkOpportunitySummaryFundingCategory.legacy_funding_category_id, - OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryFundingCategory.opportunity_summary_id - ], is_forecast=False, is_historical_table=True) + synopsis_funding_category_hist_records = self.fetch_with_opportunity_summary( + TfundactcatSynopsisHist, + LinkOpportunitySummaryFundingCategory, + [ + TfundactcatSynopsisHist.fac_syn_id + == LinkOpportunitySummaryFundingCategory.legacy_funding_category_id, + OpportunitySummary.opportunity_summary_id + == LinkOpportunitySummaryFundingCategory.opportunity_summary_id, + ], + is_forecast=False, + is_historical_table=True, + ) self.process_link_funding_categories_group(synopsis_funding_category_hist_records) - def process_link_funding_categories_group(self, records: Sequence[Tuple[SourceFundingCategory, LinkOpportunitySummaryFundingCategory | None, OpportunitySummary | None]]) -> None: + def process_link_funding_categories_group( + self, + records: Sequence[ + Tuple[ + SourceFundingCategory, + LinkOpportunitySummaryFundingCategory | None, + OpportunitySummary | None, + ] + ], + ) -> None: for source_funding_category, target_funding_category, opportunity_summary in records: try: - self.process_link_funding_category(source_funding_category, target_funding_category, opportunity_summary) + self.process_link_funding_category( + source_funding_category, target_funding_category, opportunity_summary + ) except ValueError: self.increment(self.Metrics.TOTAL_ERROR_COUNT) logger.exception( @@ -487,8 +603,12 @@ def process_link_funding_categories_group(self, records: Sequence[Tuple[SourceFu extra=transform_util.get_log_extra_funding_category(source_funding_category), ) - - def process_link_funding_category(self, source_funding_category: SourceFundingCategory, target_funding_category: LinkOpportunitySummaryFundingCategory | None, opportunity_summary: OpportunitySummary | None) -> None: + def process_link_funding_category( + self, + source_funding_category: SourceFundingCategory, + target_funding_category: LinkOpportunitySummaryFundingCategory | None, + opportunity_summary: OpportunitySummary | None, + ) -> None: self.increment(self.Metrics.TOTAL_RECORDS_PROCESSED) extra = transform_util.get_log_extra_funding_category(source_funding_category) logger.info("Processing funding category", extra=extra) @@ -511,11 +631,13 @@ def process_link_funding_category(self, source_funding_category: SourceFundingCa else: # To avoid incrementing metrics for records we fail to transform, record # here whether it's an insert/update and we'll increment after transforming - is_insert = source_funding_category is None + is_insert = target_funding_category is None logger.info("Transforming and upserting funding category", extra=extra) - transformed_funding_category = transform_util.convert_opportunity_summary_funding_category( - source_funding_category, target_funding_category, opportunity_summary + transformed_funding_category = ( + transform_util.convert_opportunity_summary_funding_category( + source_funding_category, target_funding_category, opportunity_summary + ) ) self.db_session.merge(transformed_funding_category) @@ -528,42 +650,92 @@ def process_link_funding_category(self, source_funding_category: SourceFundingCa source_funding_category.transformed_at = self.transform_time def process_link_funding_instruments(self) -> None: - forecast_funding_instrument_records = self.fetch_with_opportunity_summary(TfundinstrForecast, LinkOpportunitySummaryFundingInstrument, [ - TfundinstrForecast.fi_frcst_id == LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id, - OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryFundingInstrument.opportunity_summary_id - ], is_forecast=True, is_historical_table=False) + forecast_funding_instrument_records = self.fetch_with_opportunity_summary( + TfundinstrForecast, + LinkOpportunitySummaryFundingInstrument, + [ + TfundinstrForecast.fi_frcst_id + == LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id, + OpportunitySummary.opportunity_summary_id + == LinkOpportunitySummaryFundingInstrument.opportunity_summary_id, + ], + is_forecast=True, + is_historical_table=False, + ) self.process_link_funding_instruments_group(forecast_funding_instrument_records) - forecast_funding_instrument_hist_records = self.fetch_with_opportunity_summary(TfundinstrForecastHist, LinkOpportunitySummaryFundingInstrument, [ - TfundinstrForecastHist.fi_frcst_id == LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id, - OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryFundingInstrument.opportunity_summary_id - ], is_forecast=True, is_historical_table=True) + forecast_funding_instrument_hist_records = self.fetch_with_opportunity_summary( + TfundinstrForecastHist, + LinkOpportunitySummaryFundingInstrument, + [ + TfundinstrForecastHist.fi_frcst_id + == LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id, + OpportunitySummary.opportunity_summary_id + == LinkOpportunitySummaryFundingInstrument.opportunity_summary_id, + ], + is_forecast=True, + is_historical_table=True, + ) self.process_link_funding_instruments_group(forecast_funding_instrument_hist_records) - synopsis_funding_instrument_records = self.fetch_with_opportunity_summary(TfundinstrSynopsis, LinkOpportunitySummaryFundingInstrument, [ - TfundinstrSynopsis.fi_syn_id == LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id, - OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryFundingInstrument.opportunity_summary_id - ], is_forecast=False, is_historical_table=False) + synopsis_funding_instrument_records = self.fetch_with_opportunity_summary( + TfundinstrSynopsis, + LinkOpportunitySummaryFundingInstrument, + [ + TfundinstrSynopsis.fi_syn_id + == LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id, + OpportunitySummary.opportunity_summary_id + == LinkOpportunitySummaryFundingInstrument.opportunity_summary_id, + ], + is_forecast=False, + is_historical_table=False, + ) self.process_link_funding_instruments_group(synopsis_funding_instrument_records) - synopsis_funding_instrument_hist_records = self.fetch_with_opportunity_summary(TfundinstrSynopsisHist, LinkOpportunitySummaryFundingInstrument, [ - TfundinstrSynopsisHist.fi_syn_id == LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id, - OpportunitySummary.opportunity_summary_id == LinkOpportunitySummaryFundingInstrument.opportunity_summary_id - ], is_forecast=False, is_historical_table=True) + synopsis_funding_instrument_hist_records = self.fetch_with_opportunity_summary( + TfundinstrSynopsisHist, + LinkOpportunitySummaryFundingInstrument, + [ + TfundinstrSynopsisHist.fi_syn_id + == LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id, + OpportunitySummary.opportunity_summary_id + == LinkOpportunitySummaryFundingInstrument.opportunity_summary_id, + ], + is_forecast=False, + is_historical_table=True, + ) self.process_link_funding_instruments_group(synopsis_funding_instrument_hist_records) - def process_link_funding_instruments_group(self, records: Sequence[Tuple[SourceFundingInstrument, LinkOpportunitySummaryFundingInstrument | None, OpportunitySummary | None]]) -> None: + def process_link_funding_instruments_group( + self, + records: Sequence[ + Tuple[ + SourceFundingInstrument, + LinkOpportunitySummaryFundingInstrument | None, + OpportunitySummary | None, + ] + ], + ) -> None: for source_funding_instrument, target_funding_instrument, opportunity_summary in records: try: - self.process_link_funding_category(source_funding_instrument, target_funding_instrument, opportunity_summary) + self.process_link_funding_instrument( + source_funding_instrument, target_funding_instrument, opportunity_summary + ) except ValueError: self.increment(self.Metrics.TOTAL_ERROR_COUNT) logger.exception( "Failed to process opportunity summary funding instrument", - extra=transform_util.get_log_extra_funding_category(source_funding_instrument), + extra=transform_util.get_log_extra_funding_instrument( + source_funding_instrument + ), ) - def process_link_funding_instrument(self, source_funding_instrument: SourceFundingInstrument, target_funding_instrument: LinkOpportunitySummaryFundingInstrument | None, opportunity_summary: OpportunitySummary | None) -> None: + def process_link_funding_instrument( + self, + source_funding_instrument: SourceFundingInstrument, + target_funding_instrument: LinkOpportunitySummaryFundingInstrument | None, + opportunity_summary: OpportunitySummary | None, + ) -> None: self.increment(self.Metrics.TOTAL_RECORDS_PROCESSED) extra = transform_util.get_log_extra_funding_instrument(source_funding_instrument) logger.info("Processing funding instrument", extra=extra) @@ -586,11 +758,13 @@ def process_link_funding_instrument(self, source_funding_instrument: SourceFundi else: # To avoid incrementing metrics for records we fail to transform, record # here whether it's an insert/update and we'll increment after transforming - is_insert = source_funding_instrument is None + is_insert = target_funding_instrument is None logger.info("Transforming and upserting funding instrument", extra=extra) - transformed_funding_instrument = transform_util.convert_opportunity_summary_funding_instrument( - source_funding_instrument, target_funding_instrument, opportunity_summary + transformed_funding_instrument = ( + transform_util.convert_opportunity_summary_funding_instrument( + source_funding_instrument, target_funding_instrument, opportunity_summary + ) ) self.db_session.merge(transformed_funding_instrument) diff --git a/api/src/data_migration/transformation/transform_util.py b/api/src/data_migration/transformation/transform_util.py index 9b21321cc..8ba6759df 100644 --- a/api/src/data_migration/transformation/transform_util.py +++ b/api/src/data_migration/transformation/transform_util.py @@ -1,20 +1,36 @@ import logging from datetime import datetime -from src.constants.lookup_constants import OpportunityCategory, ApplicantType, FundingInstrument, FundingCategory +from src.constants.lookup_constants import ( + ApplicantType, + FundingCategory, + FundingInstrument, + OpportunityCategory, +) from src.db.models.base import TimestampMixin from src.db.models.opportunity_models import ( + LinkOpportunitySummaryApplicantType, + LinkOpportunitySummaryFundingCategory, + LinkOpportunitySummaryFundingInstrument, Opportunity, OpportunityAssistanceListing, - OpportunitySummary, LinkOpportunitySummaryApplicantType, LinkOpportunitySummaryFundingInstrument, LinkOpportunitySummaryFundingCategory, + OpportunitySummary, +) +from src.db.models.staging.forecast import ( + TapplicanttypesForecast, + TapplicanttypesForecastHist, + TforecastHist, + TfundactcatForecast, + TfundactcatForecastHist, + TfundinstrForecast, + TfundinstrForecastHist, ) -from src.db.models.staging.forecast import TforecastHist, TapplicanttypesForecast, TapplicanttypesForecastHist, TfundactcatForecastHist, TfundactcatForecast, TfundinstrForecastHist, TfundinstrForecast from src.db.models.staging.opportunity import Topportunity, TopportunityCfda from src.db.models.staging.staging_base import StagingBase from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist from src.util import datetime_util -from . import SourceSummary, SourceApplicantType, SourceFundingCategory, SourceFundingInstrument +from . import SourceApplicantType, SourceFundingCategory, SourceFundingInstrument, SourceSummary logger = logging.getLogger(__name__) @@ -43,7 +59,7 @@ "22": ApplicantType.FOR_PROFIT_ORGANIZATIONS_OTHER_THAN_SMALL_BUSINESSES, "23": ApplicantType.SMALL_BUSINESSES, "25": ApplicantType.OTHER, - "99": ApplicantType.UNRESTRICTED + "99": ApplicantType.UNRESTRICTED, } FUNDING_CATEGORY_MAP = { @@ -82,6 +98,7 @@ "O": FundingInstrument.OTHER, } + def transform_opportunity( source_opportunity: Topportunity, existing_opportunity: Opportunity | None ) -> Opportunity: @@ -131,6 +148,7 @@ def transform_applicant_type(value: str | None) -> ApplicantType | None: return APPLICANT_TYPE_MAP[value] + def transform_funding_category(value: str | None) -> FundingCategory | None: if value is None or value == "": return None @@ -150,6 +168,7 @@ def transform_funding_instrument(value: str | None) -> FundingInstrument | None: return FUNDING_INSTRUMENT_MAP[value] + def transform_assistance_listing( source_assistance_listing: TopportunityCfda, existing_assistance_listing: OpportunityAssistanceListing | None, @@ -266,7 +285,11 @@ def transform_opportunity_summary( return target_summary -def convert_opportunity_summary_applicant_type(source_applicant_type: SourceApplicantType, existing_applicant_type: LinkOpportunitySummaryApplicantType | None, opportunity_summary: OpportunitySummary) -> LinkOpportunitySummaryApplicantType: +def convert_opportunity_summary_applicant_type( + source_applicant_type: SourceApplicantType, + existing_applicant_type: LinkOpportunitySummaryApplicantType | None, + opportunity_summary: OpportunitySummary, +) -> LinkOpportunitySummaryApplicantType: log_extra = get_log_extra_applicant_type(source_applicant_type) # NOTE: The columns we're working with here are mostly the primary keys @@ -289,13 +312,20 @@ def convert_opportunity_summary_applicant_type(source_applicant_type: SourceAppl legacy_applicant_type_id=legacy_applicant_type_id, applicant_type=applicant_type, updated_by=source_applicant_type.last_upd_id, - created_by=source_applicant_type.creator_id + created_by=source_applicant_type.creator_id, + ) + transform_update_create_timestamp( + source_applicant_type, target_applicant_type, log_extra=log_extra ) - transform_update_create_timestamp(source_applicant_type, target_applicant_type, log_extra=log_extra) return target_applicant_type -def convert_opportunity_summary_funding_instrument(source_funding_instrument: SourceFundingInstrument, existing_funding_instrument: LinkOpportunitySummaryFundingInstrument | None, opportunity_summary: OpportunitySummary) -> LinkOpportunitySummaryFundingInstrument: + +def convert_opportunity_summary_funding_instrument( + source_funding_instrument: SourceFundingInstrument, + existing_funding_instrument: LinkOpportunitySummaryFundingInstrument | None, + opportunity_summary: OpportunitySummary, +) -> LinkOpportunitySummaryFundingInstrument: log_extra = get_log_extra_funding_instrument(source_funding_instrument) # NOTE: The columns we're working with here are mostly the primary keys @@ -309,23 +339,30 @@ def convert_opportunity_summary_funding_instrument(source_funding_instrument: So # The legacy ID is named differently in the forecast/synopsis tables if isinstance(source_funding_instrument, (TfundinstrForecast, TfundinstrForecastHist)): - legacy_funding_instrument_id = source_funding_instrument.fi_syn_id - else: legacy_funding_instrument_id = source_funding_instrument.fi_frcst_id + else: + legacy_funding_instrument_id = source_funding_instrument.fi_syn_id target_funding_instrument = LinkOpportunitySummaryFundingInstrument( opportunity_summary_id=opportunity_summary.opportunity_summary_id, legacy_funding_instrument_id=legacy_funding_instrument_id, funding_instrument=funding_instrument, updated_by=source_funding_instrument.last_upd_id, - created_by=source_funding_instrument.creator_id + created_by=source_funding_instrument.creator_id, ) - transform_update_create_timestamp(source_funding_instrument, target_funding_instrument, log_extra=log_extra) + transform_update_create_timestamp( + source_funding_instrument, target_funding_instrument, log_extra=log_extra + ) return target_funding_instrument -def convert_opportunity_summary_funding_category(source_funding_category: SourceFundingCategory, existing_funding_category: LinkOpportunitySummaryFundingCategory | None, opportunity_summary: OpportunitySummary) -> LinkOpportunitySummaryFundingCategory: + +def convert_opportunity_summary_funding_category( + source_funding_category: SourceFundingCategory, + existing_funding_category: LinkOpportunitySummaryFundingCategory | None, + opportunity_summary: OpportunitySummary, +) -> LinkOpportunitySummaryFundingCategory: log_extra = get_log_extra_funding_category(source_funding_category) # NOTE: The columns we're working with here are mostly the primary keys @@ -348,13 +385,16 @@ def convert_opportunity_summary_funding_category(source_funding_category: Source legacy_funding_category_id=legacy_funding_category_id, funding_category=funding_category, updated_by=source_funding_category.last_upd_id, - created_by=source_funding_category.creator_id + created_by=source_funding_category.creator_id, ) - transform_update_create_timestamp(source_funding_category, target_funding_category, log_extra=log_extra) + transform_update_create_timestamp( + source_funding_category, target_funding_category, log_extra=log_extra + ) return target_funding_category + def convert_est_timestamp_to_utc(timestamp: datetime | None) -> datetime | None: if timestamp is None: return None @@ -464,6 +504,7 @@ def get_log_extra_applicant_type(source_applicant_type: SourceApplicantType) -> "table_name": source_applicant_type.__tablename__, } + def get_log_extra_funding_category(source_funding_category: SourceFundingCategory) -> dict: return { "opportunity_id": source_funding_category.opportunity_id, @@ -473,6 +514,7 @@ def get_log_extra_funding_category(source_funding_category: SourceFundingCategor "table_name": source_funding_category.__tablename__, } + def get_log_extra_funding_instrument(source_funding_instrument: SourceFundingInstrument) -> dict: return { "opportunity_id": source_funding_instrument.opportunity_id, diff --git a/api/src/db/migrations/versions/2024_05_07_add_unique_constraint_link_table_legacy_.py b/api/src/db/migrations/versions/2024_05_07_add_unique_constraint_link_table_legacy_.py new file mode 100644 index 000000000..06e25b87c --- /dev/null +++ b/api/src/db/migrations/versions/2024_05_07_add_unique_constraint_link_table_legacy_.py @@ -0,0 +1,61 @@ +"""add unique constraint link table legacy ids + +Revision ID: f97987d087b5 +Revises: 24061ff82646 +Create Date: 2024-05-07 14:41:19.401963 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "f97987d087b5" +down_revision = "24061ff82646" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_unique_constraint( + op.f("link_opportunity_summary_applicant_type_opportunity_summary_id_uniq"), + "link_opportunity_summary_applicant_type", + ["opportunity_summary_id", "legacy_applicant_type_id"], + schema="api", + ) + op.create_unique_constraint( + op.f("link_opportunity_summary_funding_category_opportunity_summary_id_uniq"), + "link_opportunity_summary_funding_category", + ["opportunity_summary_id", "legacy_funding_category_id"], + schema="api", + ) + op.create_unique_constraint( + op.f("link_opportunity_summary_funding_instrument_opportunity_summary_id_uniq"), + "link_opportunity_summary_funding_instrument", + ["opportunity_summary_id", "legacy_funding_instrument_id"], + schema="api", + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint( + op.f("link_opportunity_summary_funding_instrument_opportunity_summary_id_uniq"), + "link_opportunity_summary_funding_instrument", + schema="api", + type_="unique", + ) + op.drop_constraint( + op.f("link_opportunity_summary_funding_category_opportunity_summary_id_uniq"), + "link_opportunity_summary_funding_category", + schema="api", + type_="unique", + ) + op.drop_constraint( + op.f("link_opportunity_summary_applicant_type_opportunity_summary_id_uniq"), + "link_opportunity_summary_applicant_type", + schema="api", + type_="unique", + ) + # ### end Alembic commands ### diff --git a/api/src/db/models/opportunity_models.py b/api/src/db/models/opportunity_models.py index 423583d8f..dfc1c7845 100644 --- a/api/src/db/models/opportunity_models.py +++ b/api/src/db/models/opportunity_models.py @@ -212,6 +212,14 @@ class OpportunityAssistanceListing(ApiSchemaTable, TimestampMixin): class LinkOpportunitySummaryFundingInstrument(ApiSchemaTable, TimestampMixin): __tablename__ = "link_opportunity_summary_funding_instrument" + __table_args__ = ( + # We want a unique constraint so that legacy IDs are unique for a given opportunity summary + UniqueConstraint("opportunity_summary_id", "legacy_funding_instrument_id"), + # Need to define the table args like this to inherit whatever we set on the super table + # otherwise we end up overwriting things and Alembic remakes the whole table + ApiSchemaTable.__table_args__, + ) + opportunity_summary_id: Mapped[int] = mapped_column( BigInteger, ForeignKey(OpportunitySummary.opportunity_summary_id), @@ -237,6 +245,14 @@ class LinkOpportunitySummaryFundingInstrument(ApiSchemaTable, TimestampMixin): class LinkOpportunitySummaryFundingCategory(ApiSchemaTable, TimestampMixin): __tablename__ = "link_opportunity_summary_funding_category" + __table_args__ = ( + # We want a unique constraint so that legacy IDs are unique for a given opportunity summary + UniqueConstraint("opportunity_summary_id", "legacy_funding_category_id"), + # Need to define the table args like this to inherit whatever we set on the super table + # otherwise we end up overwriting things and Alembic remakes the whole table + ApiSchemaTable.__table_args__, + ) + opportunity_summary_id: Mapped[int] = mapped_column( BigInteger, ForeignKey(OpportunitySummary.opportunity_summary_id), @@ -263,9 +279,8 @@ class LinkOpportunitySummaryApplicantType(ApiSchemaTable, TimestampMixin): __tablename__ = "link_opportunity_summary_applicant_type" __table_args__ = ( - UniqueConstraint( - "opportunity_summary_id", "legacy_applicant_type_id" - ), + # We want a unique constraint so that legacy IDs are unique for a given opportunity summary + UniqueConstraint("opportunity_summary_id", "legacy_applicant_type_id"), # Need to define the table args like this to inherit whatever we set on the super table # otherwise we end up overwriting things and Alembic remakes the whole table ApiSchemaTable.__table_args__, diff --git a/api/src/db/models/staging/forecast.py b/api/src/db/models/staging/forecast.py index 1a028aa2e..3604a32f2 100644 --- a/api/src/db/models/staging/forecast.py +++ b/api/src/db/models/staging/forecast.py @@ -41,14 +41,14 @@ class TapplicanttypesForecast( ): __tablename__ = "tapplicanttypes_forecast" - forecast: Mapped[Tforecast | None] = relationship( Tforecast, primaryjoin="TapplicanttypesForecast.opportunity_id == foreign(Tforecast.opportunity_id)", uselist=False, - overlaps="forecast" + overlaps="forecast", ) + class TapplicanttypesForecastHist( StagingBase, forecast_mixin.TapplicanttypesForecastHistMixin, StagingParamMixin ): @@ -58,9 +58,10 @@ class TapplicanttypesForecastHist( TforecastHist, primaryjoin="and_(TapplicanttypesForecastHist.opportunity_id == foreign(TforecastHist.opportunity_id), TapplicanttypesForecastHist.revision_number == foreign(TforecastHist.revision_number))", uselist=False, - overlaps="forecast" + overlaps="forecast", ) + class TfundactcatForecast(StagingBase, forecast_mixin.TfundactcatForecastMixin, StagingParamMixin): __tablename__ = "tfundactcat_forecast" @@ -68,9 +69,10 @@ class TfundactcatForecast(StagingBase, forecast_mixin.TfundactcatForecastMixin, Tforecast, primaryjoin="TfundactcatForecast.opportunity_id == foreign(Tforecast.opportunity_id)", uselist=False, - overlaps="forecast" + overlaps="forecast", ) + class TfundactcatForecastHist( StagingBase, forecast_mixin.TfundactcatForecastHistMixin, StagingParamMixin ): @@ -80,9 +82,10 @@ class TfundactcatForecastHist( TforecastHist, primaryjoin="and_(TfundactcatForecastHist.opportunity_id == foreign(TforecastHist.opportunity_id), TfundactcatForecastHist.revision_number == foreign(TforecastHist.revision_number))", uselist=False, - overlaps="forecast" + overlaps="forecast", ) + class TfundinstrForecast(StagingBase, forecast_mixin.TfundinstrForecastMixin, StagingParamMixin): __tablename__ = "tfundinstr_forecast" @@ -90,9 +93,10 @@ class TfundinstrForecast(StagingBase, forecast_mixin.TfundinstrForecastMixin, St Tforecast, primaryjoin="TfundinstrForecast.opportunity_id == foreign(Tforecast.opportunity_id)", uselist=False, - overlaps="forecast" + overlaps="forecast", ) + class TfundinstrForecastHist( StagingBase, forecast_mixin.TfundinstrForecastHistMixin, StagingParamMixin ): @@ -102,5 +106,5 @@ class TfundinstrForecastHist( TforecastHist, primaryjoin="and_(TfundinstrForecastHist.opportunity_id == foreign(TforecastHist.opportunity_id), TfundinstrForecastHist.revision_number == foreign(TforecastHist.revision_number))", uselist=False, - overlaps="forecast" + overlaps="forecast", ) diff --git a/api/src/db/models/staging/synopsis.py b/api/src/db/models/staging/synopsis.py index ba824ab36..a70fe6bc3 100644 --- a/api/src/db/models/staging/synopsis.py +++ b/api/src/db/models/staging/synopsis.py @@ -45,9 +45,10 @@ class TapplicanttypesSynopsis( Tsynopsis, primaryjoin="TapplicanttypesSynopsis.opportunity_id == foreign(Tsynopsis.opportunity_id)", uselist=False, - overlaps="synopsis" + overlaps="synopsis", ) + class TapplicanttypesSynopsisHist( StagingBase, synopsis_mixin.TapplicanttypesSynopsisHistMixin, StagingParamMixin ): @@ -57,9 +58,10 @@ class TapplicanttypesSynopsisHist( TsynopsisHist, primaryjoin="and_(TapplicanttypesSynopsisHist.opportunity_id == foreign(TsynopsisHist.opportunity_id), TapplicanttypesSynopsisHist.revision_number == foreign(TsynopsisHist.revision_number))", uselist=False, - overlaps="synopsis" + overlaps="synopsis", ) + class TfundactcatSynopsis(StagingBase, synopsis_mixin.TfundactcatSynopsisMixin, StagingParamMixin): __tablename__ = "tfundactcat_synopsis" @@ -67,9 +69,10 @@ class TfundactcatSynopsis(StagingBase, synopsis_mixin.TfundactcatSynopsisMixin, Tsynopsis, primaryjoin="TfundactcatSynopsis.opportunity_id == foreign(Tsynopsis.opportunity_id)", uselist=False, - overlaps="synopsis" + overlaps="synopsis", ) + class TfundactcatSynopsisHist( StagingBase, synopsis_mixin.TfundactcatSynopsisHistMixin, StagingParamMixin ): @@ -79,9 +82,10 @@ class TfundactcatSynopsisHist( TsynopsisHist, primaryjoin="and_(TfundactcatSynopsisHist.opportunity_id == foreign(TsynopsisHist.opportunity_id), TfundactcatSynopsisHist.revision_number == foreign(TsynopsisHist.revision_number))", uselist=False, - overlaps="synopsis" + overlaps="synopsis", ) + class TfundinstrSynopsis(StagingBase, synopsis_mixin.TfundinstrSynopsisMixin, StagingParamMixin): __tablename__ = "tfundinstr_synopsis" @@ -89,9 +93,10 @@ class TfundinstrSynopsis(StagingBase, synopsis_mixin.TfundinstrSynopsisMixin, St Tsynopsis, primaryjoin="TfundinstrSynopsis.opportunity_id == foreign(Tsynopsis.opportunity_id)", uselist=False, - overlaps="synopsis" + overlaps="synopsis", ) + class TfundinstrSynopsisHist( StagingBase, synopsis_mixin.TfundinstrSynopsisHistMixin, StagingParamMixin ): @@ -101,5 +106,5 @@ class TfundinstrSynopsisHist( TsynopsisHist, primaryjoin="and_(TfundinstrSynopsisHist.opportunity_id == foreign(TsynopsisHist.opportunity_id), TfundinstrSynopsisHist.revision_number == foreign(TsynopsisHist.revision_number))", uselist=False, - overlaps="synopsis" - ) \ No newline at end of file + overlaps="synopsis", + ) diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py index 59252e8dd..6cc316483 100644 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -2,33 +2,19 @@ import pytest -from src.constants.lookup_constants import ApplicantType, FundingInstrument, FundingCategory +import tests.src.db.models.factories as f +from src.constants.lookup_constants import ApplicantType, FundingCategory, FundingInstrument from src.data_migration.transformation.transform_oracle_data_task import TransformOracleDataTask +from src.db.models import staging from src.db.models.opportunity_models import ( + LinkOpportunitySummaryApplicantType, + LinkOpportunitySummaryFundingCategory, + LinkOpportunitySummaryFundingInstrument, Opportunity, OpportunityAssistanceListing, - OpportunitySummary, LinkOpportunitySummaryApplicantType, LinkOpportunitySummaryFundingInstrument, LinkOpportunitySummaryFundingCategory, + OpportunitySummary, ) -from src.db.models.staging.forecast import TforecastHist, TapplicanttypesForecastHist, TfundinstrForecastHist, TfundactcatForecastHist -from src.db.models.staging.opportunity import Topportunity, TopportunityCfda -from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist, TapplicanttypesSynopsis, TapplicanttypesSynopsisHist, TfundinstrSynopsis, TfundinstrSynopsisHist, TfundactcatSynopsis, \ - TfundactcatSynopsisHist from tests.conftest import BaseTestClass -from tests.src.db.models.factories import ( - OpportunityAssistanceListingFactory, - OpportunityFactory, - OpportunitySummaryFactory, - StagingTforecastFactory, - StagingTforecastHistFactory, - StagingTopportunityCfdaFactory, - StagingTopportunityFactory, - StagingTsynopsisFactory, - StagingTsynopsisHistFactory, StagingTapplicanttypesForecastFactory, StagingTapplicanttypesForecastHistFactory, LinkOpportunitySummaryApplicantTypeFactory, StagingTapplicanttypesSynopsisFactory, - StagingTapplicanttypesSynopsisHistFactory, StagingTfundinstrForecastFactory, StagingTfundinstrForecastHistFactory, StagingTfundinstrSynopsisFactory, StagingTfundinstrSynopsisHistFactory, - LinkOpportunitySummaryFundingInstrumentFactory, StagingTfundactcatForecastFactory, StagingTfundactcatForecastHistFactory, StagingTfundactcatSynopsisFactory, StagingTfundactcatSynopsisHistFactory, - LinkOpportunitySummaryFundingCategoryFactory, -) -from sqlalchemy import and_ def setup_opportunity( @@ -37,11 +23,11 @@ def setup_opportunity( is_already_processed: bool = False, source_values: dict | None = None, all_fields_null: bool = False, -) -> Topportunity: +) -> staging.opportunity.Topportunity: if source_values is None: source_values = {} - source_opportunity = StagingTopportunityFactory.create( + source_opportunity = f.StagingTopportunityFactory.create( **source_values, is_deleted=is_delete, already_transformed=is_already_processed, @@ -50,7 +36,7 @@ def setup_opportunity( ) if create_existing: - OpportunityFactory.create( + f.OpportunityFactory.create( opportunity_id=source_opportunity.opportunity_id, # set created_at/updated_at to an earlier time so its clear # when they were last updated @@ -67,7 +53,7 @@ def setup_cfda( source_values: dict | None = None, all_fields_null: bool = False, opportunity: Opportunity | None = None, -) -> TopportunityCfda: +) -> staging.opportunity.TopportunityCfda: if source_values is None: source_values = {} @@ -75,7 +61,7 @@ def setup_cfda( if opportunity is not None: source_values["opportunity_id"] = opportunity.opportunity_id - source_cfda = StagingTopportunityCfdaFactory.create( + source_cfda = f.StagingTopportunityCfdaFactory.create( **source_values, opportunity=None, # To override the factory trying to create something is_deleted=is_delete, @@ -84,7 +70,7 @@ def setup_cfda( ) if create_existing: - OpportunityAssistanceListingFactory.create( + f.OpportunityAssistanceListingFactory.create( opportunity=opportunity, opportunity_assistance_listing_id=source_cfda.opp_cfda_id, # set created_at/updated_at to an earlier time so its clear @@ -109,14 +95,14 @@ def setup_synopsis_forecast( if is_forecast: if revision_number is None: - factory_cls = StagingTforecastFactory + factory_cls = f.StagingTforecastFactory else: - factory_cls = StagingTforecastHistFactory + factory_cls = f.StagingTforecastHistFactory else: if revision_number is None: - factory_cls = StagingTsynopsisFactory + factory_cls = f.StagingTsynopsisFactory else: - factory_cls = StagingTsynopsisHistFactory + factory_cls = f.StagingTsynopsisHistFactory if revision_number is not None: source_values["revision_number"] = revision_number @@ -130,7 +116,7 @@ def setup_synopsis_forecast( ) if create_existing: - OpportunitySummaryFactory.create( + f.OpportunitySummaryFactory.create( opportunity=opportunity, is_forecast=is_forecast, revision_number=revision_number ) @@ -147,7 +133,9 @@ def setup_applicant_type( source_values: dict | None = None, ): if create_existing and is_delete is False and applicant_type is None: - raise Exception("If create_existing is True, is_delete is False - must provide the properly converted / mapped value for applicant_type") + raise Exception( + "If create_existing is True, is_delete is False - must provide the properly converted / mapped value for applicant_type" + ) if source_values is None: source_values = {} @@ -155,16 +143,16 @@ def setup_applicant_type( if opportunity_summary.is_forecast: source_values["forecast"] = None if opportunity_summary.revision_number is None: - factory_cls = StagingTapplicanttypesForecastFactory + factory_cls = f.StagingTapplicanttypesForecastFactory else: - factory_cls = StagingTapplicanttypesForecastHistFactory + factory_cls = f.StagingTapplicanttypesForecastHistFactory source_values["revision_number"] = opportunity_summary.revision_number else: source_values["synopsis"] = None if opportunity_summary.revision_number is None: - factory_cls = StagingTapplicanttypesSynopsisFactory + factory_cls = f.StagingTapplicanttypesSynopsisFactory else: - factory_cls = StagingTapplicanttypesSynopsisHistFactory + factory_cls = f.StagingTapplicanttypesSynopsisHistFactory source_values["revision_number"] = opportunity_summary.revision_number source_applicant_type = factory_cls.create( @@ -181,14 +169,15 @@ def setup_applicant_type( else: legacy_id = source_applicant_type.at_syn_id - LinkOpportunitySummaryApplicantTypeFactory.create( + f.LinkOpportunitySummaryApplicantTypeFactory.create( opportunity_summary=opportunity_summary, legacy_applicant_type_id=legacy_id, - applicant_type=applicant_type + applicant_type=applicant_type, ) return source_applicant_type + def setup_funding_instrument( create_existing: bool, opportunity_summary: OpportunitySummary, @@ -199,21 +188,26 @@ def setup_funding_instrument( source_values: dict | None = None, ): if create_existing and is_delete is False and funding_instrument is None: - raise Exception("If create_existing is True, is_delete is False - must provide the properly converted / mapped value for funding_instrument") + raise Exception( + "If create_existing is True, is_delete is False - must provide the properly converted / mapped value for funding_instrument" + ) + + if source_values is None: + source_values = {} if opportunity_summary.is_forecast: source_values["forecast"] = None if opportunity_summary.revision_number is None: - factory_cls = StagingTfundinstrForecastFactory + factory_cls = f.StagingTfundinstrForecastFactory else: - factory_cls = StagingTfundinstrForecastHistFactory + factory_cls = f.StagingTfundinstrForecastHistFactory source_values["revision_number"] = opportunity_summary.revision_number else: source_values["synopsis"] = None if opportunity_summary.revision_number is None: - factory_cls = StagingTfundinstrSynopsisFactory + factory_cls = f.StagingTfundinstrSynopsisFactory else: - factory_cls = StagingTfundinstrSynopsisHistFactory + factory_cls = f.StagingTfundinstrSynopsisHistFactory source_values["revision_number"] = opportunity_summary.revision_number source_funding_instrument = factory_cls.create( @@ -230,10 +224,10 @@ def setup_funding_instrument( else: legacy_id = source_funding_instrument.fi_syn_id - LinkOpportunitySummaryFundingInstrumentFactory.create( + f.LinkOpportunitySummaryFundingInstrumentFactory.create( opportunity_summary=opportunity_summary, legacy_funding_instrument_id=legacy_id, - funding_instrument=funding_instrument + funding_instrument=funding_instrument, ) return source_funding_instrument @@ -249,7 +243,9 @@ def setup_funding_category( source_values: dict | None = None, ): if create_existing and is_delete is False and funding_category is None: - raise Exception("If create_existing is True, is_delete is False - must provide the properly converted / mapped value for funding_category") + raise Exception( + "If create_existing is True, is_delete is False - must provide the properly converted / mapped value for funding_category" + ) if source_values is None: source_values = {} @@ -257,16 +253,16 @@ def setup_funding_category( if opportunity_summary.is_forecast: source_values["forecast"] = None if opportunity_summary.revision_number is None: - factory_cls = StagingTfundactcatForecastFactory + factory_cls = f.StagingTfundactcatForecastFactory else: - factory_cls = StagingTfundactcatForecastHistFactory + factory_cls = f.StagingTfundactcatForecastHistFactory source_values["revision_number"] = opportunity_summary.revision_number else: source_values["synopsis"] = None if opportunity_summary.revision_number is None: - factory_cls = StagingTfundactcatSynopsisFactory + factory_cls = f.StagingTfundactcatSynopsisFactory else: - factory_cls = StagingTfundactcatSynopsisHistFactory + factory_cls = f.StagingTfundactcatSynopsisHistFactory source_values["revision_number"] = opportunity_summary.revision_number source_funding_category = factory_cls.create( @@ -283,13 +279,14 @@ def setup_funding_category( else: legacy_id = source_funding_category.fac_syn_id - LinkOpportunitySummaryFundingCategoryFactory.create( + f.LinkOpportunitySummaryFundingCategoryFactory.create( opportunity_summary=opportunity_summary, legacy_funding_category_id=legacy_id, - funding_category=funding_category + funding_category=funding_category, ) - return funding_category + return source_funding_category + def validate_matching_fields( source, destination, fields: list[Tuple[str, str]], expect_all_to_match: bool @@ -319,7 +316,7 @@ def validate_matching_fields( def validate_opportunity( db_session, - source_opportunity: Topportunity, + source_opportunity: staging.opportunity.Topportunity, expect_in_db: bool = True, expect_values_to_match: bool = True, ): @@ -362,7 +359,7 @@ def validate_opportunity( def validate_assistance_listing( db_session, - source_cfda: TopportunityCfda, + source_cfda: staging.opportunity.TopportunityCfda, expect_in_db: bool = True, expect_values_to_match: bool = True, ): @@ -397,7 +394,7 @@ def validate_opportunity_summary( ): revision_number = None is_forecast = source_summary.is_forecast - if isinstance(source_summary, (TsynopsisHist, TforecastHist)): + if isinstance(source_summary, (staging.synopsis.TsynopsisHist, staging.forecast.TforecastHist)): revision_number = source_summary.revision_number opportunity_summary = ( @@ -432,7 +429,7 @@ def validate_opportunity_summary( ("creator_id", "created_by"), ] - if isinstance(source_summary, (Tsynopsis, TsynopsisHist)): + if isinstance(source_summary, (staging.synopsis.Tsynopsis, staging.synopsis.TsynopsisHist)): matching_fields.extend( [ ("syn_desc", "summary_description"), @@ -461,7 +458,7 @@ def validate_opportunity_summary( # History only fields is_deleted = False - if isinstance(source_summary, (TsynopsisHist, TforecastHist)): + if isinstance(source_summary, (staging.synopsis.TsynopsisHist, staging.forecast.TforecastHist)): matching_fields.extend([("revision_number", "revision_number")]) is_deleted = source_summary.action_type == "D" @@ -473,10 +470,21 @@ def validate_opportunity_summary( assert opportunity_summary.is_deleted == is_deleted -def validate_applicant_type(db_session, source_applicant_type, expect_in_db: bool = True, expected_applicant_type: ApplicantType | None = None, was_processed: bool = True, expect_values_to_match: bool = True): + +def validate_applicant_type( + db_session, + source_applicant_type, + expect_in_db: bool = True, + expected_applicant_type: ApplicantType | None = None, + was_processed: bool = True, + expect_values_to_match: bool = True, +): assert (source_applicant_type.transformed_at is not None) == was_processed - if isinstance(source_applicant_type, (TapplicanttypesSynopsis, TapplicanttypesSynopsisHist)): + if isinstance( + source_applicant_type, + (staging.synopsis.TapplicanttypesSynopsis, staging.synopsis.TapplicanttypesSynopsisHist), + ): is_forecast = False legacy_id = source_applicant_type.at_syn_id else: @@ -484,20 +492,32 @@ def validate_applicant_type(db_session, source_applicant_type, expect_in_db: boo legacy_id = source_applicant_type.at_frcst_id revision_number = None - if isinstance(source_applicant_type, (TapplicanttypesSynopsisHist, TapplicanttypesForecastHist)): + if isinstance( + source_applicant_type, + ( + staging.synopsis.TapplicanttypesSynopsisHist, + staging.forecast.TapplicanttypesForecastHist, + ), + ): revision_number = source_applicant_type.revision_number # In order to properly find the link table value, need to first determine # the opportunity summary in a subquery - opportunity_summary_id = db_session.query(OpportunitySummary.opportunity_summary_id).filter( - OpportunitySummary.revision_number == revision_number, OpportunitySummary.is_forecast == is_forecast, OpportunitySummary.opportunity_id == source_applicant_type.opportunity_id - ).scalar() + opportunity_summary_id = ( + db_session.query(OpportunitySummary.opportunity_summary_id) + .filter( + OpportunitySummary.revision_number == revision_number, + OpportunitySummary.is_forecast == is_forecast, + OpportunitySummary.opportunity_id == source_applicant_type.opportunity_id, + ) + .scalar() + ) link_applicant_type = ( db_session.query(LinkOpportunitySummaryApplicantType) .filter( LinkOpportunitySummaryApplicantType.legacy_applicant_type_id == legacy_id, - LinkOpportunitySummaryApplicantType.opportunity_summary_id == opportunity_summary_id + LinkOpportunitySummaryApplicantType.opportunity_summary_id == opportunity_summary_id, ) .one_or_none() ) @@ -509,12 +529,28 @@ def validate_applicant_type(db_session, source_applicant_type, expect_in_db: boo assert link_applicant_type is not None assert link_applicant_type.applicant_type == expected_applicant_type - validate_matching_fields(source_applicant_type, link_applicant_type, [("creator_id", "created_by"), ("last_upd_id", "updated_by")], expect_values_to_match) + validate_matching_fields( + source_applicant_type, + link_applicant_type, + [("creator_id", "created_by"), ("last_upd_id", "updated_by")], + expect_values_to_match, + ) + -def validate_funding_instrument(db_session, source_funding_instrument, expect_in_db: bool = True, expected_funding_instrument: FundingInstrument | None = None, was_processed: bool = True, expect_values_to_match: bool = True): +def validate_funding_instrument( + db_session, + source_funding_instrument, + expect_in_db: bool = True, + expected_funding_instrument: FundingInstrument | None = None, + was_processed: bool = True, + expect_values_to_match: bool = True, +): assert (source_funding_instrument.transformed_at is not None) == was_processed - if isinstance(source_funding_instrument, (TfundinstrSynopsis, TfundinstrSynopsisHist)): + if isinstance( + source_funding_instrument, + (staging.synopsis.TfundinstrSynopsis, staging.synopsis.TfundinstrSynopsisHist), + ): is_forecast = False legacy_id = source_funding_instrument.fi_syn_id else: @@ -522,20 +558,30 @@ def validate_funding_instrument(db_session, source_funding_instrument, expect_in legacy_id = source_funding_instrument.fi_frcst_id revision_number = None - if isinstance(source_funding_instrument, (TfundinstrSynopsisHist, TfundinstrForecastHist)): + if isinstance( + source_funding_instrument, + (staging.synopsis.TfundinstrSynopsisHist, staging.forecast.TfundinstrForecastHist), + ): revision_number = source_funding_instrument.revision_number # In order to properly find the link table value, need to first determine # the opportunity summary in a subquery - opportunity_summary_id = db_session.query(OpportunitySummary.opportunity_summary_id).filter( - OpportunitySummary.revision_number == revision_number, OpportunitySummary.is_forecast == is_forecast, OpportunitySummary.opportunity_id == source_funding_instrument.opportunity_id - ).scalar() + opportunity_summary_id = ( + db_session.query(OpportunitySummary.opportunity_summary_id) + .filter( + OpportunitySummary.revision_number == revision_number, + OpportunitySummary.is_forecast == is_forecast, + OpportunitySummary.opportunity_id == source_funding_instrument.opportunity_id, + ) + .scalar() + ) link_funding_instrument = ( db_session.query(LinkOpportunitySummaryFundingInstrument) .filter( LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id == legacy_id, - LinkOpportunitySummaryFundingInstrument.opportunity_summary_id == opportunity_summary_id + LinkOpportunitySummaryFundingInstrument.opportunity_summary_id + == opportunity_summary_id, ) .one_or_none() ) @@ -547,12 +593,28 @@ def validate_funding_instrument(db_session, source_funding_instrument, expect_in assert link_funding_instrument is not None assert link_funding_instrument.funding_instrument == expected_funding_instrument - validate_matching_fields(source_funding_instrument, link_funding_instrument, [("creator_id", "created_by"), ("last_upd_id", "updated_by")], expect_values_to_match) + validate_matching_fields( + source_funding_instrument, + link_funding_instrument, + [("creator_id", "created_by"), ("last_upd_id", "updated_by")], + expect_values_to_match, + ) -def validate_funding_category(db_session, source_funding_category, expect_in_db: bool = True, expected_funding_category: FundingCategory | None = None, was_processed: bool = True, expect_values_to_match: bool = True): + +def validate_funding_category( + db_session, + source_funding_category, + expect_in_db: bool = True, + expected_funding_category: FundingCategory | None = None, + was_processed: bool = True, + expect_values_to_match: bool = True, +): assert (source_funding_category.transformed_at is not None) == was_processed - if isinstance(source_funding_category, (TfundactcatSynopsis, TfundactcatSynopsisHist)): + if isinstance( + source_funding_category, + (staging.synopsis.TfundactcatSynopsis, staging.synopsis.TfundactcatSynopsisHist), + ): is_forecast = False legacy_id = source_funding_category.fac_syn_id else: @@ -560,20 +622,29 @@ def validate_funding_category(db_session, source_funding_category, expect_in_db: legacy_id = source_funding_category.fac_frcst_id revision_number = None - if isinstance(source_funding_category, (TfundactcatSynopsisHist, TfundactcatForecastHist)): + if isinstance( + source_funding_category, + (staging.synopsis.TfundactcatSynopsisHist, staging.forecast.TfundactcatForecastHist), + ): revision_number = source_funding_category.revision_number # In order to properly find the link table value, need to first determine # the opportunity summary in a subquery - opportunity_summary_id = db_session.query(OpportunitySummary.opportunity_summary_id).filter( - OpportunitySummary.revision_number == revision_number, OpportunitySummary.is_forecast == is_forecast, OpportunitySummary.opportunity_id == source_funding_category.opportunity_id - ).scalar() + opportunity_summary_id = ( + db_session.query(OpportunitySummary.opportunity_summary_id) + .filter( + OpportunitySummary.revision_number == revision_number, + OpportunitySummary.is_forecast == is_forecast, + OpportunitySummary.opportunity_id == source_funding_category.opportunity_id, + ) + .scalar() + ) link_funding_category = ( db_session.query(LinkOpportunitySummaryFundingCategory) .filter( LinkOpportunitySummaryFundingCategory.legacy_funding_category_id == legacy_id, - LinkOpportunitySummaryFundingCategory.opportunity_summary_id == opportunity_summary_id + LinkOpportunitySummaryFundingCategory.opportunity_summary_id == opportunity_summary_id, ) .one_or_none() ) @@ -585,7 +656,12 @@ def validate_funding_category(db_session, source_funding_category, expect_in_db: assert link_funding_category is not None assert link_funding_category.funding_category == expected_funding_category - validate_matching_fields(source_funding_category, link_funding_category, [("creator_id", "created_by"), ("last_upd_id", "updated_by")], expect_values_to_match) + validate_matching_fields( + source_funding_category, + link_funding_category, + [("creator_id", "created_by"), ("last_upd_id", "updated_by")], + expect_values_to_match, + ) class TestTransformOpportunity(BaseTestClass): @@ -696,7 +772,7 @@ def transform_oracle_data_task( return TransformOracleDataTask(db_session) def test_process_opportunity_assistance_listings(self, db_session, transform_oracle_data_task): - opportunity1 = OpportunityFactory.create(opportunity_assistance_listings=[]) + opportunity1 = f.OpportunityFactory.create(opportunity_assistance_listings=[]) cfda_insert1 = setup_cfda(create_existing=False, opportunity=opportunity1) cfda_insert2 = setup_cfda(create_existing=False, opportunity=opportunity1) cfda_update1 = setup_cfda(create_existing=True, opportunity=opportunity1) @@ -705,7 +781,7 @@ def test_process_opportunity_assistance_listings(self, db_session, transform_ora create_existing=True, is_already_processed=True, opportunity=opportunity1 ) - opportunity2 = OpportunityFactory.create(opportunity_assistance_listings=[]) + opportunity2 = f.OpportunityFactory.create(opportunity_assistance_listings=[]) cfda_insert3 = setup_cfda(create_existing=False, opportunity=opportunity2) cfda_update_already_processed2 = setup_cfda( create_existing=True, is_already_processed=True, opportunity=opportunity2 @@ -718,7 +794,7 @@ def test_process_opportunity_assistance_listings(self, db_session, transform_ora ) cfda_delete2 = setup_cfda(create_existing=True, is_delete=True, opportunity=opportunity2) - opportunity3 = OpportunityFactory.create(opportunity_assistance_listings=[]) + opportunity3 = f.OpportunityFactory.create(opportunity_assistance_listings=[]) cfda_update2 = setup_cfda(create_existing=True, opportunity=opportunity3) cfda_delete_but_current_missing = setup_cfda( create_existing=False, is_delete=True, opportunity=opportunity3 @@ -818,7 +894,7 @@ def test_process_assistance_listing_orphaned_record( def test_process_assistance_listing_delete_but_current_missing( self, db_session, transform_oracle_data_task ): - opportunity = OpportunityFactory.create(opportunity_assistance_listings=[]) + opportunity = f.OpportunityFactory.create(opportunity_assistance_listings=[]) delete_but_current_missing = setup_cfda( create_existing=False, is_delete=True, opportunity=opportunity ) @@ -842,7 +918,7 @@ def transform_oracle_data_task( def test_process_opportunity_summaries(self, db_session, transform_oracle_data_task): # Basic inserts - opportunity1 = OpportunityFactory.create( + opportunity1 = f.OpportunityFactory.create( no_current_summary=True, opportunity_assistance_listings=[] ) forecast_insert1 = setup_synopsis_forecast( @@ -860,7 +936,7 @@ def test_process_opportunity_summaries(self, db_session, transform_oracle_data_t # Mix of updates and inserts, somewhat resembling what happens when summary objects # get moved to the historical table (we'd update the synopsis/forecast records, and create new historical) - opportunity2 = OpportunityFactory.create( + opportunity2 = f.OpportunityFactory.create( no_current_summary=True, opportunity_assistance_listings=[] ) forecast_update1 = setup_synopsis_forecast( @@ -883,7 +959,7 @@ def test_process_opportunity_summaries(self, db_session, transform_oracle_data_t ) # Mix of inserts, updates, and deletes - opportunity3 = OpportunityFactory.create( + opportunity3 = f.OpportunityFactory.create( no_current_summary=True, opportunity_assistance_listings=[] ) forecast_delete1 = setup_synopsis_forecast( @@ -912,7 +988,7 @@ def test_process_opportunity_summaries(self, db_session, transform_oracle_data_t ) # A few error scenarios - opportunity4 = OpportunityFactory.create( + opportunity4 = f.OpportunityFactory.create( no_current_summary=True, opportunity_assistance_listings=[] ) forecast_delete_but_current_missing = setup_synopsis_forecast( @@ -1003,7 +1079,7 @@ def test_process_opportunity_summaries(self, db_session, transform_oracle_data_t def test_process_opportunity_summary_delete_but_current_missing( self, db_session, transform_oracle_data_task, is_forecast, revision_number ): - opportunity = OpportunityFactory.create( + opportunity = f.OpportunityFactory.create( no_current_summary=True, opportunity_assistance_listings=[] ) delete_but_current_missing = setup_synopsis_forecast( @@ -1039,7 +1115,7 @@ def test_process_opportunity_summary_invalid_value_errors( source_values, expected_error, ): - opportunity = OpportunityFactory.create( + opportunity = f.OpportunityFactory.create( no_current_summary=True, opportunity_assistance_listings=[] ) source_summary = setup_synopsis_forecast( @@ -1064,55 +1140,237 @@ def transform_oracle_data_task( return TransformOracleDataTask(db_session) def test_process_applicant_types(self, db_session, transform_oracle_data_task): - opportunity_summary_forecast = OpportunitySummaryFactory.create(is_forecast=True, revision_number=None, no_link_values=True) - forecast_insert1 = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="00") - forecast_update1 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="01", applicant_type=ApplicantType.COUNTY_GOVERNMENTS) - forecast_update2 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="02", applicant_type=ApplicantType.CITY_OR_TOWNSHIP_GOVERNMENTS) - forecast_delete1 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="04", applicant_type=ApplicantType.SPECIAL_DISTRICT_GOVERNMENTS) - forecast_delete2 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="05", applicant_type=ApplicantType.INDEPENDENT_SCHOOL_DISTRICTS) - forecast_update_already_processed = setup_applicant_type(create_existing=True, is_already_processed=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="06", applicant_type=ApplicantType.PUBLIC_AND_STATE_INSTITUTIONS_OF_HIGHER_EDUCATION) - - opportunity_summary_forecast_hist = OpportunitySummaryFactory.create(is_forecast=True, revision_number=3, no_link_values=True) - forecast_hist_insert1 = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="07") - forecast_hist_update1 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="08", applicant_type=ApplicantType.PUBLIC_AND_INDIAN_HOUSING_AUTHORITIES) - forecast_hist_update2 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="11", applicant_type=ApplicantType.OTHER_NATIVE_AMERICAN_TRIBAL_ORGANIZATIONS) - forecast_hist_delete1 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="12", applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITH_501C3) - forecast_hist_delete_already_processed = setup_applicant_type(create_existing=False, is_delete=True, is_already_processed=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="13") - - opportunity_summary_syn = OpportunitySummaryFactory.create(is_forecast=False, revision_number=None, no_link_values=True) - syn_insert1 = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="20") - syn_insert2 = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="21") - syn_update1 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="22", applicant_type=ApplicantType.FOR_PROFIT_ORGANIZATIONS_OTHER_THAN_SMALL_BUSINESSES) - syn_update2 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="23", applicant_type=ApplicantType.SMALL_BUSINESSES) - syn_delete1 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="25", applicant_type=ApplicantType.OTHER) - syn_delete2 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="99", applicant_type=ApplicantType.UNRESTRICTED) - syn_delete_but_current_missing = setup_applicant_type(create_existing=False, is_delete=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="07") - syn_update_already_processed = setup_applicant_type(create_existing=True, is_already_processed=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="08", applicant_type=ApplicantType.PUBLIC_AND_INDIAN_HOUSING_AUTHORITIES) - - opportunity_summary_syn_hist = OpportunitySummaryFactory.create(is_forecast=False, revision_number=21, no_link_values=True) - syn_hist_insert1 = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="11") - syn_hist_update1 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="12", applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITH_501C3) - syn_hist_update2 = setup_applicant_type(create_existing=True, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="13", applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITHOUT_501C3) - syn_hist_delete1 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="25", applicant_type=ApplicantType.OTHER) - syn_hist_delete2 = setup_applicant_type(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="99", applicant_type=ApplicantType.UNRESTRICTED) - syn_hist_insert_invalid_type = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="X", applicant_type=ApplicantType.STATE_GOVERNMENTS) + opportunity_summary_forecast = f.OpportunitySummaryFactory.create( + is_forecast=True, revision_number=None, no_link_values=True + ) + forecast_insert1 = setup_applicant_type( + create_existing=False, + opportunity_summary=opportunity_summary_forecast, + legacy_lookup_value="00", + ) + forecast_update1 = setup_applicant_type( + create_existing=True, + opportunity_summary=opportunity_summary_forecast, + legacy_lookup_value="01", + applicant_type=ApplicantType.COUNTY_GOVERNMENTS, + ) + forecast_update2 = setup_applicant_type( + create_existing=True, + opportunity_summary=opportunity_summary_forecast, + legacy_lookup_value="02", + applicant_type=ApplicantType.CITY_OR_TOWNSHIP_GOVERNMENTS, + ) + forecast_delete1 = setup_applicant_type( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_forecast, + legacy_lookup_value="04", + applicant_type=ApplicantType.SPECIAL_DISTRICT_GOVERNMENTS, + ) + forecast_delete2 = setup_applicant_type( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_forecast, + legacy_lookup_value="05", + applicant_type=ApplicantType.INDEPENDENT_SCHOOL_DISTRICTS, + ) + forecast_update_already_processed = setup_applicant_type( + create_existing=True, + is_already_processed=True, + opportunity_summary=opportunity_summary_forecast, + legacy_lookup_value="06", + applicant_type=ApplicantType.PUBLIC_AND_STATE_INSTITUTIONS_OF_HIGHER_EDUCATION, + ) + + opportunity_summary_forecast_hist = f.OpportunitySummaryFactory.create( + is_forecast=True, revision_number=3, no_link_values=True + ) + forecast_hist_insert1 = setup_applicant_type( + create_existing=False, + opportunity_summary=opportunity_summary_forecast_hist, + legacy_lookup_value="07", + ) + forecast_hist_update1 = setup_applicant_type( + create_existing=True, + opportunity_summary=opportunity_summary_forecast_hist, + legacy_lookup_value="08", + applicant_type=ApplicantType.PUBLIC_AND_INDIAN_HOUSING_AUTHORITIES, + ) + forecast_hist_update2 = setup_applicant_type( + create_existing=True, + opportunity_summary=opportunity_summary_forecast_hist, + legacy_lookup_value="11", + applicant_type=ApplicantType.OTHER_NATIVE_AMERICAN_TRIBAL_ORGANIZATIONS, + ) + forecast_hist_delete1 = setup_applicant_type( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_forecast_hist, + legacy_lookup_value="12", + applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITH_501C3, + ) + forecast_hist_delete_already_processed = setup_applicant_type( + create_existing=False, + is_delete=True, + is_already_processed=True, + opportunity_summary=opportunity_summary_forecast_hist, + legacy_lookup_value="13", + ) + + opportunity_summary_syn = f.OpportunitySummaryFactory.create( + is_forecast=False, revision_number=None, no_link_values=True + ) + syn_insert1 = setup_applicant_type( + create_existing=False, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="20", + ) + syn_insert2 = setup_applicant_type( + create_existing=False, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="21", + ) + syn_update1 = setup_applicant_type( + create_existing=True, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="22", + applicant_type=ApplicantType.FOR_PROFIT_ORGANIZATIONS_OTHER_THAN_SMALL_BUSINESSES, + ) + syn_update2 = setup_applicant_type( + create_existing=True, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="23", + applicant_type=ApplicantType.SMALL_BUSINESSES, + ) + syn_delete1 = setup_applicant_type( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="25", + applicant_type=ApplicantType.OTHER, + ) + syn_delete2 = setup_applicant_type( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="99", + applicant_type=ApplicantType.UNRESTRICTED, + ) + syn_delete_but_current_missing = setup_applicant_type( + create_existing=False, + is_delete=True, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="07", + ) + syn_update_already_processed = setup_applicant_type( + create_existing=True, + is_already_processed=True, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="08", + applicant_type=ApplicantType.PUBLIC_AND_INDIAN_HOUSING_AUTHORITIES, + ) + + opportunity_summary_syn_hist = f.OpportunitySummaryFactory.create( + is_forecast=False, revision_number=21, no_link_values=True + ) + syn_hist_insert1 = setup_applicant_type( + create_existing=False, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="11", + ) + syn_hist_update1 = setup_applicant_type( + create_existing=True, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="12", + applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITH_501C3, + ) + syn_hist_update2 = setup_applicant_type( + create_existing=True, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="13", + applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITHOUT_501C3, + ) + syn_hist_delete1 = setup_applicant_type( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="25", + applicant_type=ApplicantType.OTHER, + ) + syn_hist_delete2 = setup_applicant_type( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="99", + applicant_type=ApplicantType.UNRESTRICTED, + ) + syn_hist_insert_invalid_type = setup_applicant_type( + create_existing=False, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="X", + applicant_type=ApplicantType.STATE_GOVERNMENTS, + ) transform_oracle_data_task.process_link_applicant_types() - validate_applicant_type(db_session, forecast_insert1, expected_applicant_type=ApplicantType.STATE_GOVERNMENTS) - validate_applicant_type(db_session, forecast_hist_insert1, expected_applicant_type=ApplicantType.FEDERALLY_RECOGNIZED_NATIVE_AMERICAN_TRIBAL_GOVERNMENTS) - validate_applicant_type(db_session, syn_insert1, expected_applicant_type=ApplicantType.PRIVATE_INSTITUTIONS_OF_HIGHER_EDUCATION) - validate_applicant_type(db_session, syn_insert2, expected_applicant_type=ApplicantType.INDIVIDUALS) - validate_applicant_type(db_session, syn_hist_insert1, expected_applicant_type=ApplicantType.OTHER_NATIVE_AMERICAN_TRIBAL_ORGANIZATIONS) - - validate_applicant_type(db_session, forecast_update1, expected_applicant_type=ApplicantType.COUNTY_GOVERNMENTS) - validate_applicant_type(db_session, forecast_update2, expected_applicant_type=ApplicantType.CITY_OR_TOWNSHIP_GOVERNMENTS) - validate_applicant_type(db_session, forecast_hist_update1, expected_applicant_type=ApplicantType.PUBLIC_AND_INDIAN_HOUSING_AUTHORITIES) - validate_applicant_type(db_session, forecast_hist_update2, expected_applicant_type=ApplicantType.OTHER_NATIVE_AMERICAN_TRIBAL_ORGANIZATIONS) - validate_applicant_type(db_session, syn_update1, expected_applicant_type=ApplicantType.FOR_PROFIT_ORGANIZATIONS_OTHER_THAN_SMALL_BUSINESSES) - validate_applicant_type(db_session, syn_update2, expected_applicant_type=ApplicantType.SMALL_BUSINESSES) - validate_applicant_type(db_session, syn_hist_update1, expected_applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITH_501C3) - validate_applicant_type(db_session, syn_hist_update2, expected_applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITHOUT_501C3) + validate_applicant_type( + db_session, forecast_insert1, expected_applicant_type=ApplicantType.STATE_GOVERNMENTS + ) + validate_applicant_type( + db_session, + forecast_hist_insert1, + expected_applicant_type=ApplicantType.FEDERALLY_RECOGNIZED_NATIVE_AMERICAN_TRIBAL_GOVERNMENTS, + ) + validate_applicant_type( + db_session, + syn_insert1, + expected_applicant_type=ApplicantType.PRIVATE_INSTITUTIONS_OF_HIGHER_EDUCATION, + ) + validate_applicant_type( + db_session, syn_insert2, expected_applicant_type=ApplicantType.INDIVIDUALS + ) + validate_applicant_type( + db_session, + syn_hist_insert1, + expected_applicant_type=ApplicantType.OTHER_NATIVE_AMERICAN_TRIBAL_ORGANIZATIONS, + ) + + validate_applicant_type( + db_session, forecast_update1, expected_applicant_type=ApplicantType.COUNTY_GOVERNMENTS + ) + validate_applicant_type( + db_session, + forecast_update2, + expected_applicant_type=ApplicantType.CITY_OR_TOWNSHIP_GOVERNMENTS, + ) + validate_applicant_type( + db_session, + forecast_hist_update1, + expected_applicant_type=ApplicantType.PUBLIC_AND_INDIAN_HOUSING_AUTHORITIES, + ) + validate_applicant_type( + db_session, + forecast_hist_update2, + expected_applicant_type=ApplicantType.OTHER_NATIVE_AMERICAN_TRIBAL_ORGANIZATIONS, + ) + validate_applicant_type( + db_session, + syn_update1, + expected_applicant_type=ApplicantType.FOR_PROFIT_ORGANIZATIONS_OTHER_THAN_SMALL_BUSINESSES, + ) + validate_applicant_type( + db_session, syn_update2, expected_applicant_type=ApplicantType.SMALL_BUSINESSES + ) + validate_applicant_type( + db_session, + syn_hist_update1, + expected_applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITH_501C3, + ) + validate_applicant_type( + db_session, + syn_hist_update2, + expected_applicant_type=ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITHOUT_501C3, + ) validate_applicant_type(db_session, forecast_delete1, expect_in_db=False) validate_applicant_type(db_session, forecast_delete2, expect_in_db=False) @@ -1122,12 +1380,28 @@ def test_process_applicant_types(self, db_session, transform_oracle_data_task): validate_applicant_type(db_session, syn_hist_delete1, expect_in_db=False) validate_applicant_type(db_session, syn_hist_delete2, expect_in_db=False) - validate_applicant_type(db_session, forecast_update_already_processed, expected_applicant_type=ApplicantType.PUBLIC_AND_STATE_INSTITUTIONS_OF_HIGHER_EDUCATION, expect_values_to_match=False) - validate_applicant_type(db_session, forecast_hist_delete_already_processed, expect_in_db=False) - validate_applicant_type(db_session, syn_update_already_processed, expected_applicant_type=ApplicantType.PUBLIC_AND_INDIAN_HOUSING_AUTHORITIES, expect_values_to_match=False) + validate_applicant_type( + db_session, + forecast_update_already_processed, + expected_applicant_type=ApplicantType.PUBLIC_AND_STATE_INSTITUTIONS_OF_HIGHER_EDUCATION, + expect_values_to_match=False, + ) + validate_applicant_type( + db_session, forecast_hist_delete_already_processed, expect_in_db=False + ) + validate_applicant_type( + db_session, + syn_update_already_processed, + expected_applicant_type=ApplicantType.PUBLIC_AND_INDIAN_HOUSING_AUTHORITIES, + expect_values_to_match=False, + ) - validate_applicant_type(db_session, syn_delete_but_current_missing, expect_in_db=False, was_processed=False) - validate_applicant_type(db_session, syn_hist_insert_invalid_type, expect_in_db=False, was_processed=False) + validate_applicant_type( + db_session, syn_delete_but_current_missing, expect_in_db=False, was_processed=False + ) + validate_applicant_type( + db_session, syn_hist_insert_invalid_type, expect_in_db=False, was_processed=False + ) metrics = transform_oracle_data_task.metrics assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED] == 22 @@ -1147,22 +1421,49 @@ def test_process_applicant_types(self, db_session, transform_oracle_data_task): @pytest.mark.parametrize( "is_forecast,revision_number", [(True, None), (False, None), (True, 5), (False, 10)] ) - def test_process_applicant_types_but_current_missing(self, db_session, transform_oracle_data_task, is_forecast, revision_number): - opportunity_summary = OpportunitySummaryFactory.create(is_forecast=is_forecast, revision_number=revision_number, no_link_values=True) - delete_but_current_missing = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary, legacy_lookup_value="00", is_delete=True) + def test_process_applicant_types_but_current_missing( + self, db_session, transform_oracle_data_task, is_forecast, revision_number + ): + opportunity_summary = f.OpportunitySummaryFactory.create( + is_forecast=is_forecast, revision_number=revision_number, no_link_values=True + ) + delete_but_current_missing = setup_applicant_type( + create_existing=False, + opportunity_summary=opportunity_summary, + legacy_lookup_value="00", + is_delete=True, + ) with pytest.raises(ValueError, match="Cannot delete applicant type as it does not exist"): - transform_oracle_data_task.process_link_applicant_type(delete_but_current_missing, None, opportunity_summary) + transform_oracle_data_task.process_link_applicant_type( + delete_but_current_missing, None, opportunity_summary + ) @pytest.mark.parametrize( - "is_forecast,revision_number,legacy_lookup_value", [(True, None, "90"), (False, None, "xx"), (True, 5, "50"), (False, 10, "1")] + "is_forecast,revision_number,legacy_lookup_value", + [(True, None, "90"), (False, None, "xx"), (True, 5, "50"), (False, 10, "1")], ) - def test_process_applicant_types_but_invalid_lookup_value(self, db_session, transform_oracle_data_task, is_forecast, revision_number, legacy_lookup_value): - opportunity_summary = OpportunitySummaryFactory.create(is_forecast=is_forecast, revision_number=revision_number, no_link_values=True) - insert_but_invalid_value = setup_applicant_type(create_existing=False, opportunity_summary=opportunity_summary, legacy_lookup_value=legacy_lookup_value) + def test_process_applicant_types_but_invalid_lookup_value( + self, + db_session, + transform_oracle_data_task, + is_forecast, + revision_number, + legacy_lookup_value, + ): + opportunity_summary = f.OpportunitySummaryFactory.create( + is_forecast=is_forecast, revision_number=revision_number, no_link_values=True + ) + insert_but_invalid_value = setup_applicant_type( + create_existing=False, + opportunity_summary=opportunity_summary, + legacy_lookup_value=legacy_lookup_value, + ) with pytest.raises(ValueError, match="Unrecognized applicant type"): - transform_oracle_data_task.process_link_applicant_type(insert_but_invalid_value, None, opportunity_summary) + transform_oracle_data_task.process_link_applicant_type( + insert_but_invalid_value, None, opportunity_summary + ) class TestTransformFundingInstrument(BaseTestClass): @@ -1173,30 +1474,570 @@ def transform_oracle_data_task( return TransformOracleDataTask(db_session) def test_process_funding_instruments(self, db_session, transform_oracle_data_task): - opportunity_summary_forecast = OpportunitySummaryFactory.create(is_forecast=True, revision_number=None, no_link_values=True) - forecast_insert1 = setup_funding_instrument(create_existing=False, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="CA") - forecast_update1 = setup_funding_instrument(create_existing=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="G", funding_instrument=FundingInstrument.GRANT) - forecast_delete1 = setup_funding_instrument(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="PC", funding_instrument=FundingInstrument.PROCUREMENT_CONTRACT) - forecast_update_already_processed = setup_funding_instrument(create_existing=True, is_already_processed=True, opportunity_summary=opportunity_summary_forecast, legacy_lookup_value="O", funding_instrument=FundingInstrument.OTHER) - - opportunity_summary_forecast_hist = OpportunitySummaryFactory.create(is_forecast=True, revision_number=3, no_link_values=True) - forecast_hist_insert1 = setup_funding_instrument(create_existing=False, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="G") - forecast_hist_delete1 = setup_funding_instrument(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="CA", funding_instrument=FundingInstrument.COOPERATIVE_AGREEMENT) - forecast_hist_delete_already_processed = setup_funding_instrument(create_existing=False, is_delete=True, is_already_processed=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="O") - syn_delete_but_current_missing = setup_funding_instrument(create_existing=False, is_delete=True, opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="PC") - - - opportunity_summary_syn = OpportunitySummaryFactory.create(is_forecast=False, revision_number=None, no_link_values=True) - syn_insert1 = setup_funding_instrument(create_existing=False, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="20") - syn_insert2 = setup_funding_instrument(create_existing=False, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="21") - syn_delete1 = setup_funding_instrument(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="25", funding_instrument=FundingInstrument.GRANT) - syn_update_already_processed = setup_funding_instrument(create_existing=True, is_already_processed=True, opportunity_summary=opportunity_summary_syn, legacy_lookup_value="08", funding_instrument=FundingInstrument.GRANT) - - opportunity_summary_syn_hist = OpportunitySummaryFactory.create(is_forecast=False, revision_number=21, no_link_values=True) - syn_hist_insert1 = setup_funding_instrument(create_existing=False, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="11") - syn_hist_update1 = setup_funding_instrument(create_existing=True, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="12", funding_instrument=FundingInstrument.GRANT) - syn_hist_delete1 = setup_funding_instrument(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="25", funding_instrument=FundingInstrument.GRANT) - syn_hist_delete2 = setup_funding_instrument(create_existing=True, is_delete=True, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="99", funding_instrument=FundingInstrument.GRANT) - syn_hist_insert_invalid_type = setup_funding_instrument(create_existing=False, opportunity_summary=opportunity_summary_syn_hist, legacy_lookup_value="X", funding_instrument=FundingInstrument.GRANT) + opportunity_summary_forecast = f.OpportunitySummaryFactory.create( + is_forecast=True, revision_number=None, no_link_values=True + ) + forecast_insert1 = setup_funding_instrument( + create_existing=False, + opportunity_summary=opportunity_summary_forecast, + legacy_lookup_value="CA", + ) + forecast_update1 = setup_funding_instrument( + create_existing=True, + opportunity_summary=opportunity_summary_forecast, + legacy_lookup_value="G", + funding_instrument=FundingInstrument.GRANT, + ) + forecast_delete1 = setup_funding_instrument( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_forecast, + legacy_lookup_value="PC", + funding_instrument=FundingInstrument.PROCUREMENT_CONTRACT, + ) + forecast_update_already_processed = setup_funding_instrument( + create_existing=True, + is_already_processed=True, + opportunity_summary=opportunity_summary_forecast, + legacy_lookup_value="O", + funding_instrument=FundingInstrument.OTHER, + ) + + opportunity_summary_forecast_hist = f.OpportunitySummaryFactory.create( + is_forecast=True, revision_number=3, no_link_values=True + ) + forecast_hist_insert1 = setup_funding_instrument( + create_existing=False, + opportunity_summary=opportunity_summary_forecast_hist, + legacy_lookup_value="G", + ) + forecast_hist_delete1 = setup_funding_instrument( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_forecast_hist, + legacy_lookup_value="CA", + funding_instrument=FundingInstrument.COOPERATIVE_AGREEMENT, + ) + forecast_hist_delete_already_processed = setup_funding_instrument( + create_existing=False, + is_delete=True, + is_already_processed=True, + opportunity_summary=opportunity_summary_forecast_hist, + legacy_lookup_value="O", + ) + syn_delete_but_current_missing = setup_funding_instrument( + create_existing=False, + is_delete=True, + opportunity_summary=opportunity_summary_forecast_hist, + legacy_lookup_value="PC", + ) + + opportunity_summary_syn = f.OpportunitySummaryFactory.create( + is_forecast=False, revision_number=None, no_link_values=True + ) + syn_insert1 = setup_funding_instrument( + create_existing=False, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="O", + ) + syn_insert2 = setup_funding_instrument( + create_existing=False, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="G", + ) + syn_delete1 = setup_funding_instrument( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="CA", + funding_instrument=FundingInstrument.COOPERATIVE_AGREEMENT, + ) + syn_update_already_processed = setup_funding_instrument( + create_existing=True, + is_already_processed=True, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="PC", + funding_instrument=FundingInstrument.PROCUREMENT_CONTRACT, + ) + + opportunity_summary_syn_hist = f.OpportunitySummaryFactory.create( + is_forecast=False, revision_number=21, no_link_values=True + ) + syn_hist_insert1 = setup_funding_instrument( + create_existing=False, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="CA", + ) + syn_hist_update1 = setup_funding_instrument( + create_existing=True, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="O", + funding_instrument=FundingInstrument.OTHER, + ) + syn_hist_delete1 = setup_funding_instrument( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="PC", + funding_instrument=FundingInstrument.PROCUREMENT_CONTRACT, + ) + syn_hist_delete2 = setup_funding_instrument( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="G", + funding_instrument=FundingInstrument.GRANT, + ) + syn_hist_insert_invalid_type = setup_funding_instrument( + create_existing=False, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="X", + ) transform_oracle_data_task.process_link_funding_instruments() + + validate_funding_instrument( + db_session, + forecast_insert1, + expected_funding_instrument=FundingInstrument.COOPERATIVE_AGREEMENT, + ) + validate_funding_instrument( + db_session, forecast_hist_insert1, expected_funding_instrument=FundingInstrument.GRANT + ) + validate_funding_instrument( + db_session, syn_insert1, expected_funding_instrument=FundingInstrument.OTHER + ) + validate_funding_instrument( + db_session, syn_insert2, expected_funding_instrument=FundingInstrument.GRANT + ) + validate_funding_instrument( + db_session, + syn_hist_insert1, + expected_funding_instrument=FundingInstrument.COOPERATIVE_AGREEMENT, + ) + + validate_funding_instrument( + db_session, forecast_update1, expected_funding_instrument=FundingInstrument.GRANT + ) + validate_funding_instrument( + db_session, syn_hist_update1, expected_funding_instrument=FundingInstrument.OTHER + ) + + validate_funding_instrument(db_session, forecast_delete1, expect_in_db=False) + validate_funding_instrument(db_session, forecast_hist_delete1, expect_in_db=False) + validate_funding_instrument(db_session, syn_delete1, expect_in_db=False) + validate_funding_instrument(db_session, syn_hist_delete1, expect_in_db=False) + validate_funding_instrument(db_session, syn_hist_delete2, expect_in_db=False) + + validate_funding_instrument( + db_session, + forecast_update_already_processed, + expected_funding_instrument=FundingInstrument.OTHER, + expect_values_to_match=False, + ) + validate_funding_instrument( + db_session, forecast_hist_delete_already_processed, expect_in_db=False + ) + validate_funding_instrument( + db_session, + syn_update_already_processed, + expected_funding_instrument=FundingInstrument.PROCUREMENT_CONTRACT, + expect_values_to_match=False, + ) + + validate_funding_instrument( + db_session, syn_delete_but_current_missing, expect_in_db=False, was_processed=False + ) + validate_funding_instrument( + db_session, syn_hist_insert_invalid_type, expect_in_db=False, was_processed=False + ) + + metrics = transform_oracle_data_task.metrics + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED] == 14 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_DELETED] == 5 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_INSERTED] == 5 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_UPDATED] == 2 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_ERROR_COUNT] == 2 + + # Rerunning will only attempt to re-process the errors, so total+errors goes up by 2 + transform_oracle_data_task.process_link_funding_instruments() + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED] == 16 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_DELETED] == 5 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_INSERTED] == 5 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_UPDATED] == 2 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_ERROR_COUNT] == 4 + + @pytest.mark.parametrize( + "is_forecast,revision_number", [(True, None), (False, None), (True, 1), (False, 4)] + ) + def test_process_funding_instrument_but_current_missing( + self, db_session, transform_oracle_data_task, is_forecast, revision_number + ): + opportunity_summary = f.OpportunitySummaryFactory.create( + is_forecast=is_forecast, revision_number=revision_number, no_link_values=True + ) + delete_but_current_missing = setup_funding_instrument( + create_existing=False, + opportunity_summary=opportunity_summary, + legacy_lookup_value="G", + is_delete=True, + ) + + with pytest.raises( + ValueError, match="Cannot delete funding instrument as it does not exist" + ): + transform_oracle_data_task.process_link_funding_instrument( + delete_but_current_missing, None, opportunity_summary + ) + + @pytest.mark.parametrize( + "is_forecast,revision_number,legacy_lookup_value", + [(True, None, "X"), (False, None, "4"), (True, 5, "Y"), (False, 10, "A")], + ) + def test_process_applicant_types_but_invalid_lookup_value( + self, + db_session, + transform_oracle_data_task, + is_forecast, + revision_number, + legacy_lookup_value, + ): + opportunity_summary = f.OpportunitySummaryFactory.create( + is_forecast=is_forecast, revision_number=revision_number, no_link_values=True + ) + insert_but_invalid_value = setup_funding_instrument( + create_existing=False, + opportunity_summary=opportunity_summary, + legacy_lookup_value=legacy_lookup_value, + ) + + with pytest.raises(ValueError, match="Unrecognized funding instrument"): + transform_oracle_data_task.process_link_funding_instrument( + insert_but_invalid_value, None, opportunity_summary + ) + + +class TestTransformFundingCategory(BaseTestClass): + @pytest.fixture() + def transform_oracle_data_task( + self, db_session, enable_factory_create, truncate_opportunities + ) -> TransformOracleDataTask: + return TransformOracleDataTask(db_session) + + def test_process_funding_categories(self, db_session, transform_oracle_data_task): + opportunity_summary_forecast = f.OpportunitySummaryFactory.create( + is_forecast=True, revision_number=None, no_link_values=True + ) + forecast_insert1 = setup_funding_category( + create_existing=False, + opportunity_summary=opportunity_summary_forecast, + legacy_lookup_value="RA", + ) + forecast_insert2 = setup_funding_category( + create_existing=False, + opportunity_summary=opportunity_summary_forecast, + legacy_lookup_value="AG", + ) + forecast_update1 = setup_funding_category( + create_existing=True, + opportunity_summary=opportunity_summary_forecast, + legacy_lookup_value="AR", + funding_category=FundingCategory.ARTS, + ) + forecast_delete1 = setup_funding_category( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_forecast, + legacy_lookup_value="BC", + funding_category=FundingCategory.BUSINESS_AND_COMMERCE, + ) + forecast_delete2 = setup_funding_category( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_forecast, + legacy_lookup_value="CD", + funding_category=FundingCategory.COMMUNITY_DEVELOPMENT, + ) + forecast_update_already_processed = setup_funding_category( + create_existing=True, + is_already_processed=True, + opportunity_summary=opportunity_summary_forecast, + legacy_lookup_value="CP", + funding_category=FundingCategory.CONSUMER_PROTECTION, + ) + + opportunity_summary_forecast_hist = f.OpportunitySummaryFactory.create( + is_forecast=True, revision_number=3, no_link_values=True + ) + forecast_hist_insert1 = setup_funding_category( + create_existing=False, + opportunity_summary=opportunity_summary_forecast_hist, + legacy_lookup_value="DPR", + ) + forecast_hist_insert2 = setup_funding_category( + create_existing=False, + opportunity_summary=opportunity_summary_forecast_hist, + legacy_lookup_value="ED", + ) + forecast_hist_update1 = setup_funding_category( + create_existing=True, + opportunity_summary=opportunity_summary_forecast_hist, + legacy_lookup_value="ELT", + funding_category=FundingCategory.EMPLOYMENT_LABOR_AND_TRAINING, + ) + forecast_hist_delete1 = setup_funding_category( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_forecast_hist, + legacy_lookup_value="EN", + funding_category=FundingCategory.ENERGY, + ) + forecast_hist_delete_already_processed = setup_funding_category( + create_existing=False, + is_delete=True, + is_already_processed=True, + opportunity_summary=opportunity_summary_forecast_hist, + legacy_lookup_value="ENV", + ) + + opportunity_summary_syn = f.OpportunitySummaryFactory.create( + is_forecast=False, revision_number=None, no_link_values=True + ) + syn_insert1 = setup_funding_category( + create_existing=False, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="FN", + ) + syn_insert2 = setup_funding_category( + create_existing=False, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="HL", + ) + syn_update1 = setup_funding_category( + create_existing=True, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="HO", + funding_category=FundingCategory.HOUSING, + ) + syn_delete1 = setup_funding_category( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="HU", + funding_category=FundingCategory.HUMANITIES, + ) + syn_delete2 = setup_funding_category( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="IIJ", + funding_category=FundingCategory.INFRASTRUCTURE_INVESTMENT_AND_JOBS_ACT, + ) + syn_delete_but_current_missing = setup_funding_category( + create_existing=False, + is_delete=True, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="IS", + ) + syn_update_already_processed = setup_funding_category( + create_existing=True, + is_already_processed=True, + opportunity_summary=opportunity_summary_syn, + legacy_lookup_value="ISS", + funding_category=FundingCategory.INCOME_SECURITY_AND_SOCIAL_SERVICES, + ) + + opportunity_summary_syn_hist = f.OpportunitySummaryFactory.create( + is_forecast=False, revision_number=21, no_link_values=True + ) + syn_hist_insert1 = setup_funding_category( + create_existing=False, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="LJL", + ) + syn_hist_insert2 = setup_funding_category( + create_existing=False, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="NR", + ) + syn_hist_insert3 = setup_funding_category( + create_existing=False, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="OZ", + ) + syn_hist_update1 = setup_funding_category( + create_existing=True, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="RD", + funding_category=FundingCategory.REGIONAL_DEVELOPMENT, + ) + + syn_hist_delete1 = setup_funding_category( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="ST", + funding_category=FundingCategory.SCIENCE_TECHNOLOGY_AND_OTHER_RESEARCH_AND_DEVELOPMENT, + ) + syn_hist_delete2 = setup_funding_category( + create_existing=True, + is_delete=True, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="T", + funding_category=FundingCategory.TRANSPORTATION, + ) + syn_hist_insert_invalid_type = setup_funding_category( + create_existing=False, + opportunity_summary=opportunity_summary_syn_hist, + legacy_lookup_value="XYZ", + funding_category=FundingCategory.HEALTH, + ) + + transform_oracle_data_task.process_link_funding_categories() + print(transform_oracle_data_task.metrics) + + validate_funding_category( + db_session, forecast_insert1, expected_funding_category=FundingCategory.RECOVERY_ACT + ) + validate_funding_category( + db_session, forecast_insert2, expected_funding_category=FundingCategory.AGRICULTURE + ) + validate_funding_category( + db_session, + forecast_hist_insert1, + expected_funding_category=FundingCategory.DISASTER_PREVENTION_AND_RELIEF, + ) + validate_funding_category( + db_session, forecast_hist_insert2, expected_funding_category=FundingCategory.EDUCATION + ) + validate_funding_category( + db_session, syn_insert1, expected_funding_category=FundingCategory.FOOD_AND_NUTRITION + ) + validate_funding_category( + db_session, syn_insert2, expected_funding_category=FundingCategory.HEALTH + ) + validate_funding_category( + db_session, + syn_hist_insert1, + expected_funding_category=FundingCategory.LAW_JUSTICE_AND_LEGAL_SERVICES, + ) + validate_funding_category( + db_session, + syn_hist_insert2, + expected_funding_category=FundingCategory.NATURAL_RESOURCES, + ) + validate_funding_category( + db_session, + syn_hist_insert3, + expected_funding_category=FundingCategory.OPPORTUNITY_ZONE_BENEFITS, + ) + + validate_funding_category( + db_session, forecast_update1, expected_funding_category=FundingCategory.ARTS + ) + validate_funding_category( + db_session, + forecast_hist_update1, + expected_funding_category=FundingCategory.EMPLOYMENT_LABOR_AND_TRAINING, + ) + validate_funding_category( + db_session, syn_update1, expected_funding_category=FundingCategory.HOUSING + ) + validate_funding_category( + db_session, + syn_hist_update1, + expected_funding_category=FundingCategory.REGIONAL_DEVELOPMENT, + ) + + validate_funding_category(db_session, forecast_delete1, expect_in_db=False) + validate_funding_category(db_session, forecast_delete2, expect_in_db=False) + validate_funding_category(db_session, forecast_hist_delete1, expect_in_db=False) + validate_funding_category(db_session, syn_delete1, expect_in_db=False) + validate_funding_category(db_session, syn_delete2, expect_in_db=False) + validate_funding_category(db_session, syn_hist_delete1, expect_in_db=False) + validate_funding_category(db_session, syn_hist_delete2, expect_in_db=False) + + validate_funding_category( + db_session, + forecast_update_already_processed, + expected_funding_category=FundingCategory.CONSUMER_PROTECTION, + expect_values_to_match=False, + ) + validate_funding_category( + db_session, forecast_hist_delete_already_processed, expect_in_db=False + ) + validate_funding_category( + db_session, + syn_update_already_processed, + expected_funding_category=FundingCategory.INCOME_SECURITY_AND_SOCIAL_SERVICES, + expect_values_to_match=False, + ) + + validate_funding_category( + db_session, syn_delete_but_current_missing, expect_in_db=False, was_processed=False + ) + validate_funding_category( + db_session, syn_hist_insert_invalid_type, expect_in_db=False, was_processed=False + ) + + metrics = transform_oracle_data_task.metrics + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED] == 22 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_DELETED] == 7 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_INSERTED] == 9 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_UPDATED] == 4 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_ERROR_COUNT] == 2 + + # Rerunning will only attempt to re-process the errors, so total+errors goes up by 2 + transform_oracle_data_task.process_link_funding_categories() + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED] == 24 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_DELETED] == 7 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_INSERTED] == 9 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_UPDATED] == 4 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_ERROR_COUNT] == 4 + + @pytest.mark.parametrize( + "is_forecast,revision_number", [(True, None), (False, None), (True, 1), (False, 70)] + ) + def test_process_applicant_types_but_current_missing( + self, db_session, transform_oracle_data_task, is_forecast, revision_number + ): + opportunity_summary = f.OpportunitySummaryFactory.create( + is_forecast=is_forecast, revision_number=revision_number, no_link_values=True + ) + delete_but_current_missing = setup_funding_category( + create_existing=False, + opportunity_summary=opportunity_summary, + legacy_lookup_value="00", + is_delete=True, + ) + + with pytest.raises(ValueError, match="Cannot delete funding category as it does not exist"): + transform_oracle_data_task.process_link_funding_category( + delete_but_current_missing, None, opportunity_summary + ) + + @pytest.mark.parametrize( + "is_forecast,revision_number,legacy_lookup_value", + [(True, None, "ab"), (False, None, "cd"), (True, 5, "ef"), (False, 10, "Ag")], + ) + def test_process_applicant_types_but_invalid_lookup_value( + self, + db_session, + transform_oracle_data_task, + is_forecast, + revision_number, + legacy_lookup_value, + ): + opportunity_summary = f.OpportunitySummaryFactory.create( + is_forecast=is_forecast, revision_number=revision_number, no_link_values=True + ) + insert_but_invalid_value = setup_funding_category( + create_existing=False, + opportunity_summary=opportunity_summary, + legacy_lookup_value=legacy_lookup_value, + ) + + with pytest.raises(ValueError, match="Unrecognized funding category"): + transform_oracle_data_task.process_link_funding_category( + insert_but_invalid_value, None, opportunity_summary + ) diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index 62028157c..2aa6fffaf 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -885,10 +885,56 @@ class Params: # TODO - move these to the top? -APPLICANT_TYPE_IDS = ["00", "01", "02", "04", "05", "06", "07", "08", "11", "12", "13", "20", "21", "22", "23", "25", "99"] -FUNDING_CATEGORY_IDS = ["RA", "AG", "AR", "BC", "CD", "CP", "DPR", "ED", "ELT", "EN", "ENV", "FN", "HL", "HO", "HU", "IIJ", "IS", "ISS", "LJL", "NR", "OZ", "RD", "ST", "T", "ACA", "O"] +APPLICANT_TYPE_IDS = [ + "00", + "01", + "02", + "04", + "05", + "06", + "07", + "08", + "11", + "12", + "13", + "20", + "21", + "22", + "23", + "25", + "99", +] +FUNDING_CATEGORY_IDS = [ + "RA", + "AG", + "AR", + "BC", + "CD", + "CP", + "DPR", + "ED", + "ELT", + "EN", + "ENV", + "FN", + "HL", + "HO", + "HU", + "IIJ", + "IS", + "ISS", + "LJL", + "NR", + "OZ", + "RD", + "ST", + "T", + "ACA", + "O", +] FUNDING_INSTRUMENT_IDS = ["CA", "G", "PC", "O"] + class StagingTapplicanttypesForecastFactory(BaseFactory): class Meta: model = staging.forecast.TapplicanttypesForecast @@ -917,6 +963,7 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) + class StagingTapplicanttypesForecastHistFactory(StagingTapplicanttypesForecastFactory): class Meta: model = staging.forecast.TapplicanttypesForecastHist @@ -930,6 +977,7 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) + class StagingTapplicanttypesSynopsisFactory(BaseFactory): class Meta: model = staging.synopsis.TapplicanttypesSynopsis @@ -958,6 +1006,7 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) + class StagingTapplicanttypesSynopsisHistFactory(StagingTapplicanttypesSynopsisFactory): class Meta: model = staging.synopsis.TapplicanttypesSynopsisHist @@ -1000,6 +1049,7 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) + class StagingTfundactcatForecastHistFactory(StagingTfundactcatForecastFactory): class Meta: model = staging.forecast.TfundactcatForecastHist @@ -1013,6 +1063,7 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) + class StagingTfundactcatSynopsisFactory(BaseFactory): class Meta: model = staging.synopsis.TfundactcatSynopsis @@ -1041,6 +1092,7 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) + class StagingTfundactcatSynopsisHistFactory(StagingTfundactcatSynopsisFactory): class Meta: model = staging.synopsis.TfundactcatSynopsisHist @@ -1054,6 +1106,7 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) + class StagingTfundinstrForecastFactory(BaseFactory): class Meta: model = staging.forecast.TfundinstrForecast @@ -1082,6 +1135,7 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) + class StagingTfundinstrForecastHistFactory(StagingTfundinstrForecastFactory): class Meta: model = staging.forecast.TfundinstrForecastHist @@ -1095,6 +1149,7 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) + class StagingTfundinstrSynopsisFactory(BaseFactory): class Meta: model = staging.synopsis.TfundinstrSynopsis @@ -1123,6 +1178,7 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) + class StagingTfundinstrSynopsisHistFactory(StagingTfundinstrSynopsisFactory): class Meta: model = staging.synopsis.TfundinstrSynopsisHist From b11477472645f52380f43ba42dabca95a30e3d2a Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Tue, 7 May 2024 14:56:15 -0400 Subject: [PATCH 26/39] Minor adjustment --- api/tests/src/db/models/factories.py | 112 +++++++++++++-------------- 1 file changed, 55 insertions(+), 57 deletions(-) diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index 2aa6fffaf..1189d30bc 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -627,6 +627,55 @@ class Meta: # Staging Table Factories #################################### +LEGACY_APPLICANT_TYPE_IDS = [ + "00", + "01", + "02", + "04", + "05", + "06", + "07", + "08", + "11", + "12", + "13", + "20", + "21", + "22", + "23", + "25", + "99", +] +LEGACY_FUNDING_CATEGORY_IDS = [ + "RA", + "AG", + "AR", + "BC", + "CD", + "CP", + "DPR", + "ED", + "ELT", + "EN", + "ENV", + "FN", + "HL", + "HO", + "HU", + "IIJ", + "IS", + "ISS", + "LJL", + "NR", + "OZ", + "RD", + "ST", + "T", + "ACA", + "O", +] +LEGACY_FUNDING_INSTRUMENT_IDS = ["CA", "G", "PC", "O"] + class StagingTopportunityFactory(BaseFactory): class Meta: @@ -884,64 +933,13 @@ class Params: ) -# TODO - move these to the top? -APPLICANT_TYPE_IDS = [ - "00", - "01", - "02", - "04", - "05", - "06", - "07", - "08", - "11", - "12", - "13", - "20", - "21", - "22", - "23", - "25", - "99", -] -FUNDING_CATEGORY_IDS = [ - "RA", - "AG", - "AR", - "BC", - "CD", - "CP", - "DPR", - "ED", - "ELT", - "EN", - "ENV", - "FN", - "HL", - "HO", - "HU", - "IIJ", - "IS", - "ISS", - "LJL", - "NR", - "OZ", - "RD", - "ST", - "T", - "ACA", - "O", -] -FUNDING_INSTRUMENT_IDS = ["CA", "G", "PC", "O"] - - class StagingTapplicanttypesForecastFactory(BaseFactory): class Meta: model = staging.forecast.TapplicanttypesForecast at_frcst_id = factory.Sequence(lambda n: n) - at_id = factory.Iterator(APPLICANT_TYPE_IDS) + at_id = factory.Iterator(LEGACY_APPLICANT_TYPE_IDS) forecast = factory.SubFactory(StagingTforecastFactory) opportunity_id = factory.LazyAttribute(lambda s: s.forecast.opportunity_id) @@ -984,7 +982,7 @@ class Meta: at_syn_id = factory.Sequence(lambda n: n) - at_id = factory.Iterator(APPLICANT_TYPE_IDS) + at_id = factory.Iterator(LEGACY_APPLICANT_TYPE_IDS) synopsis = factory.SubFactory(StagingTsynopsisFactory) opportunity_id = factory.LazyAttribute(lambda s: s.synopsis.opportunity_id) @@ -1027,7 +1025,7 @@ class Meta: fac_frcst_id = factory.Sequence(lambda n: n) - fac_id = factory.Iterator(FUNDING_CATEGORY_IDS) + fac_id = factory.Iterator(LEGACY_FUNDING_CATEGORY_IDS) forecast = factory.SubFactory(StagingTforecastFactory) opportunity_id = factory.LazyAttribute(lambda s: s.forecast.opportunity_id) @@ -1070,7 +1068,7 @@ class Meta: fac_syn_id = factory.Sequence(lambda n: n) - fac_id = factory.Iterator(FUNDING_CATEGORY_IDS) + fac_id = factory.Iterator(LEGACY_FUNDING_CATEGORY_IDS) synopsis = factory.SubFactory(StagingTsynopsisFactory) opportunity_id = factory.LazyAttribute(lambda s: s.synopsis.opportunity_id) @@ -1113,7 +1111,7 @@ class Meta: fi_frcst_id = factory.Sequence(lambda n: n) - fi_id = factory.Iterator(FUNDING_INSTRUMENT_IDS) + fi_id = factory.Iterator(LEGACY_FUNDING_INSTRUMENT_IDS) forecast = factory.SubFactory(StagingTforecastFactory) opportunity_id = factory.LazyAttribute(lambda s: s.forecast.opportunity_id) @@ -1156,7 +1154,7 @@ class Meta: fi_syn_id = factory.Sequence(lambda n: n) - fi_id = factory.Iterator(FUNDING_INSTRUMENT_IDS) + fi_id = factory.Iterator(LEGACY_FUNDING_INSTRUMENT_IDS) synopsis = factory.SubFactory(StagingTsynopsisFactory) opportunity_id = factory.LazyAttribute(lambda s: s.synopsis.opportunity_id) From 870189e15bac63a67ae7c0f46bfad3dbaba5c36c Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Tue, 7 May 2024 16:11:12 -0400 Subject: [PATCH 27/39] A few final fixes --- .../transform_oracle_data_task.py | 20 +++++++++++-------- ...dd_unique_constraint_link_table_legacy_.py | 1 - ...k_opportunity_summary_funding_category.csv | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/api/src/data_migration/transformation/transform_oracle_data_task.py b/api/src/data_migration/transformation/transform_oracle_data_task.py index 4fb8b0a2a..3131b52e4 100644 --- a/api/src/data_migration/transformation/transform_oracle_data_task.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -195,12 +195,13 @@ def process_opportunity( transformed_opportunity = transform_util.transform_opportunity( source_opportunity, target_opportunity ) - self.db_session.merge(transformed_opportunity) if is_insert: self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + self.db_session.add(transformed_opportunity) else: self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) + self.db_session.merge(transformed_opportunity) logger.info("Processed opportunity", extra=extra) source_opportunity.transformed_at = self.transform_time @@ -278,12 +279,13 @@ def process_assistance_listing( transformed_assistance_listing = transform_util.transform_assistance_listing( source_assistance_listing, target_assistance_listing ) - self.db_session.merge(transformed_assistance_listing) if is_insert: self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + self.db_session.add(transformed_assistance_listing) else: self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) + self.db_session.merge(transformed_assistance_listing) logger.info("Processed assistance listing", extra=extra) source_assistance_listing.transformed_at = self.transform_time @@ -386,12 +388,13 @@ def process_opportunity_summary( transformed_opportunity_summary = transform_util.transform_opportunity_summary( source_summary, target_summary ) - self.db_session.merge(transformed_opportunity_summary) if is_insert: self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + self.db_session.add(transformed_opportunity_summary) else: self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) + self.db_session.merge(transformed_opportunity_summary) logger.info("Processed opportunity summary", extra=extra) source_summary.transformed_at = self.transform_time @@ -514,12 +517,13 @@ def process_link_applicant_type( transformed_applicant_type = transform_util.convert_opportunity_summary_applicant_type( source_applicant_type, target_applicant_type, opportunity_summary ) - self.db_session.merge(transformed_applicant_type) if is_insert: self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + self.db_session.add(transformed_applicant_type) else: self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) + self.db_session.merge(transformed_applicant_type) logger.info("Processed applicant type", extra=extra) source_applicant_type.transformed_at = self.transform_time @@ -639,12 +643,12 @@ def process_link_funding_category( source_funding_category, target_funding_category, opportunity_summary ) ) - self.db_session.merge(transformed_funding_category) - if is_insert: self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + self.db_session.add(transformed_funding_category) else: self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) + self.db_session.merge(transformed_funding_category) logger.info("Processed funding category", extra=extra) source_funding_category.transformed_at = self.transform_time @@ -766,12 +770,12 @@ def process_link_funding_instrument( source_funding_instrument, target_funding_instrument, opportunity_summary ) ) - self.db_session.merge(transformed_funding_instrument) - if is_insert: self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + self.db_session.add(transformed_funding_instrument) else: self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) + self.db_session.merge(transformed_funding_instrument) logger.info("Processed funding instrument", extra=extra) source_funding_instrument.transformed_at = self.transform_time diff --git a/api/src/db/migrations/versions/2024_05_07_add_unique_constraint_link_table_legacy_.py b/api/src/db/migrations/versions/2024_05_07_add_unique_constraint_link_table_legacy_.py index 06e25b87c..30310476f 100644 --- a/api/src/db/migrations/versions/2024_05_07_add_unique_constraint_link_table_legacy_.py +++ b/api/src/db/migrations/versions/2024_05_07_add_unique_constraint_link_table_legacy_.py @@ -5,7 +5,6 @@ Create Date: 2024-05-07 14:41:19.401963 """ -import sqlalchemy as sa from alembic import op # revision identifiers, used by Alembic. diff --git a/api/tests/src/task/opportunities/test_files/link_opportunity_summary_funding_category.csv b/api/tests/src/task/opportunities/test_files/link_opportunity_summary_funding_category.csv index f4870ba50..88c8248e5 100644 --- a/api/tests/src/task/opportunities/test_files/link_opportunity_summary_funding_category.csv +++ b/api/tests/src/task/opportunities/test_files/link_opportunity_summary_funding_category.csv @@ -3,4 +3,4 @@ "2","18","1841","","" "3","13","1842","","" "3","18","1843","","" -"3","16","1843","","" \ No newline at end of file +"3","16","1844","","" \ No newline at end of file From 979fc7aa1e9629286601bada37d3becc87ee9918 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Thu, 9 May 2024 13:51:18 -0400 Subject: [PATCH 28/39] Fix issues regarding transforms of one-to-many tables, cleanup --- .../transform_oracle_data_task.py | 123 +++- .../transformation/transform_util.py | 34 +- api/src/db/models/staging/forecast.py | 60 ++ api/src/db/models/staging/synopsis.py | 60 ++ .../test_transform_oracle_data_task.py | 544 +++++++++++++++--- 5 files changed, 696 insertions(+), 125 deletions(-) diff --git a/api/src/data_migration/transformation/transform_oracle_data_task.py b/api/src/data_migration/transformation/transform_oracle_data_task.py index 3131b52e4..e7f2af4ca 100644 --- a/api/src/data_migration/transformation/transform_oracle_data_task.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -1,9 +1,10 @@ import logging from datetime import datetime from enum import StrEnum -from typing import Sequence, Tuple, Type, TypeVar, cast +from typing import Any, Sequence, Tuple, Type, TypeVar, cast from sqlalchemy import and_, select +from sqlalchemy.orm import selectinload from src.adapters import db from src.data_migration.transformation import transform_util @@ -56,6 +57,7 @@ class Metrics(StrEnum): TOTAL_RECORDS_INSERTED = "total_records_inserted" TOTAL_RECORDS_UPDATED = "total_records_updated" TOTAL_RECORDS_ORPHANED = "total_records_orphaned" + TOTAL_DUPLICATE_RECORDS_SKIPPED = "total_duplicate_records_skipped" TOTAL_ERROR_COUNT = "total_error_count" @@ -78,7 +80,9 @@ def run_task(self) -> None: self.process_opportunity_summaries() # One-to-many lookups - self.process_one_to_many_lookup_tables() + self.process_link_applicant_types() + self.process_link_funding_categories() + self.process_link_funding_instruments() def fetch( self, source_model: Type[S], destination_model: Type[D], join_clause: Sequence @@ -125,6 +129,7 @@ def fetch_with_opportunity_summary( join_clause: Sequence, is_forecast: bool, is_historical_table: bool, + relationship_load_value: Any, ) -> list[Tuple[S, D | None, OpportunitySummary | None]]: # setup the join clause for getting the opportunity summary @@ -147,7 +152,8 @@ def fetch_with_opportunity_summary( .join(OpportunitySummary, and_(*opportunity_summary_join_clause), isouter=True) .join(destination_model, and_(*join_clause), isouter=True) .where(source_model.transformed_at.is_(None)) - .execution_options(yield_per=5000) + .options(selectinload(relationship_load_value)) + .execution_options(yield_per=5000, populate_existing=True) ), ) @@ -399,15 +405,13 @@ def process_opportunity_summary( logger.info("Processed opportunity summary", extra=extra) source_summary.transformed_at = self.transform_time - def process_one_to_many_lookup_tables(self) -> None: - self.process_link_applicant_types() - self.process_link_funding_categories() - self.process_link_funding_categories() - def process_link_applicant_types(self) -> None: + link_table = LinkOpportunitySummaryApplicantType + relationship_load_value = OpportunitySummary.link_applicant_types + forecast_applicant_type_records = self.fetch_with_opportunity_summary( TapplicanttypesForecast, - LinkOpportunitySummaryApplicantType, + link_table, [ TapplicanttypesForecast.at_frcst_id == LinkOpportunitySummaryApplicantType.legacy_applicant_type_id, @@ -416,12 +420,13 @@ def process_link_applicant_types(self) -> None: ], is_forecast=True, is_historical_table=False, + relationship_load_value=relationship_load_value, ) self.process_link_applicant_types_group(forecast_applicant_type_records) forecast_applicant_type_hist_records = self.fetch_with_opportunity_summary( TapplicanttypesForecastHist, - LinkOpportunitySummaryApplicantType, + link_table, [ TapplicanttypesForecastHist.at_frcst_id == LinkOpportunitySummaryApplicantType.legacy_applicant_type_id, @@ -430,12 +435,13 @@ def process_link_applicant_types(self) -> None: ], is_forecast=True, is_historical_table=True, + relationship_load_value=relationship_load_value, ) self.process_link_applicant_types_group(forecast_applicant_type_hist_records) synopsis_applicant_type_records = self.fetch_with_opportunity_summary( TapplicanttypesSynopsis, - LinkOpportunitySummaryApplicantType, + link_table, [ TapplicanttypesSynopsis.at_syn_id == LinkOpportunitySummaryApplicantType.legacy_applicant_type_id, @@ -444,12 +450,13 @@ def process_link_applicant_types(self) -> None: ], is_forecast=False, is_historical_table=False, + relationship_load_value=relationship_load_value, ) self.process_link_applicant_types_group(synopsis_applicant_type_records) synopsis_applicant_type_hist_records = self.fetch_with_opportunity_summary( TapplicanttypesSynopsisHist, - LinkOpportunitySummaryApplicantType, + link_table, [ TapplicanttypesSynopsisHist.at_syn_id == LinkOpportunitySummaryApplicantType.legacy_applicant_type_id, @@ -458,6 +465,7 @@ def process_link_applicant_types(self) -> None: ], is_forecast=False, is_historical_table=True, + relationship_load_value=relationship_load_value, ) self.process_link_applicant_types_group(synopsis_applicant_type_hist_records) @@ -519,8 +527,21 @@ def process_link_applicant_type( ) if is_insert: - self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) - self.db_session.add(transformed_applicant_type) + # Before we insert, we have to still be certain we're not adding a duplicate record + # because the primary key of the legacy tables is the legacy ID + lookup value + opportunity ID + # its possible for the same lookup value to appear multiple times because the legacy ID is different + # This would hit a conflict in our DBs primary key, so we need to verify that won't happen + if transformed_applicant_type.applicant_type in opportunity_summary.applicant_types: + self.increment(self.Metrics.TOTAL_DUPLICATE_RECORDS_SKIPPED) + logger.warning( + "Skipping applicant type record", + extra=extra | {"applicant_type": transformed_applicant_type.applicant_type}, + ) + else: + self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + # We append to the relationship so SQLAlchemy immediately attaches it to its cached + # opportunity summary object so that the above check works when we receive dupes in the same batch + opportunity_summary.link_applicant_types.append(transformed_applicant_type) else: self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) self.db_session.merge(transformed_applicant_type) @@ -529,9 +550,12 @@ def process_link_applicant_type( source_applicant_type.transformed_at = self.transform_time def process_link_funding_categories(self) -> None: + link_table = LinkOpportunitySummaryFundingCategory + relationship_load_value = OpportunitySummary.link_funding_categories + forecast_funding_category_records = self.fetch_with_opportunity_summary( TfundactcatForecast, - LinkOpportunitySummaryFundingCategory, + link_table, [ TfundactcatForecast.fac_frcst_id == LinkOpportunitySummaryFundingCategory.legacy_funding_category_id, @@ -540,12 +564,13 @@ def process_link_funding_categories(self) -> None: ], is_forecast=True, is_historical_table=False, + relationship_load_value=relationship_load_value, ) self.process_link_funding_categories_group(forecast_funding_category_records) forecast_funding_category_hist_records = self.fetch_with_opportunity_summary( TfundactcatForecastHist, - LinkOpportunitySummaryFundingCategory, + link_table, [ TfundactcatForecastHist.fac_frcst_id == LinkOpportunitySummaryFundingCategory.legacy_funding_category_id, @@ -554,12 +579,13 @@ def process_link_funding_categories(self) -> None: ], is_forecast=True, is_historical_table=True, + relationship_load_value=relationship_load_value, ) self.process_link_funding_categories_group(forecast_funding_category_hist_records) synopsis_funding_category_records = self.fetch_with_opportunity_summary( TfundactcatSynopsis, - LinkOpportunitySummaryFundingCategory, + link_table, [ TfundactcatSynopsis.fac_syn_id == LinkOpportunitySummaryFundingCategory.legacy_funding_category_id, @@ -568,12 +594,13 @@ def process_link_funding_categories(self) -> None: ], is_forecast=False, is_historical_table=False, + relationship_load_value=relationship_load_value, ) self.process_link_funding_categories_group(synopsis_funding_category_records) synopsis_funding_category_hist_records = self.fetch_with_opportunity_summary( TfundactcatSynopsisHist, - LinkOpportunitySummaryFundingCategory, + link_table, [ TfundactcatSynopsisHist.fac_syn_id == LinkOpportunitySummaryFundingCategory.legacy_funding_category_id, @@ -582,6 +609,7 @@ def process_link_funding_categories(self) -> None: ], is_forecast=False, is_historical_table=True, + relationship_load_value=relationship_load_value, ) self.process_link_funding_categories_group(synopsis_funding_category_hist_records) @@ -644,8 +672,25 @@ def process_link_funding_category( ) ) if is_insert: - self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) - self.db_session.add(transformed_funding_category) + # Before we insert, we have to still be certain we're not adding a duplicate record + # because the primary key of the legacy tables is the legacy ID + lookup value + opportunity ID + # its possible for the same lookup value to appear multiple times because the legacy ID is different + # This would hit a conflict in our DBs primary key, so we need to verify that won't happen + if ( + transformed_funding_category.funding_category + in opportunity_summary.funding_categories + ): + self.increment(self.Metrics.TOTAL_DUPLICATE_RECORDS_SKIPPED) + logger.warning( + "Skipping funding category record", + extra=extra + | {"funding_category": transformed_funding_category.funding_category}, + ) + else: + self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + # We append to the relationship so SQLAlchemy immediately attaches it to its cached + # opportunity summary object so that the above check works when we receive dupes in the same batch + opportunity_summary.link_funding_categories.append(transformed_funding_category) else: self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) self.db_session.merge(transformed_funding_category) @@ -654,9 +699,12 @@ def process_link_funding_category( source_funding_category.transformed_at = self.transform_time def process_link_funding_instruments(self) -> None: + link_table = LinkOpportunitySummaryFundingInstrument + relationship_load_value = OpportunitySummary.link_funding_instruments + forecast_funding_instrument_records = self.fetch_with_opportunity_summary( TfundinstrForecast, - LinkOpportunitySummaryFundingInstrument, + link_table, [ TfundinstrForecast.fi_frcst_id == LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id, @@ -665,12 +713,13 @@ def process_link_funding_instruments(self) -> None: ], is_forecast=True, is_historical_table=False, + relationship_load_value=relationship_load_value, ) self.process_link_funding_instruments_group(forecast_funding_instrument_records) forecast_funding_instrument_hist_records = self.fetch_with_opportunity_summary( TfundinstrForecastHist, - LinkOpportunitySummaryFundingInstrument, + link_table, [ TfundinstrForecastHist.fi_frcst_id == LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id, @@ -679,12 +728,13 @@ def process_link_funding_instruments(self) -> None: ], is_forecast=True, is_historical_table=True, + relationship_load_value=relationship_load_value, ) self.process_link_funding_instruments_group(forecast_funding_instrument_hist_records) synopsis_funding_instrument_records = self.fetch_with_opportunity_summary( TfundinstrSynopsis, - LinkOpportunitySummaryFundingInstrument, + link_table, [ TfundinstrSynopsis.fi_syn_id == LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id, @@ -693,12 +743,13 @@ def process_link_funding_instruments(self) -> None: ], is_forecast=False, is_historical_table=False, + relationship_load_value=relationship_load_value, ) self.process_link_funding_instruments_group(synopsis_funding_instrument_records) synopsis_funding_instrument_hist_records = self.fetch_with_opportunity_summary( TfundinstrSynopsisHist, - LinkOpportunitySummaryFundingInstrument, + link_table, [ TfundinstrSynopsisHist.fi_syn_id == LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id, @@ -707,6 +758,7 @@ def process_link_funding_instruments(self) -> None: ], is_forecast=False, is_historical_table=True, + relationship_load_value=relationship_load_value, ) self.process_link_funding_instruments_group(synopsis_funding_instrument_hist_records) @@ -771,8 +823,27 @@ def process_link_funding_instrument( ) ) if is_insert: - self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) - self.db_session.add(transformed_funding_instrument) + # Before we insert, we have to still be certain we're not adding a duplicate record + # because the primary key of the legacy tables is the legacy ID + lookup value + opportunity ID + # its possible for the same lookup value to appear multiple times because the legacy ID is different + # This would hit a conflict in our DBs primary key, so we need to verify that won't happen + if ( + transformed_funding_instrument.funding_instrument + in opportunity_summary.funding_instruments + ): + self.increment(self.Metrics.TOTAL_DUPLICATE_RECORDS_SKIPPED) + logger.warning( + "Skipping funding instrument record", + extra=extra + | {"funding_instrument": transformed_funding_instrument.funding_instrument}, + ) + else: + self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + # We append to the relationship so SQLAlchemy immediately attaches it to its cached + # opportunity summary object so that the above check works when we receive dupes in the same batch + opportunity_summary.link_funding_instruments.append( + transformed_funding_instrument + ) else: self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) self.db_session.merge(transformed_funding_instrument) diff --git a/api/src/data_migration/transformation/transform_util.py b/api/src/data_migration/transformation/transform_util.py index 8ba6759df..aac9008a4 100644 --- a/api/src/data_migration/transformation/transform_util.py +++ b/api/src/data_migration/transformation/transform_util.py @@ -16,15 +16,7 @@ OpportunityAssistanceListing, OpportunitySummary, ) -from src.db.models.staging.forecast import ( - TapplicanttypesForecast, - TapplicanttypesForecastHist, - TforecastHist, - TfundactcatForecast, - TfundactcatForecastHist, - TfundinstrForecast, - TfundinstrForecastHist, -) +from src.db.models.staging.forecast import TforecastHist from src.db.models.staging.opportunity import Topportunity, TopportunityCfda from src.db.models.staging.staging_base import StagingBase from src.db.models.staging.synopsis import Tsynopsis, TsynopsisHist @@ -301,15 +293,9 @@ def convert_opportunity_summary_applicant_type( applicant_type = transform_applicant_type(source_applicant_type.at_id) - # The legacy ID is named differently in the forecast/synopsis tables - if isinstance(source_applicant_type, (TapplicanttypesForecast, TapplicanttypesForecastHist)): - legacy_applicant_type_id = source_applicant_type.at_frcst_id - else: - legacy_applicant_type_id = source_applicant_type.at_syn_id - target_applicant_type = LinkOpportunitySummaryApplicantType( opportunity_summary_id=opportunity_summary.opportunity_summary_id, - legacy_applicant_type_id=legacy_applicant_type_id, + legacy_applicant_type_id=source_applicant_type.legacy_applicant_type_id, applicant_type=applicant_type, updated_by=source_applicant_type.last_upd_id, created_by=source_applicant_type.creator_id, @@ -337,15 +323,9 @@ def convert_opportunity_summary_funding_instrument( funding_instrument = transform_funding_instrument(source_funding_instrument.fi_id) - # The legacy ID is named differently in the forecast/synopsis tables - if isinstance(source_funding_instrument, (TfundinstrForecast, TfundinstrForecastHist)): - legacy_funding_instrument_id = source_funding_instrument.fi_frcst_id - else: - legacy_funding_instrument_id = source_funding_instrument.fi_syn_id - target_funding_instrument = LinkOpportunitySummaryFundingInstrument( opportunity_summary_id=opportunity_summary.opportunity_summary_id, - legacy_funding_instrument_id=legacy_funding_instrument_id, + legacy_funding_instrument_id=source_funding_instrument.legacy_funding_instrument_id, funding_instrument=funding_instrument, updated_by=source_funding_instrument.last_upd_id, created_by=source_funding_instrument.creator_id, @@ -374,15 +354,9 @@ def convert_opportunity_summary_funding_category( funding_category = transform_funding_category(source_funding_category.fac_id) - # The legacy ID is named differently in the forecast/synopsis tables - if isinstance(source_funding_category, (TfundactcatForecast, TfundactcatForecastHist)): - legacy_funding_category_id = source_funding_category.fac_frcst_id - else: - legacy_funding_category_id = source_funding_category.fac_syn_id - target_funding_category = LinkOpportunitySummaryFundingCategory( opportunity_summary_id=opportunity_summary.opportunity_summary_id, - legacy_funding_category_id=legacy_funding_category_id, + legacy_funding_category_id=source_funding_category.legacy_funding_category_id, funding_category=funding_category, updated_by=source_funding_category.last_upd_id, created_by=source_funding_category.creator_id, diff --git a/api/src/db/models/staging/forecast.py b/api/src/db/models/staging/forecast.py index 3604a32f2..b4e372580 100644 --- a/api/src/db/models/staging/forecast.py +++ b/api/src/db/models/staging/forecast.py @@ -48,6 +48,18 @@ class TapplicanttypesForecast( overlaps="forecast", ) + @property + def legacy_applicant_type_id(self) -> int: + return self.at_frcst_id + + @property + def is_forecast(self) -> bool: + return True + + @property + def revision_number(self) -> None: + return None + class TapplicanttypesForecastHist( StagingBase, forecast_mixin.TapplicanttypesForecastHistMixin, StagingParamMixin @@ -61,6 +73,14 @@ class TapplicanttypesForecastHist( overlaps="forecast", ) + @property + def legacy_applicant_type_id(self) -> int: + return self.at_frcst_id + + @property + def is_forecast(self) -> bool: + return True + class TfundactcatForecast(StagingBase, forecast_mixin.TfundactcatForecastMixin, StagingParamMixin): __tablename__ = "tfundactcat_forecast" @@ -72,6 +92,18 @@ class TfundactcatForecast(StagingBase, forecast_mixin.TfundactcatForecastMixin, overlaps="forecast", ) + @property + def legacy_funding_category_id(self) -> int: + return self.fac_frcst_id + + @property + def is_forecast(self) -> bool: + return True + + @property + def revision_number(self) -> None: + return None + class TfundactcatForecastHist( StagingBase, forecast_mixin.TfundactcatForecastHistMixin, StagingParamMixin @@ -85,6 +117,14 @@ class TfundactcatForecastHist( overlaps="forecast", ) + @property + def legacy_funding_category_id(self) -> int: + return self.fac_frcst_id + + @property + def is_forecast(self) -> bool: + return True + class TfundinstrForecast(StagingBase, forecast_mixin.TfundinstrForecastMixin, StagingParamMixin): __tablename__ = "tfundinstr_forecast" @@ -96,6 +136,18 @@ class TfundinstrForecast(StagingBase, forecast_mixin.TfundinstrForecastMixin, St overlaps="forecast", ) + @property + def legacy_funding_instrument_id(self) -> int: + return self.fi_frcst_id + + @property + def is_forecast(self) -> bool: + return True + + @property + def revision_number(self) -> None: + return None + class TfundinstrForecastHist( StagingBase, forecast_mixin.TfundinstrForecastHistMixin, StagingParamMixin @@ -108,3 +160,11 @@ class TfundinstrForecastHist( uselist=False, overlaps="forecast", ) + + @property + def legacy_funding_instrument_id(self) -> int: + return self.fi_frcst_id + + @property + def is_forecast(self) -> bool: + return True diff --git a/api/src/db/models/staging/synopsis.py b/api/src/db/models/staging/synopsis.py index a70fe6bc3..857ccb07a 100644 --- a/api/src/db/models/staging/synopsis.py +++ b/api/src/db/models/staging/synopsis.py @@ -48,6 +48,18 @@ class TapplicanttypesSynopsis( overlaps="synopsis", ) + @property + def legacy_applicant_type_id(self) -> int: + return self.at_syn_id + + @property + def is_forecast(self) -> bool: + return False + + @property + def revision_number(self) -> None: + return None + class TapplicanttypesSynopsisHist( StagingBase, synopsis_mixin.TapplicanttypesSynopsisHistMixin, StagingParamMixin @@ -61,6 +73,14 @@ class TapplicanttypesSynopsisHist( overlaps="synopsis", ) + @property + def legacy_applicant_type_id(self) -> int: + return self.at_syn_id + + @property + def is_forecast(self) -> bool: + return False + class TfundactcatSynopsis(StagingBase, synopsis_mixin.TfundactcatSynopsisMixin, StagingParamMixin): __tablename__ = "tfundactcat_synopsis" @@ -72,6 +92,18 @@ class TfundactcatSynopsis(StagingBase, synopsis_mixin.TfundactcatSynopsisMixin, overlaps="synopsis", ) + @property + def legacy_funding_category_id(self) -> int: + return self.fac_syn_id + + @property + def is_forecast(self) -> bool: + return False + + @property + def revision_number(self) -> None: + return None + class TfundactcatSynopsisHist( StagingBase, synopsis_mixin.TfundactcatSynopsisHistMixin, StagingParamMixin @@ -85,6 +117,14 @@ class TfundactcatSynopsisHist( overlaps="synopsis", ) + @property + def legacy_funding_category_id(self) -> int: + return self.fac_syn_id + + @property + def is_forecast(self) -> bool: + return False + class TfundinstrSynopsis(StagingBase, synopsis_mixin.TfundinstrSynopsisMixin, StagingParamMixin): __tablename__ = "tfundinstr_synopsis" @@ -96,6 +136,18 @@ class TfundinstrSynopsis(StagingBase, synopsis_mixin.TfundinstrSynopsisMixin, St overlaps="synopsis", ) + @property + def legacy_funding_instrument_id(self) -> int: + return self.fi_syn_id + + @property + def is_forecast(self) -> bool: + return False + + @property + def revision_number(self) -> None: + return None + class TfundinstrSynopsisHist( StagingBase, synopsis_mixin.TfundinstrSynopsisHistMixin, StagingParamMixin @@ -108,3 +160,11 @@ class TfundinstrSynopsisHist( uselist=False, overlaps="synopsis", ) + + @property + def legacy_funding_instrument_id(self) -> int: + return self.fi_syn_id + + @property + def is_forecast(self) -> bool: + return False diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py index 6cc316483..4affc58b0 100644 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -389,9 +389,7 @@ def validate_assistance_listing( ) -def validate_opportunity_summary( - db_session, source_summary, expect_in_db: bool = True, expect_values_to_match: bool = True -): +def get_summary_from_source(db_session, source_summary): revision_number = None is_forecast = source_summary.is_forecast if isinstance(source_summary, (staging.synopsis.TsynopsisHist, staging.forecast.TforecastHist)): @@ -403,10 +401,20 @@ def validate_opportunity_summary( OpportunitySummary.opportunity_id == source_summary.opportunity_id, OpportunitySummary.revision_number == revision_number, OpportunitySummary.is_forecast == is_forecast, + # Populate existing to force it to fetch updates from the DB ) + .execution_options(populate_existing=True) .one_or_none() ) + return opportunity_summary + + +def validate_opportunity_summary( + db_session, source_summary, expect_in_db: bool = True, expect_values_to_match: bool = True +): + opportunity_summary = get_summary_from_source(db_session, source_summary) + if not expect_in_db: assert opportunity_summary is None return @@ -471,6 +479,27 @@ def validate_opportunity_summary( assert opportunity_summary.is_deleted == is_deleted +def validate_summary_and_nested( + db_session, + source_summary, + expected_applicant_types: list[ApplicantType], + expected_funding_categories: list[FundingCategory], + expected_funding_instruments: list[FundingInstrument], + expect_in_db: bool = True, + expect_values_to_match: bool = True, +): + validate_opportunity_summary(db_session, source_summary, expect_in_db, expect_values_to_match) + + if not expect_in_db: + return + + created_record = get_summary_from_source(db_session, source_summary) + + assert set(created_record.applicant_types) == set(expected_applicant_types) + assert set(created_record.funding_categories) == set(expected_funding_categories) + assert set(created_record.funding_instruments) == set(expected_funding_instruments) + + def validate_applicant_type( db_session, source_applicant_type, @@ -481,33 +510,13 @@ def validate_applicant_type( ): assert (source_applicant_type.transformed_at is not None) == was_processed - if isinstance( - source_applicant_type, - (staging.synopsis.TapplicanttypesSynopsis, staging.synopsis.TapplicanttypesSynopsisHist), - ): - is_forecast = False - legacy_id = source_applicant_type.at_syn_id - else: - is_forecast = True - legacy_id = source_applicant_type.at_frcst_id - - revision_number = None - if isinstance( - source_applicant_type, - ( - staging.synopsis.TapplicanttypesSynopsisHist, - staging.forecast.TapplicanttypesForecastHist, - ), - ): - revision_number = source_applicant_type.revision_number - # In order to properly find the link table value, need to first determine # the opportunity summary in a subquery opportunity_summary_id = ( db_session.query(OpportunitySummary.opportunity_summary_id) .filter( - OpportunitySummary.revision_number == revision_number, - OpportunitySummary.is_forecast == is_forecast, + OpportunitySummary.revision_number == source_applicant_type.revision_number, + OpportunitySummary.is_forecast == source_applicant_type.is_forecast, OpportunitySummary.opportunity_id == source_applicant_type.opportunity_id, ) .scalar() @@ -516,7 +525,8 @@ def validate_applicant_type( link_applicant_type = ( db_session.query(LinkOpportunitySummaryApplicantType) .filter( - LinkOpportunitySummaryApplicantType.legacy_applicant_type_id == legacy_id, + LinkOpportunitySummaryApplicantType.legacy_applicant_type_id + == source_applicant_type.legacy_applicant_type_id, LinkOpportunitySummaryApplicantType.opportunity_summary_id == opportunity_summary_id, ) .one_or_none() @@ -547,30 +557,13 @@ def validate_funding_instrument( ): assert (source_funding_instrument.transformed_at is not None) == was_processed - if isinstance( - source_funding_instrument, - (staging.synopsis.TfundinstrSynopsis, staging.synopsis.TfundinstrSynopsisHist), - ): - is_forecast = False - legacy_id = source_funding_instrument.fi_syn_id - else: - is_forecast = True - legacy_id = source_funding_instrument.fi_frcst_id - - revision_number = None - if isinstance( - source_funding_instrument, - (staging.synopsis.TfundinstrSynopsisHist, staging.forecast.TfundinstrForecastHist), - ): - revision_number = source_funding_instrument.revision_number - # In order to properly find the link table value, need to first determine # the opportunity summary in a subquery opportunity_summary_id = ( db_session.query(OpportunitySummary.opportunity_summary_id) .filter( - OpportunitySummary.revision_number == revision_number, - OpportunitySummary.is_forecast == is_forecast, + OpportunitySummary.revision_number == source_funding_instrument.revision_number, + OpportunitySummary.is_forecast == source_funding_instrument.is_forecast, OpportunitySummary.opportunity_id == source_funding_instrument.opportunity_id, ) .scalar() @@ -579,7 +572,8 @@ def validate_funding_instrument( link_funding_instrument = ( db_session.query(LinkOpportunitySummaryFundingInstrument) .filter( - LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id == legacy_id, + LinkOpportunitySummaryFundingInstrument.legacy_funding_instrument_id + == source_funding_instrument.legacy_funding_instrument_id, LinkOpportunitySummaryFundingInstrument.opportunity_summary_id == opportunity_summary_id, ) @@ -611,30 +605,13 @@ def validate_funding_category( ): assert (source_funding_category.transformed_at is not None) == was_processed - if isinstance( - source_funding_category, - (staging.synopsis.TfundactcatSynopsis, staging.synopsis.TfundactcatSynopsisHist), - ): - is_forecast = False - legacy_id = source_funding_category.fac_syn_id - else: - is_forecast = True - legacy_id = source_funding_category.fac_frcst_id - - revision_number = None - if isinstance( - source_funding_category, - (staging.synopsis.TfundactcatSynopsisHist, staging.forecast.TfundactcatForecastHist), - ): - revision_number = source_funding_category.revision_number - # In order to properly find the link table value, need to first determine # the opportunity summary in a subquery opportunity_summary_id = ( db_session.query(OpportunitySummary.opportunity_summary_id) .filter( - OpportunitySummary.revision_number == revision_number, - OpportunitySummary.is_forecast == is_forecast, + OpportunitySummary.revision_number == source_funding_category.revision_number, + OpportunitySummary.is_forecast == source_funding_category.is_forecast, OpportunitySummary.opportunity_id == source_funding_category.opportunity_id, ) .scalar() @@ -643,7 +620,8 @@ def validate_funding_category( link_funding_category = ( db_session.query(LinkOpportunitySummaryFundingCategory) .filter( - LinkOpportunitySummaryFundingCategory.legacy_funding_category_id == legacy_id, + LinkOpportunitySummaryFundingCategory.legacy_funding_category_id + == source_funding_category.legacy_funding_category_id, LinkOpportunitySummaryFundingCategory.opportunity_summary_id == opportunity_summary_id, ) .one_or_none() @@ -1216,6 +1194,11 @@ def test_process_applicant_types(self, db_session, transform_oracle_data_task): opportunity_summary=opportunity_summary_forecast_hist, legacy_lookup_value="13", ) + forecast_hist_duplicate_insert = setup_applicant_type( + create_existing=False, + opportunity_summary=opportunity_summary_forecast_hist, + legacy_lookup_value="08", + ) opportunity_summary_syn = f.OpportunitySummaryFactory.create( is_forecast=False, revision_number=None, no_link_values=True @@ -1403,20 +1386,26 @@ def test_process_applicant_types(self, db_session, transform_oracle_data_task): db_session, syn_hist_insert_invalid_type, expect_in_db=False, was_processed=False ) + validate_applicant_type( + db_session, forecast_hist_duplicate_insert, expect_in_db=False, was_processed=True + ) + metrics = transform_oracle_data_task.metrics - assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED] == 22 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED] == 23 assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_DELETED] == 7 assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_INSERTED] == 5 assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_UPDATED] == 8 assert metrics[transform_oracle_data_task.Metrics.TOTAL_ERROR_COUNT] == 2 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_DUPLICATE_RECORDS_SKIPPED] == 1 # Rerunning will only attempt to re-process the errors, so total+errors goes up by 2 transform_oracle_data_task.process_link_applicant_types() - assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED] == 24 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED] == 25 assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_DELETED] == 7 assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_INSERTED] == 5 assert metrics[transform_oracle_data_task.Metrics.TOTAL_RECORDS_UPDATED] == 8 assert metrics[transform_oracle_data_task.Metrics.TOTAL_ERROR_COUNT] == 4 + assert metrics[transform_oracle_data_task.Metrics.TOTAL_DUPLICATE_RECORDS_SKIPPED] == 1 @pytest.mark.parametrize( "is_forecast,revision_number", [(True, None), (False, None), (True, 5), (False, 10)] @@ -1894,7 +1883,6 @@ def test_process_funding_categories(self, db_session, transform_oracle_data_task ) transform_oracle_data_task.process_link_funding_categories() - print(transform_oracle_data_task.metrics) validate_funding_category( db_session, forecast_insert1, expected_funding_category=FundingCategory.RECOVERY_ACT @@ -2041,3 +2029,421 @@ def test_process_applicant_types_but_invalid_lookup_value( transform_oracle_data_task.process_link_funding_category( insert_but_invalid_value, None, opportunity_summary ) + + +class TestTransformFullRunTask(BaseTestClass): + # The above tests validated we could run the tests + + @pytest.fixture() + def truncate_all_staging_tables(self, db_session): + # Iterate over all the staging tables and truncate them to avoid + # any collisions with prior test data. There are no foreign keys + # between these tables, so the order doesn't matter here. + for table in staging.metadata.tables.values(): + db_session.query(table).delete() + + @pytest.fixture() + def transform_oracle_data_task( + self, db_session, enable_factory_create, truncate_opportunities, truncate_all_staging_tables + ) -> TransformOracleDataTask: + return TransformOracleDataTask(db_session) + + def test_all_inserts(self, db_session, transform_oracle_data_task): + # Test that we're fully capable of processing inserts across an entire opportunity record + opportunity = setup_opportunity(create_existing=False) + + cfda1 = setup_cfda(create_existing=False, opportunity=opportunity) + cfda2 = setup_cfda(create_existing=False, opportunity=opportunity) + + ### Forecast + forecast = setup_synopsis_forecast( + create_existing=False, is_forecast=True, revision_number=None, opportunity=opportunity + ) + f.StagingTapplicanttypesForecastFactory(forecast=forecast, at_id="01") + # This is a duplicate record (same at_id, but will have a different at_frcst_id), verifying we handle duplicates + f.StagingTapplicanttypesForecastFactory(forecast=forecast, at_id="01") + f.StagingTfundactcatForecastFactory(forecast=forecast, fac_id="RA") + f.StagingTfundactcatForecastFactory(forecast=forecast, fac_id="HO") + # Duplicate here too + f.StagingTfundactcatForecastFactory(forecast=forecast, fac_id="HO") + + f.StagingTfundinstrForecastFactory(forecast=forecast, fi_id="CA") + f.StagingTfundinstrForecastFactory(forecast=forecast, fi_id="G") + # Duplicate here as well + f.StagingTfundinstrForecastFactory(forecast=forecast, fi_id="G") + + ### Forecast Hist 1 (only applicant types) + forecast_hist1 = setup_synopsis_forecast( + create_existing=False, is_forecast=True, revision_number=1, opportunity=opportunity + ) + f.StagingTapplicanttypesForecastHistFactory(forecast=forecast_hist1, at_id="05") + f.StagingTapplicanttypesForecastHistFactory(forecast=forecast_hist1, at_id="06") + f.StagingTapplicanttypesForecastHistFactory(forecast=forecast_hist1, at_id="25") + f.StagingTapplicanttypesForecastHistFactory(forecast=forecast_hist1, at_id="13") + f.StagingTapplicanttypesForecastHistFactory(forecast=forecast_hist1, at_id="11") + + ### Forecast Hist 2 (only funding instrument and funding categories) + forecast_hist2 = setup_synopsis_forecast( + create_existing=False, is_forecast=True, revision_number=2, opportunity=opportunity + ) + f.StagingTfundactcatForecastHistFactory(forecast=forecast_hist2, fac_id="ED") + f.StagingTfundactcatForecastHistFactory(forecast=forecast_hist2, fac_id="HU") + f.StagingTfundactcatForecastHistFactory(forecast=forecast_hist2, fac_id="IIJ") + f.StagingTfundactcatForecastHistFactory(forecast=forecast_hist2, fac_id="T") + f.StagingTfundinstrForecastHistFactory(forecast=forecast_hist2, fi_id="G") + f.StagingTfundinstrForecastHistFactory(forecast=forecast_hist2, fi_id="CA") + f.StagingTfundinstrForecastHistFactory(forecast=forecast_hist2, fi_id="PC") + + ### Synopsis (has some invalid values) + synopsis = setup_synopsis_forecast( + create_existing=False, is_forecast=False, revision_number=None, opportunity=opportunity + ) + f.StagingTapplicanttypesSynopsisFactory(synopsis=synopsis, at_id="06") + f.StagingTapplicanttypesSynopsisFactory(synopsis=synopsis, at_id="07") + f.StagingTapplicanttypesSynopsisFactory(synopsis=synopsis, at_id="11") + # Invalid value + f.StagingTapplicanttypesSynopsisFactory(synopsis=synopsis, at_id="x") + f.StagingTfundactcatSynopsisFactory(synopsis=synopsis, fac_id="ACA") + f.StagingTfundactcatSynopsisFactory(synopsis=synopsis, fac_id="O") + # Invalid value + f.StagingTfundactcatSynopsisFactory(synopsis=synopsis, fac_id="BOB") + f.StagingTfundinstrSynopsisFactory(synopsis=synopsis, fi_id="G") + # Invalid value + f.StagingTfundinstrSynopsisFactory(synopsis=synopsis, fi_id="x") + + # Synopsis Hist (has no link values, is also marked as deleted) + synopsis_hist = setup_synopsis_forecast( + create_existing=False, is_forecast=False, revision_number=5, opportunity=opportunity + ) + + transform_oracle_data_task.run() + + created_opportunity: Opportunity = ( + db_session.query(Opportunity) + .filter(Opportunity.opportunity_id == opportunity.opportunity_id) + .one_or_none() + ) + + # Validate that all of the expected records were created + # not worrying about all of the transforms specifically here, + # just that everything is in place + + assert created_opportunity is not None + validate_opportunity(db_session, opportunity) + assert { + al.opportunity_assistance_listing_id + for al in created_opportunity.opportunity_assistance_listings + } == {cfda1.opp_cfda_id, cfda2.opp_cfda_id} + + assert len(created_opportunity.all_opportunity_summaries) == 5 + + created_forecast = get_summary_from_source(db_session, forecast) + assert created_forecast is not None + validate_summary_and_nested( + db_session, + forecast, + [ApplicantType.COUNTY_GOVERNMENTS], + [FundingCategory.RECOVERY_ACT, FundingCategory.HOUSING], + [FundingInstrument.GRANT, FundingInstrument.COOPERATIVE_AGREEMENT], + ) + validate_summary_and_nested( + db_session, + forecast_hist1, + [ + ApplicantType.OTHER, + ApplicantType.INDEPENDENT_SCHOOL_DISTRICTS, + ApplicantType.PUBLIC_AND_STATE_INSTITUTIONS_OF_HIGHER_EDUCATION, + ApplicantType.NONPROFITS_NON_HIGHER_EDUCATION_WITHOUT_501C3, + ApplicantType.OTHER_NATIVE_AMERICAN_TRIBAL_ORGANIZATIONS, + ], + [], + [], + ) + validate_summary_and_nested( + db_session, + forecast_hist2, + [], + [ + FundingCategory.TRANSPORTATION, + FundingCategory.EDUCATION, + FundingCategory.INFRASTRUCTURE_INVESTMENT_AND_JOBS_ACT, + FundingCategory.HUMANITIES, + ], + [ + FundingInstrument.COOPERATIVE_AGREEMENT, + FundingInstrument.GRANT, + FundingInstrument.PROCUREMENT_CONTRACT, + ], + ) + validate_summary_and_nested( + db_session, + synopsis, + [ + ApplicantType.PUBLIC_AND_STATE_INSTITUTIONS_OF_HIGHER_EDUCATION, + ApplicantType.FEDERALLY_RECOGNIZED_NATIVE_AMERICAN_TRIBAL_GOVERNMENTS, + ApplicantType.OTHER_NATIVE_AMERICAN_TRIBAL_ORGANIZATIONS, + ], + [FundingCategory.AFFORDABLE_CARE_ACT, FundingCategory.OTHER], + [FundingInstrument.GRANT], + ) + validate_summary_and_nested(db_session, synopsis_hist, [], [], []) + + assert { + transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED: 37, + transform_oracle_data_task.Metrics.TOTAL_RECORDS_INSERTED: 31, + transform_oracle_data_task.Metrics.TOTAL_RECORDS_UPDATED: 0, + transform_oracle_data_task.Metrics.TOTAL_RECORDS_DELETED: 0, + transform_oracle_data_task.Metrics.TOTAL_DUPLICATE_RECORDS_SKIPPED: 3, + transform_oracle_data_task.Metrics.TOTAL_RECORDS_ORPHANED: 0, + transform_oracle_data_task.Metrics.TOTAL_ERROR_COUNT: 3, + }.items() <= transform_oracle_data_task.metrics.items() + + def test_mix_of_inserts_updates_deletes(self, db_session, transform_oracle_data_task): + existing_opportunity = f.OpportunityFactory( + no_current_summary=True, opportunity_assistance_listings=[] + ) + opportunity = f.StagingTopportunityFactory( + opportunity_id=existing_opportunity.opportunity_id, cfdas=[] + ) + + cfda_insert = setup_cfda(create_existing=False, opportunity=existing_opportunity) + cfda_update = setup_cfda(create_existing=True, opportunity=existing_opportunity) + setup_cfda(create_existing=True, is_delete=True, opportunity=existing_opportunity) + + ### Forecast (update) + summary_forecast = f.OpportunitySummaryFactory( + is_forecast=True, opportunity=existing_opportunity, no_link_values=True + ) + forecast_update = f.StagingTforecastFactory(opportunity=opportunity) + + ## Forecast applicant type + # insert + f.StagingTapplicanttypesForecastFactory(forecast=forecast_update, at_id="01") + # update + f.StagingTapplicanttypesForecastFactory( + forecast=forecast_update, at_id="02", at_frcst_id=1000 + ) + f.LinkOpportunitySummaryApplicantTypeFactory( + opportunity_summary=summary_forecast, + applicant_type=ApplicantType.CITY_OR_TOWNSHIP_GOVERNMENTS, + legacy_applicant_type_id=1000, + ) + # delete + f.StagingTapplicanttypesForecastFactory( + forecast=forecast_update, at_id="04", at_frcst_id=1001, is_deleted=True + ) + f.LinkOpportunitySummaryApplicantTypeFactory( + opportunity_summary=summary_forecast, + applicant_type=ApplicantType.SPECIAL_DISTRICT_GOVERNMENTS, + legacy_applicant_type_id=1001, + ) + + ## Forecast funding category + # insert + f.StagingTfundactcatForecastFactory(forecast=forecast_update, fac_id="OZ") + # update + f.StagingTfundactcatForecastFactory( + forecast=forecast_update, fac_id="NR", fac_frcst_id=2000 + ) + f.LinkOpportunitySummaryFundingCategoryFactory( + opportunity_summary=summary_forecast, + funding_category=FundingCategory.NATURAL_RESOURCES, + legacy_funding_category_id=2000, + ) + # delete + f.StagingTfundactcatForecastFactory( + forecast=forecast_update, fac_id="ST", fac_frcst_id=2001, is_deleted=True + ) + f.LinkOpportunitySummaryFundingCategoryFactory( + opportunity_summary=summary_forecast, + funding_category=FundingCategory.SCIENCE_TECHNOLOGY_AND_OTHER_RESEARCH_AND_DEVELOPMENT, + legacy_funding_category_id=2001, + ) + + ## Forecast funding instrument + # insert + f.StagingTfundinstrForecastFactory(forecast=forecast_update, fi_id="G") + # update + f.StagingTfundinstrForecastFactory(forecast=forecast_update, fi_id="CA", fi_frcst_id=3000) + f.LinkOpportunitySummaryFundingInstrumentFactory( + opportunity_summary=summary_forecast, + funding_instrument=FundingInstrument.COOPERATIVE_AGREEMENT, + legacy_funding_instrument_id=3000, + ) + # delete + f.StagingTfundinstrForecastFactory( + forecast=forecast_update, fi_id="O", fi_frcst_id=3001, is_deleted=True + ) + f.LinkOpportunitySummaryFundingInstrumentFactory( + opportunity_summary=summary_forecast, + funding_instrument=FundingInstrument.OTHER, + legacy_funding_instrument_id=3001, + ) + + ### Forecast Hist (deleted) + # note that by default the factory creates 1-3 of each link value, those will automatically get deleted uneventfully by cascades + f.OpportunitySummaryFactory( + is_forecast=True, revision_number=1, opportunity=existing_opportunity + ) + forecast_hist_delete = f.StagingTforecastHistFactory( + revision_number=1, is_deleted=True, opportunity=opportunity + ) + + ### Synopsis (not modified as the update was already processed) + summary_synopsis = f.OpportunitySummaryFactory( + is_forecast=False, opportunity=existing_opportunity, no_link_values=True + ) + synopsis_already_processed = f.StagingTsynopsisFactory( + opportunity=opportunity, already_transformed=True + ) + + ## Synopsis applicant type (many duplicates) + # insert + f.StagingTapplicanttypesSynopsisFactory(synopsis=synopsis_already_processed, at_id="99") + f.StagingTapplicanttypesSynopsisFactory(synopsis=synopsis_already_processed, at_id="99") + f.StagingTapplicanttypesSynopsisFactory(synopsis=synopsis_already_processed, at_id="99") + # update + f.StagingTapplicanttypesSynopsisFactory( + synopsis=synopsis_already_processed, at_id="07", at_syn_id=1000 + ) + f.StagingTapplicanttypesSynopsisFactory( + synopsis=synopsis_already_processed, at_id="07", at_syn_id=11000 + ) + f.LinkOpportunitySummaryApplicantTypeFactory( + opportunity_summary=summary_synopsis, + applicant_type=ApplicantType.FEDERALLY_RECOGNIZED_NATIVE_AMERICAN_TRIBAL_GOVERNMENTS, + legacy_applicant_type_id=1000, + ) + # delete + f.StagingTapplicanttypesSynopsisFactory( + synopsis=synopsis_already_processed, at_id="21", at_syn_id=1001, is_deleted=True + ) + # this will actually error because we don't yet handle these dupe deletes + f.StagingTapplicanttypesSynopsisFactory( + synopsis=synopsis_already_processed, at_id="21", at_syn_id=11001, is_deleted=True + ) + f.LinkOpportunitySummaryApplicantTypeFactory( + opportunity_summary=summary_synopsis, + applicant_type=ApplicantType.INDIVIDUALS, + legacy_applicant_type_id=1001, + ) + + ## Synopsis funding category + # insert + f.StagingTfundactcatSynopsisFactory(synopsis=synopsis_already_processed, fac_id="IIJ") + f.StagingTfundactcatSynopsisFactory(synopsis=synopsis_already_processed, fac_id="IIJ") + f.StagingTfundactcatSynopsisFactory(synopsis=synopsis_already_processed, fac_id="IIJ") + f.StagingTfundactcatSynopsisFactory(synopsis=synopsis_already_processed, fac_id="IIJ") + # update + f.StagingTfundactcatSynopsisFactory( + synopsis=synopsis_already_processed, fac_id="FN", fac_syn_id=2000 + ) + f.StagingTfundactcatSynopsisFactory( + synopsis=synopsis_already_processed, fac_id="FN", fac_syn_id=20000 + ) + f.StagingTfundactcatSynopsisFactory( + synopsis=synopsis_already_processed, fac_id="FN", fac_syn_id=21000 + ) + f.StagingTfundactcatSynopsisFactory( + synopsis=synopsis_already_processed, fac_id="FN", fac_syn_id=22000 + ) + f.LinkOpportunitySummaryFundingCategoryFactory( + opportunity_summary=summary_synopsis, + funding_category=FundingCategory.FOOD_AND_NUTRITION, + legacy_funding_category_id=2000, + ) + # delete + f.StagingTfundactcatSynopsisFactory( + synopsis=synopsis_already_processed, fac_id="HL", fac_syn_id=2001, is_deleted=True + ) + f.LinkOpportunitySummaryFundingCategoryFactory( + opportunity_summary=summary_synopsis, + funding_category=FundingCategory.HEALTH, + legacy_funding_category_id=2001, + ) + + ## Synopsis funding instrument + # insert + f.StagingTfundinstrSynopsisFactory(synopsis=synopsis_already_processed, fi_id="PC") + f.StagingTfundinstrSynopsisFactory(synopsis=synopsis_already_processed, fi_id="PC") + f.StagingTfundinstrSynopsisFactory(synopsis=synopsis_already_processed, fi_id="PC") + f.StagingTfundinstrSynopsisFactory(synopsis=synopsis_already_processed, fi_id="PC") + f.StagingTfundinstrSynopsisFactory(synopsis=synopsis_already_processed, fi_id="PC") + # update + f.StagingTfundinstrSynopsisFactory( + synopsis=synopsis_already_processed, fi_id="O", fi_syn_id=3000 + ) + f.StagingTfundinstrSynopsisFactory( + synopsis=synopsis_already_processed, fi_id="O", fi_syn_id=30000 + ) + f.StagingTfundinstrSynopsisFactory( + synopsis=synopsis_already_processed, fi_id="O", fi_syn_id=31000 + ) + f.LinkOpportunitySummaryFundingInstrumentFactory( + opportunity_summary=summary_synopsis, + funding_instrument=FundingInstrument.OTHER, + legacy_funding_instrument_id=3000, + ) + # delete + f.StagingTfundinstrSynopsisFactory( + synopsis=synopsis_already_processed, fi_id="G", fi_syn_id=3001, is_deleted=True + ) + f.LinkOpportunitySummaryFundingInstrumentFactory( + opportunity_summary=summary_synopsis, + funding_instrument=FundingInstrument.GRANT, + legacy_funding_instrument_id=3001, + ) + + ### Synopsis Hist (Insert - no nested values created) + synopsis_hist_insert = f.StagingTsynopsisHistFactory(opportunity=opportunity) + + transform_oracle_data_task.run() + + updated_opportunity: Opportunity = ( + db_session.query(Opportunity) + .filter(Opportunity.opportunity_id == opportunity.opportunity_id) + .one_or_none() + ) + + assert updated_opportunity is not None + validate_opportunity(db_session, opportunity) + assert { + al.opportunity_assistance_listing_id + for al in updated_opportunity.opportunity_assistance_listings + } == {cfda_insert.opp_cfda_id, cfda_update.opp_cfda_id} + + validate_summary_and_nested( + db_session, + forecast_update, + [ApplicantType.COUNTY_GOVERNMENTS, ApplicantType.CITY_OR_TOWNSHIP_GOVERNMENTS], + [FundingCategory.OPPORTUNITY_ZONE_BENEFITS, FundingCategory.NATURAL_RESOURCES], + [FundingInstrument.GRANT, FundingInstrument.COOPERATIVE_AGREEMENT], + ) + validate_summary_and_nested( + db_session, forecast_hist_delete, [], [], [], expect_in_db=False + ) + validate_summary_and_nested( + db_session, + synopsis_already_processed, + [ + ApplicantType.UNRESTRICTED, + ApplicantType.FEDERALLY_RECOGNIZED_NATIVE_AMERICAN_TRIBAL_GOVERNMENTS, + ], + [ + FundingCategory.INFRASTRUCTURE_INVESTMENT_AND_JOBS_ACT, + FundingCategory.FOOD_AND_NUTRITION, + ], + [FundingInstrument.PROCUREMENT_CONTRACT, FundingInstrument.OTHER], + expect_values_to_match=False, + ) + validate_summary_and_nested(db_session, synopsis_hist_insert, [], [], []) + + assert { + transform_oracle_data_task.Metrics.TOTAL_RECORDS_PROCESSED: 41, + transform_oracle_data_task.Metrics.TOTAL_RECORDS_INSERTED: 8, + transform_oracle_data_task.Metrics.TOTAL_RECORDS_UPDATED: 9, + transform_oracle_data_task.Metrics.TOTAL_RECORDS_DELETED: 8, + transform_oracle_data_task.Metrics.TOTAL_DUPLICATE_RECORDS_SKIPPED: 15, + transform_oracle_data_task.Metrics.TOTAL_RECORDS_ORPHANED: 0, + transform_oracle_data_task.Metrics.TOTAL_ERROR_COUNT: 1, + }.items() <= transform_oracle_data_task.metrics.items() From c1a7426747b3452581d76558dfff8487315e68d2 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Thu, 9 May 2024 13:57:58 -0400 Subject: [PATCH 29/39] Adjust nesting structure of if-statements --- .../transform_oracle_data_task.py | 118 +++++++++--------- 1 file changed, 60 insertions(+), 58 deletions(-) diff --git a/api/src/data_migration/transformation/transform_oracle_data_task.py b/api/src/data_migration/transformation/transform_oracle_data_task.py index e7f2af4ca..0230dd9ed 100644 --- a/api/src/data_migration/transformation/transform_oracle_data_task.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -526,22 +526,24 @@ def process_link_applicant_type( source_applicant_type, target_applicant_type, opportunity_summary ) - if is_insert: - # Before we insert, we have to still be certain we're not adding a duplicate record - # because the primary key of the legacy tables is the legacy ID + lookup value + opportunity ID - # its possible for the same lookup value to appear multiple times because the legacy ID is different - # This would hit a conflict in our DBs primary key, so we need to verify that won't happen - if transformed_applicant_type.applicant_type in opportunity_summary.applicant_types: - self.increment(self.Metrics.TOTAL_DUPLICATE_RECORDS_SKIPPED) - logger.warning( - "Skipping applicant type record", - extra=extra | {"applicant_type": transformed_applicant_type.applicant_type}, - ) - else: - self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) - # We append to the relationship so SQLAlchemy immediately attaches it to its cached - # opportunity summary object so that the above check works when we receive dupes in the same batch - opportunity_summary.link_applicant_types.append(transformed_applicant_type) + # Before we insert, we have to still be certain we're not adding a duplicate record + # because the primary key of the legacy tables is the legacy ID + lookup value + opportunity ID + # its possible for the same lookup value to appear multiple times because the legacy ID is different + # This would hit a conflict in our DBs primary key, so we need to verify that won't happen + if ( + is_insert + and transformed_applicant_type.applicant_type in opportunity_summary.applicant_types + ): + self.increment(self.Metrics.TOTAL_DUPLICATE_RECORDS_SKIPPED) + logger.warning( + "Skipping applicant type record", + extra=extra | {"applicant_type": transformed_applicant_type.applicant_type}, + ) + elif is_insert: + self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + # We append to the relationship so SQLAlchemy immediately attaches it to its cached + # opportunity summary object so that the above check works when we receive dupes in the same batch + opportunity_summary.link_applicant_types.append(transformed_applicant_type) else: self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) self.db_session.merge(transformed_applicant_type) @@ -671,26 +673,27 @@ def process_link_funding_category( source_funding_category, target_funding_category, opportunity_summary ) ) - if is_insert: - # Before we insert, we have to still be certain we're not adding a duplicate record - # because the primary key of the legacy tables is the legacy ID + lookup value + opportunity ID - # its possible for the same lookup value to appear multiple times because the legacy ID is different - # This would hit a conflict in our DBs primary key, so we need to verify that won't happen - if ( - transformed_funding_category.funding_category - in opportunity_summary.funding_categories - ): - self.increment(self.Metrics.TOTAL_DUPLICATE_RECORDS_SKIPPED) - logger.warning( - "Skipping funding category record", - extra=extra - | {"funding_category": transformed_funding_category.funding_category}, - ) - else: - self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) - # We append to the relationship so SQLAlchemy immediately attaches it to its cached - # opportunity summary object so that the above check works when we receive dupes in the same batch - opportunity_summary.link_funding_categories.append(transformed_funding_category) + + # Before we insert, we have to still be certain we're not adding a duplicate record + # because the primary key of the legacy tables is the legacy ID + lookup value + opportunity ID + # its possible for the same lookup value to appear multiple times because the legacy ID is different + # This would hit a conflict in our DBs primary key, so we need to verify that won't happen + if ( + is_insert + and transformed_funding_category.funding_category + in opportunity_summary.funding_categories + ): + self.increment(self.Metrics.TOTAL_DUPLICATE_RECORDS_SKIPPED) + logger.warning( + "Skipping funding category record", + extra=extra + | {"funding_category": transformed_funding_category.funding_category}, + ) + elif is_insert: + self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + # We append to the relationship so SQLAlchemy immediately attaches it to its cached + # opportunity summary object so that the above check works when we receive dupes in the same batch + opportunity_summary.link_funding_categories.append(transformed_funding_category) else: self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) self.db_session.merge(transformed_funding_category) @@ -822,28 +825,27 @@ def process_link_funding_instrument( source_funding_instrument, target_funding_instrument, opportunity_summary ) ) - if is_insert: - # Before we insert, we have to still be certain we're not adding a duplicate record - # because the primary key of the legacy tables is the legacy ID + lookup value + opportunity ID - # its possible for the same lookup value to appear multiple times because the legacy ID is different - # This would hit a conflict in our DBs primary key, so we need to verify that won't happen - if ( - transformed_funding_instrument.funding_instrument - in opportunity_summary.funding_instruments - ): - self.increment(self.Metrics.TOTAL_DUPLICATE_RECORDS_SKIPPED) - logger.warning( - "Skipping funding instrument record", - extra=extra - | {"funding_instrument": transformed_funding_instrument.funding_instrument}, - ) - else: - self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) - # We append to the relationship so SQLAlchemy immediately attaches it to its cached - # opportunity summary object so that the above check works when we receive dupes in the same batch - opportunity_summary.link_funding_instruments.append( - transformed_funding_instrument - ) + + # Before we insert, we have to still be certain we're not adding a duplicate record + # because the primary key of the legacy tables is the legacy ID + lookup value + opportunity ID + # its possible for the same lookup value to appear multiple times because the legacy ID is different + # This would hit a conflict in our DBs primary key, so we need to verify that won't happen + if ( + is_insert + and transformed_funding_instrument.funding_instrument + in opportunity_summary.funding_instruments + ): + self.increment(self.Metrics.TOTAL_DUPLICATE_RECORDS_SKIPPED) + logger.warning( + "Skipping funding instrument record", + extra=extra + | {"funding_instrument": transformed_funding_instrument.funding_instrument}, + ) + elif is_insert: + self.increment(self.Metrics.TOTAL_RECORDS_INSERTED) + # We append to the relationship so SQLAlchemy immediately attaches it to its cached + # opportunity summary object so that the above check works when we receive dupes in the same batch + opportunity_summary.link_funding_instruments.append(transformed_funding_instrument) else: self.increment(self.Metrics.TOTAL_RECORDS_UPDATED) self.db_session.merge(transformed_funding_instrument) From 20abd51533c381fb35dab5c3c7007e0b0615f221 Mon Sep 17 00:00:00 2001 From: Michael Chouinard Date: Fri, 10 May 2024 12:29:30 -0400 Subject: [PATCH 30/39] [Issue #1977] Adjust transformation logic to handle orphaned history records --- .../transform_oracle_data_task.py | 55 ++++++- ...09_add_transformation_notes_to_staging_.py | 129 +++++++++++++++ api/src/db/models/staging/forecast.py | 32 ++++ api/src/db/models/staging/staging_base.py | 2 + api/src/db/models/staging/synopsis.py | 32 ++++ api/src/task/task.py | 2 +- .../test_transform_oracle_data_task.py | 153 +++++++++++++++++- api/tests/src/db/models/factories.py | 24 +++ 8 files changed, 415 insertions(+), 14 deletions(-) create mode 100644 api/src/db/migrations/versions/2024_05_09_add_transformation_notes_to_staging_.py diff --git a/api/src/data_migration/transformation/transform_oracle_data_task.py b/api/src/data_migration/transformation/transform_oracle_data_task.py index 0230dd9ed..a6fd8a8f0 100644 --- a/api/src/data_migration/transformation/transform_oracle_data_task.py +++ b/api/src/data_migration/transformation/transform_oracle_data_task.py @@ -49,6 +49,10 @@ logger = logging.getLogger(__name__) +# Constants +ORPHANED_CFDA = "orphaned_cfda" +ORPHANED_HISTORICAL_RECORD = "orphaned_historical_record" + class TransformOracleDataTask(Task): class Metrics(StrEnum): @@ -58,6 +62,7 @@ class Metrics(StrEnum): TOTAL_RECORDS_UPDATED = "total_records_updated" TOTAL_RECORDS_ORPHANED = "total_records_orphaned" TOTAL_DUPLICATE_RECORDS_SKIPPED = "total_duplicate_records_skipped" + TOTAL_HISTORICAL_ORPHANS_SKIPPED = "total_historical_orphans_skipped" TOTAL_ERROR_COUNT = "total_error_count" @@ -266,6 +271,7 @@ def process_assistance_listing( "Assistance listing is orphaned and does not connect to any opportunity", extra=extra, ) + source_assistance_listing.transformation_notes = ORPHANED_CFDA elif source_assistance_listing.is_deleted: logger.info("Deleting assistance listing", extra=extra) @@ -369,14 +375,22 @@ def process_opportunity_summary( extra = transform_util.get_log_extra_summary(source_summary) logger.info("Processing opportunity summary", extra=extra) - if opportunity is None: + if opportunity is None and source_summary.is_historical_table: + logger.warning( + "Historical opportunity summary does not have a corresponding opportunity - cannot import, but will mark as processed", + extra=extra, + ) + self.increment(self.Metrics.TOTAL_HISTORICAL_ORPHANS_SKIPPED) + source_summary.transformation_notes = ORPHANED_HISTORICAL_RECORD + + elif opportunity is None: # This shouldn't be possible as the incoming data has foreign keys, but as a safety net # we'll make sure the opportunity actually exists raise ValueError( "Opportunity summary cannot be processed as the opportunity for it does not exist" ) - if source_summary.is_deleted: + elif source_summary.is_deleted: logger.info("Deleting opportunity summary", extra=extra) if target_summary is None: @@ -501,14 +515,22 @@ def process_link_applicant_type( extra = transform_util.get_log_extra_applicant_type(source_applicant_type) logger.info("Processing applicant type", extra=extra) - if opportunity_summary is None: + if opportunity_summary is None and source_applicant_type.is_historical_table: + logger.warning( + "Historical applicant type does not have a corresponding opportunity summary - cannot import, but will mark as processed", + extra=extra, + ) + self.increment(self.Metrics.TOTAL_HISTORICAL_ORPHANS_SKIPPED) + source_applicant_type.transformation_notes = ORPHANED_HISTORICAL_RECORD + + elif opportunity_summary is None: # This shouldn't be possible as the incoming data has foreign keys, but as a safety net # we'll make sure the opportunity actually exists raise ValueError( "Applicant type record cannot be processed as the opportunity summary for it does not exist" ) - if source_applicant_type.is_deleted: + elif source_applicant_type.is_deleted: logger.info("Deleting applicant type", extra=extra) if target_applicant_type is None: @@ -647,14 +669,22 @@ def process_link_funding_category( extra = transform_util.get_log_extra_funding_category(source_funding_category) logger.info("Processing funding category", extra=extra) - if opportunity_summary is None: + if opportunity_summary is None and source_funding_category.is_historical_table: + logger.warning( + "Historical funding category does not have a corresponding opportunity summary - cannot import, but will mark as processed", + extra=extra, + ) + self.increment(self.Metrics.TOTAL_HISTORICAL_ORPHANS_SKIPPED) + source_funding_category.transformation_notes = ORPHANED_HISTORICAL_RECORD + + elif opportunity_summary is None: # This shouldn't be possible as the incoming data has foreign keys, but as a safety net # we'll make sure the opportunity actually exists raise ValueError( "Funding category record cannot be processed as the opportunity summary for it does not exist" ) - if source_funding_category.is_deleted: + elif source_funding_category.is_deleted: logger.info("Deleting funding category", extra=extra) if target_funding_category is None: @@ -799,14 +829,23 @@ def process_link_funding_instrument( extra = transform_util.get_log_extra_funding_instrument(source_funding_instrument) logger.info("Processing funding instrument", extra=extra) - if opportunity_summary is None: + # TODO - add some notes + if opportunity_summary is None and source_funding_instrument.is_historical_table: + logger.warning( + "Historical funding instrument does not have a corresponding opportunity summary - cannot import, but will mark as processed", + extra=extra, + ) + self.increment(self.Metrics.TOTAL_HISTORICAL_ORPHANS_SKIPPED) + source_funding_instrument.transformation_notes = ORPHANED_HISTORICAL_RECORD + + elif opportunity_summary is None: # This shouldn't be possible as the incoming data has foreign keys, but as a safety net # we'll make sure the opportunity actually exists raise ValueError( "Funding instrument record cannot be processed as the opportunity summary for it does not exist" ) - if source_funding_instrument.is_deleted: + elif source_funding_instrument.is_deleted: logger.info("Deleting funding instrument", extra=extra) if target_funding_instrument is None: diff --git a/api/src/db/migrations/versions/2024_05_09_add_transformation_notes_to_staging_.py b/api/src/db/migrations/versions/2024_05_09_add_transformation_notes_to_staging_.py new file mode 100644 index 000000000..fa364dd5d --- /dev/null +++ b/api/src/db/migrations/versions/2024_05_09_add_transformation_notes_to_staging_.py @@ -0,0 +1,129 @@ +"""add transformation notes to staging tables + +Revision ID: 61c58638e56b +Revises: f97987d087b5 +Create Date: 2024-05-09 15:06:48.010975 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "61c58638e56b" +down_revision = "f97987d087b5" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "tapplicanttypes_forecast", + sa.Column("transformation_notes", sa.Text(), nullable=True), + schema="staging", + ) + op.add_column( + "tapplicanttypes_forecast_hist", + sa.Column("transformation_notes", sa.Text(), nullable=True), + schema="staging", + ) + op.add_column( + "tapplicanttypes_synopsis", + sa.Column("transformation_notes", sa.Text(), nullable=True), + schema="staging", + ) + op.add_column( + "tapplicanttypes_synopsis_hist", + sa.Column("transformation_notes", sa.Text(), nullable=True), + schema="staging", + ) + op.add_column( + "tforecast", sa.Column("transformation_notes", sa.Text(), nullable=True), schema="staging" + ) + op.add_column( + "tforecast_hist", + sa.Column("transformation_notes", sa.Text(), nullable=True), + schema="staging", + ) + op.add_column( + "tfundactcat_forecast", + sa.Column("transformation_notes", sa.Text(), nullable=True), + schema="staging", + ) + op.add_column( + "tfundactcat_forecast_hist", + sa.Column("transformation_notes", sa.Text(), nullable=True), + schema="staging", + ) + op.add_column( + "tfundactcat_synopsis", + sa.Column("transformation_notes", sa.Text(), nullable=True), + schema="staging", + ) + op.add_column( + "tfundactcat_synopsis_hist", + sa.Column("transformation_notes", sa.Text(), nullable=True), + schema="staging", + ) + op.add_column( + "tfundinstr_forecast", + sa.Column("transformation_notes", sa.Text(), nullable=True), + schema="staging", + ) + op.add_column( + "tfundinstr_forecast_hist", + sa.Column("transformation_notes", sa.Text(), nullable=True), + schema="staging", + ) + op.add_column( + "tfundinstr_synopsis", + sa.Column("transformation_notes", sa.Text(), nullable=True), + schema="staging", + ) + op.add_column( + "tfundinstr_synopsis_hist", + sa.Column("transformation_notes", sa.Text(), nullable=True), + schema="staging", + ) + op.add_column( + "topportunity", + sa.Column("transformation_notes", sa.Text(), nullable=True), + schema="staging", + ) + op.add_column( + "topportunity_cfda", + sa.Column("transformation_notes", sa.Text(), nullable=True), + schema="staging", + ) + op.add_column( + "tsynopsis", sa.Column("transformation_notes", sa.Text(), nullable=True), schema="staging" + ) + op.add_column( + "tsynopsis_hist", + sa.Column("transformation_notes", sa.Text(), nullable=True), + schema="staging", + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("tsynopsis_hist", "transformation_notes", schema="staging") + op.drop_column("tsynopsis", "transformation_notes", schema="staging") + op.drop_column("topportunity_cfda", "transformation_notes", schema="staging") + op.drop_column("topportunity", "transformation_notes", schema="staging") + op.drop_column("tfundinstr_synopsis_hist", "transformation_notes", schema="staging") + op.drop_column("tfundinstr_synopsis", "transformation_notes", schema="staging") + op.drop_column("tfundinstr_forecast_hist", "transformation_notes", schema="staging") + op.drop_column("tfundinstr_forecast", "transformation_notes", schema="staging") + op.drop_column("tfundactcat_synopsis_hist", "transformation_notes", schema="staging") + op.drop_column("tfundactcat_synopsis", "transformation_notes", schema="staging") + op.drop_column("tfundactcat_forecast_hist", "transformation_notes", schema="staging") + op.drop_column("tfundactcat_forecast", "transformation_notes", schema="staging") + op.drop_column("tforecast_hist", "transformation_notes", schema="staging") + op.drop_column("tforecast", "transformation_notes", schema="staging") + op.drop_column("tapplicanttypes_synopsis_hist", "transformation_notes", schema="staging") + op.drop_column("tapplicanttypes_synopsis", "transformation_notes", schema="staging") + op.drop_column("tapplicanttypes_forecast_hist", "transformation_notes", schema="staging") + op.drop_column("tapplicanttypes_forecast", "transformation_notes", schema="staging") + # ### end Alembic commands ### diff --git a/api/src/db/models/staging/forecast.py b/api/src/db/models/staging/forecast.py index b4e372580..2030d2329 100644 --- a/api/src/db/models/staging/forecast.py +++ b/api/src/db/models/staging/forecast.py @@ -20,6 +20,10 @@ class Tforecast(StagingBase, forecast_mixin.TforecastMixin, StagingParamMixin): def is_forecast(self) -> bool: return True + @property + def is_historical_table(self) -> bool: + return False + class TforecastHist(StagingBase, forecast_mixin.TforecastHistMixin, StagingParamMixin): __tablename__ = "tforecast_hist" @@ -35,6 +39,10 @@ class TforecastHist(StagingBase, forecast_mixin.TforecastHistMixin, StagingParam def is_forecast(self) -> bool: return True + @property + def is_historical_table(self) -> bool: + return True + class TapplicanttypesForecast( StagingBase, forecast_mixin.TapplicanttypesForecastMixin, StagingParamMixin @@ -60,6 +68,10 @@ def is_forecast(self) -> bool: def revision_number(self) -> None: return None + @property + def is_historical_table(self) -> bool: + return False + class TapplicanttypesForecastHist( StagingBase, forecast_mixin.TapplicanttypesForecastHistMixin, StagingParamMixin @@ -81,6 +93,10 @@ def legacy_applicant_type_id(self) -> int: def is_forecast(self) -> bool: return True + @property + def is_historical_table(self) -> bool: + return True + class TfundactcatForecast(StagingBase, forecast_mixin.TfundactcatForecastMixin, StagingParamMixin): __tablename__ = "tfundactcat_forecast" @@ -104,6 +120,10 @@ def is_forecast(self) -> bool: def revision_number(self) -> None: return None + @property + def is_historical_table(self) -> bool: + return False + class TfundactcatForecastHist( StagingBase, forecast_mixin.TfundactcatForecastHistMixin, StagingParamMixin @@ -125,6 +145,10 @@ def legacy_funding_category_id(self) -> int: def is_forecast(self) -> bool: return True + @property + def is_historical_table(self) -> bool: + return True + class TfundinstrForecast(StagingBase, forecast_mixin.TfundinstrForecastMixin, StagingParamMixin): __tablename__ = "tfundinstr_forecast" @@ -148,6 +172,10 @@ def is_forecast(self) -> bool: def revision_number(self) -> None: return None + @property + def is_historical_table(self) -> bool: + return False + class TfundinstrForecastHist( StagingBase, forecast_mixin.TfundinstrForecastHistMixin, StagingParamMixin @@ -168,3 +196,7 @@ def legacy_funding_instrument_id(self) -> int: @property def is_forecast(self) -> bool: return True + + @property + def is_historical_table(self) -> bool: + return True diff --git a/api/src/db/models/staging/staging_base.py b/api/src/db/models/staging/staging_base.py index 30d4f0c11..0705a866e 100644 --- a/api/src/db/models/staging/staging_base.py +++ b/api/src/db/models/staging/staging_base.py @@ -72,3 +72,5 @@ class StagingParamMixin: default=None, server_default=None, ) + + transformation_notes: Mapped[str | None] diff --git a/api/src/db/models/staging/synopsis.py b/api/src/db/models/staging/synopsis.py index 857ccb07a..2ad20d7c9 100644 --- a/api/src/db/models/staging/synopsis.py +++ b/api/src/db/models/staging/synopsis.py @@ -20,6 +20,10 @@ class Tsynopsis(StagingBase, synopsis_mixin.TsynopsisMixin, StagingParamMixin): def is_forecast(self) -> bool: return False + @property + def is_historical_table(self) -> bool: + return False + class TsynopsisHist(StagingBase, synopsis_mixin.TsynopsisHistMixin, StagingParamMixin): __tablename__ = "tsynopsis_hist" @@ -35,6 +39,10 @@ class TsynopsisHist(StagingBase, synopsis_mixin.TsynopsisHistMixin, StagingParam def is_forecast(self) -> bool: return False + @property + def is_historical_table(self) -> bool: + return True + class TapplicanttypesSynopsis( StagingBase, synopsis_mixin.TapplicanttypesSynopsisMixin, StagingParamMixin @@ -60,6 +68,10 @@ def is_forecast(self) -> bool: def revision_number(self) -> None: return None + @property + def is_historical_table(self) -> bool: + return False + class TapplicanttypesSynopsisHist( StagingBase, synopsis_mixin.TapplicanttypesSynopsisHistMixin, StagingParamMixin @@ -81,6 +93,10 @@ def legacy_applicant_type_id(self) -> int: def is_forecast(self) -> bool: return False + @property + def is_historical_table(self) -> bool: + return True + class TfundactcatSynopsis(StagingBase, synopsis_mixin.TfundactcatSynopsisMixin, StagingParamMixin): __tablename__ = "tfundactcat_synopsis" @@ -104,6 +120,10 @@ def is_forecast(self) -> bool: def revision_number(self) -> None: return None + @property + def is_historical_table(self) -> bool: + return False + class TfundactcatSynopsisHist( StagingBase, synopsis_mixin.TfundactcatSynopsisHistMixin, StagingParamMixin @@ -125,6 +145,10 @@ def legacy_funding_category_id(self) -> int: def is_forecast(self) -> bool: return False + @property + def is_historical_table(self) -> bool: + return True + class TfundinstrSynopsis(StagingBase, synopsis_mixin.TfundinstrSynopsisMixin, StagingParamMixin): __tablename__ = "tfundinstr_synopsis" @@ -148,6 +172,10 @@ def is_forecast(self) -> bool: def revision_number(self) -> None: return None + @property + def is_historical_table(self) -> bool: + return False + class TfundinstrSynopsisHist( StagingBase, synopsis_mixin.TfundinstrSynopsisHistMixin, StagingParamMixin @@ -168,3 +196,7 @@ def legacy_funding_instrument_id(self) -> int: @property def is_forecast(self) -> bool: return False + + @property + def is_historical_table(self) -> bool: + return True diff --git a/api/src/task/task.py b/api/src/task/task.py index 7691e7e52..8c29744bc 100644 --- a/api/src/task/task.py +++ b/api/src/task/task.py @@ -57,7 +57,7 @@ def initialize_metrics(self) -> None: def set_metrics(self, metrics: dict[str, Any]) -> None: self.metrics.update(**metrics) - def increment(self, name: str, value: int = 1) -> None: + def increment(self, name: str, value: int = 1, prefix: str | None = None) -> None: if name not in self.metrics: self.metrics[name] = 0 diff --git a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py index 4affc58b0..20ee40c11 100644 --- a/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py +++ b/api/tests/src/data_migration/transformation/test_transform_oracle_data_task.py @@ -85,7 +85,7 @@ def setup_synopsis_forecast( is_forecast: bool, revision_number: int | None, create_existing: bool, - opportunity: Opportunity, + opportunity: Opportunity | None, is_delete: bool = False, is_already_processed: bool = False, source_values: dict | None = None, @@ -107,10 +107,12 @@ def setup_synopsis_forecast( if revision_number is not None: source_values["revision_number"] = revision_number + if opportunity is not None: + source_values["opportunity_id"] = opportunity.opportunity_id + source_summary = factory_cls.create( **source_values, opportunity=None, # To override the factory trying to create something - opportunity_id=opportunity.opportunity_id, is_deleted=is_delete, already_transformed=is_already_processed, ) @@ -845,6 +847,7 @@ def test_process_assistance_listing_orphaned_record( cfda_insert_without_opportunity, None, None ) assert cfda_insert_without_opportunity.transformed_at is not None + assert cfda_insert_without_opportunity.transformation_notes == "orphaned_cfda" assert ( transform_oracle_data_task.metrics[ transform_oracle_data_task.Metrics.TOTAL_RECORDS_ORPHANED @@ -1109,6 +1112,49 @@ def test_process_opportunity_summary_invalid_value_errors( source_summary, None, opportunity ) + @pytest.mark.parametrize("is_forecast", [True, False]) + def test_process_opportunity_summary_but_no_opportunity_non_hist( + self, + db_session, + transform_oracle_data_task, + is_forecast, + ): + source_record = setup_synopsis_forecast( + is_forecast=is_forecast, + revision_number=None, + create_existing=False, + opportunity=None, + source_values={"opportunity_id": 12121212}, + ) + + with pytest.raises( + ValueError, + match="Opportunity summary cannot be processed as the opportunity for it does not exist", + ): + transform_oracle_data_task.process_opportunity_summary(source_record, None, None) + + @pytest.mark.parametrize("is_forecast,revision_number", [(True, 10), (False, 9)]) + def test_process_opportunity_summary_but_no_opportunity_hist( + self, + db_session, + transform_oracle_data_task, + is_forecast, + revision_number, + ): + source_record = setup_synopsis_forecast( + is_forecast=is_forecast, + revision_number=revision_number, + create_existing=False, + opportunity=None, + source_values={"opportunity_id": 12121212}, + ) + + transform_oracle_data_task.process_opportunity_summary(source_record, None, None) + + validate_opportunity_summary(db_session, source_record, expect_in_db=False) + assert source_record.transformed_at is not None + assert source_record.transformation_notes == "orphaned_historical_record" + class TestTransformApplicantType(BaseTestClass): @pytest.fixture() @@ -1454,6 +1500,39 @@ def test_process_applicant_types_but_invalid_lookup_value( insert_but_invalid_value, None, opportunity_summary ) + @pytest.mark.parametrize( + "factory_cls", + [f.StagingTapplicanttypesForecastFactory, f.StagingTapplicanttypesSynopsisFactory], + ) + def test_process_applicant_type_but_no_opportunity_summary_non_hist( + self, + db_session, + transform_oracle_data_task, + factory_cls, + ): + source_record = factory_cls.create(orphaned_record=True) + + with pytest.raises( + ValueError, + match="Applicant type record cannot be processed as the opportunity summary for it does not exist", + ): + transform_oracle_data_task.process_link_applicant_type(source_record, None, None) + + @pytest.mark.parametrize( + "factory_cls", + [f.StagingTapplicanttypesForecastHistFactory, f.StagingTapplicanttypesSynopsisHistFactory], + ) + def test_process_applicant_type_but_no_opportunity_summary_hist( + self, + db_session, + transform_oracle_data_task, + factory_cls, + ): + source_record = factory_cls.create(orphaned_record=True, revision_number=12) + transform_oracle_data_task.process_link_applicant_type(source_record, None, None) + assert source_record.transformed_at is not None + assert source_record.transformation_notes == "orphaned_historical_record" + class TestTransformFundingInstrument(BaseTestClass): @pytest.fixture() @@ -1683,7 +1762,7 @@ def test_process_funding_instrument_but_current_missing( "is_forecast,revision_number,legacy_lookup_value", [(True, None, "X"), (False, None, "4"), (True, 5, "Y"), (False, 10, "A")], ) - def test_process_applicant_types_but_invalid_lookup_value( + def test_process_funding_instrument_but_invalid_lookup_value( self, db_session, transform_oracle_data_task, @@ -1705,6 +1784,38 @@ def test_process_applicant_types_but_invalid_lookup_value( insert_but_invalid_value, None, opportunity_summary ) + @pytest.mark.parametrize( + "factory_cls", [f.StagingTfundinstrForecastFactory, f.StagingTfundinstrSynopsisFactory] + ) + def test_process_funding_instrument_but_no_opportunity_summary_non_hist( + self, + db_session, + transform_oracle_data_task, + factory_cls, + ): + source_record = factory_cls.create(orphaned_record=True) + + with pytest.raises( + ValueError, + match="Funding instrument record cannot be processed as the opportunity summary for it does not exist", + ): + transform_oracle_data_task.process_link_funding_instrument(source_record, None, None) + + @pytest.mark.parametrize( + "factory_cls", + [f.StagingTfundinstrForecastHistFactory, f.StagingTfundinstrSynopsisHistFactory], + ) + def test_process_funding_instrument_but_no_opportunity_summary_hist( + self, + db_session, + transform_oracle_data_task, + factory_cls, + ): + source_record = factory_cls.create(orphaned_record=True, revision_number=12) + transform_oracle_data_task.process_link_funding_instrument(source_record, None, None) + assert source_record.transformed_at is not None + assert source_record.transformation_notes == "orphaned_historical_record" + class TestTransformFundingCategory(BaseTestClass): @pytest.fixture() @@ -1986,7 +2097,7 @@ def test_process_funding_categories(self, db_session, transform_oracle_data_task @pytest.mark.parametrize( "is_forecast,revision_number", [(True, None), (False, None), (True, 1), (False, 70)] ) - def test_process_applicant_types_but_current_missing( + def test_process_funding_category_but_current_missing( self, db_session, transform_oracle_data_task, is_forecast, revision_number ): opportunity_summary = f.OpportunitySummaryFactory.create( @@ -2008,7 +2119,7 @@ def test_process_applicant_types_but_current_missing( "is_forecast,revision_number,legacy_lookup_value", [(True, None, "ab"), (False, None, "cd"), (True, 5, "ef"), (False, 10, "Ag")], ) - def test_process_applicant_types_but_invalid_lookup_value( + def test_process_funding_category_but_invalid_lookup_value( self, db_session, transform_oracle_data_task, @@ -2030,6 +2141,38 @@ def test_process_applicant_types_but_invalid_lookup_value( insert_but_invalid_value, None, opportunity_summary ) + @pytest.mark.parametrize( + "factory_cls", [f.StagingTfundactcatForecastFactory, f.StagingTfundactcatSynopsisFactory] + ) + def test_process_funding_category_but_no_opportunity_summary_non_hist( + self, + db_session, + transform_oracle_data_task, + factory_cls, + ): + source_record = factory_cls.create(orphaned_record=True) + + with pytest.raises( + ValueError, + match="Funding category record cannot be processed as the opportunity summary for it does not exist", + ): + transform_oracle_data_task.process_link_funding_category(source_record, None, None) + + @pytest.mark.parametrize( + "factory_cls", + [f.StagingTfundactcatForecastHistFactory, f.StagingTfundactcatSynopsisHistFactory], + ) + def test_process_funding_category_but_no_opportunity_summary_hist( + self, + db_session, + transform_oracle_data_task, + factory_cls, + ): + source_record = factory_cls.create(orphaned_record=True, revision_number=12) + transform_oracle_data_task.process_link_funding_category(source_record, None, None) + assert source_record.transformed_at is not None + assert source_record.transformation_notes == "orphaned_historical_record" + class TestTransformFullRunTask(BaseTestClass): # The above tests validated we could run the tests diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index 512cc50ad..b8b4d94a0 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -969,6 +969,10 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) + orphaned_record = factory.Trait( + forecast=None, opportunity_id=factory.Faker("random_int", min=10_000, max=50_000) + ) + class StagingTapplicanttypesForecastHistFactory(StagingTapplicanttypesForecastFactory): class Meta: @@ -1012,6 +1016,10 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) + orphaned_record = factory.Trait( + synopsis=None, opportunity_id=factory.Faker("random_int", min=10_000, max=50_000) + ) + class StagingTapplicanttypesSynopsisHistFactory(StagingTapplicanttypesSynopsisFactory): class Meta: @@ -1055,6 +1063,10 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) + orphaned_record = factory.Trait( + forecast=None, opportunity_id=factory.Faker("random_int", min=10_000, max=50_000) + ) + class StagingTfundactcatForecastHistFactory(StagingTfundactcatForecastFactory): class Meta: @@ -1098,6 +1110,10 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) + orphaned_record = factory.Trait( + synopsis=None, opportunity_id=factory.Faker("random_int", min=10_000, max=50_000) + ) + class StagingTfundactcatSynopsisHistFactory(StagingTfundactcatSynopsisFactory): class Meta: @@ -1141,6 +1157,10 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) + orphaned_record = factory.Trait( + forecast=None, opportunity_id=factory.Faker("random_int", min=10_000, max=50_000) + ) + class StagingTfundinstrForecastHistFactory(StagingTfundinstrForecastFactory): class Meta: @@ -1184,6 +1204,10 @@ class Params: transformed_at=factory.Faker("date_time_between", start_date="-7d", end_date="-1d") ) + orphaned_record = factory.Trait( + synopsis=None, opportunity_id=factory.Faker("random_int", min=10_000, max=50_000) + ) + class StagingTfundinstrSynopsisHistFactory(StagingTfundinstrSynopsisFactory): class Meta: From e49fdf47cf7e75b7fcde45458a84b84f72c2dcdb Mon Sep 17 00:00:00 2001 From: nava-platform-bot Date: Fri, 10 May 2024 17:29:35 +0000 Subject: [PATCH 31/39] Update database ERD diagrams --- .../api/database/erds/full-schema.png | Bin 686928 -> 711667 bytes .../api/database/erds/staging-schema.png | Bin 279467 -> 297223 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/documentation/api/database/erds/full-schema.png b/documentation/api/database/erds/full-schema.png index 529bc88a821f461bffc04ee78bd0a202d470d26c..ded000e39f2707bad733517f456c5161bba9073b 100644 GIT binary patch literal 711667 zcmdSB2~?BU+BO`Ir*c{qPLER&ky;Q`5Gn}B+)9;31QY~hRuLjHDS;q_5L&8K5l}`M zLR2Q1g)j(#1dGU+MCLJx$`Ga`LI@#*B>#RqJ>UBu;k>WkTK~Vk^`)yzi4V!M_rCXi zU-xxg`?-70+G6`xd%uFgVB6t8|6~h;9l8mFeX0AmE#MvI4Xruw$CfLW7C*tH(0`8` z3zA^4Z(;DCe!OriYkmxqJP?ADE)42^|GoCNL28?A@BhX2uZN%g@aX-~fB2TBXWY@h z?VNskXJPT=z1n9!>E4Bf)kiXat*gHqs&P8+m)F1M=O5a8WK+BL?<(JaKh778hh~JN zx_td&OSmyL9a%QF8ez)i@QzArxOQg9L6)?xN@uZ>bU ztRC{Y7ypo$oMQO77k>-yIHUNv7e7$(%9H)vi+@h6w3YeXi&fxGg_}S3;>6sNzD=Kd zF}$bYGcUHX!6c5@TEP4)oWcA>DgMO!%=iZHl|7}Lv z=dHry!m!#%dx!F(!y#LmP&t+UgxG4(zcZ%g_#cLUI)uYf}t;@JiTibup8DS>fR`bo@0tAMZ0JPk(ALq*-h6HGg3mWP8FM2;x-6Q zmshj8A@b;D8{Qjb9XmpBSmMNQ<9EER+%TLv!NOQ5jXc$4vrL%_)Sxm%xb$B{eClsl zBellWtdVQhgPMv#{VV6?k42DG+5;m+ewvN%%NkCw;^rHW#oLVhe$ZUwwDk>=i37v? z(ZTKZX$c)KW!Tk>?ATUZmOIAUinia4*SBM^ER?x@5LefYcDE%&BzaSA&9-Q`H)z0n z@4&PHnQ;H-vFjmLY1 zQ`0#;B)3eJ_L5;FlGd2OS~e>9I8KaIC{k&ME6e!=OE_P$@`{keZ6@-_$v4(BLSV*Qo$8kY>!yl&6>#i+g0HMXWF+a`h{{H-1y2w(w1{Wxv)>f*tPwy!t$7TS$w-57gQ<##?iMkzggwSH9vZf{TA7kN#3A(Wf7 zS#COWveR)&PRGMJkd}`k4??l88>^b{-Tf-!nM;8{ybT7csrua1`hy*6;`bj7Fnu3_ zIb)h|Z9&Qx^5Rm3Boz-6%um-H%iDK3T6OheBXseid5KSdkyrCD4z#^!`ZXC@{zAdJ_8L-iC5qK zk#RQN(1_)}AAiT)6wknXZ5KR=dlpEyw-NUC-sY=SY0H!KlG_nZi< zsU0$r>Nu2x zdB9rtV3GeQ)!4*@NsD`k&o7H#5wp=Mx zUUy5EM1=3S8kyPs#n;a${-M|BpO92}65s~{C#g2`Mp{bN3AzMc8j&idyjxkAx%~lv`$PRc6=53he_0+&c5%Y23 zwL#&Kts3mP=cB%cjgZo4tgxK%P$hkJsMlb5YZ?ODW|J>F_YQE^1g(iGM8{A~ICEg& z2_~~oBje&M*b1M%qEz!j$-TVgQT0Wkr(9Rt4q43*Sy&UQJZvL;f%TgWDj)|0 zEbUM0)37NwEyzbCl!eXS^(BN`(RbtIKAi*@?72w>Z{DVMr3)!P^G}olD&PVUf#m>5_@92Jdp!?Ocee2<+UWeaN-RVXw4~vn1}* zh!PvY9Uc~2;SKF;?Bpxg&}Q)ruUoF5vhur6OU50YMiDN%B{t3Lo$rpGO0TdSG!&9n z^(+}xvpOf<1|=2-yM);g%s$Tv5AG=GD|1g$w%B&Iz}g=Prnk59RM`H=tKPG7vq>s$ z#NalBH0f!J8!T4qJE+H{vO*#)*j>^a7R^mq-X^hcC`bGnCuwYhhEwwyol#) zAADBomK1ou@vxF`C5_<1zgx$-BT7<1x=Sp-x_vTn;tqc8h>2PFb%2Oe1=8!Qr->et z-qJ#+S+Jc&lWyAtpy% zXqHt1)wF}x6m8=LpqH@a6*Cvb&)8;mg?ZeU;YSy_q_WE3**buFcoJ=t}KbqYMvP{b%xw;0>v}kGB}BEX zG&$@uJ6WZhkt2?ZY0LJllj;a}JcON0LUDLHF_ikj2CEM;e&;)7Qc@D>#?R1ns?vGH zd0|EGVobW0ZWmENQrqi2Zx-xVk77Siz%KDs+MN&)hKfe>S&afCoy`Izf|H=OnzdnH z^C6+&oW}0hy!dq^i(=F6H4yy5KQ;t^`yr|C=~I^@qmfVh}d$&$sI3vdWF`&A0QMy zyC(d_n-nT*R2Hu&LbNC5!4sF+q3!1_ws+b@201xb9kOYS7whQtwyPE0;` z7@-8OIH5%|=AFy8!Kc8JRLiSbmL999{o&Jt>EFMLDJ%?AO;VF;l)5-hNkj!D$f?AG zmO3}Dj2rCQb<|FSY6*UufG~SR#Tn^E;Pu#?(x59*Vnps}`qE|^v=_4k|VcVuP-(69Yg9;%@_*?Dls{;)~8ERMwPa$2;|8!B&vQ?U<$ZKsyso0L8Fhzuv!JkxW3YPiIv0B^$y6I`_^Hzgq#LDtnf)(HySt8gp7!d= zZFi~*0&o~%miZ{j+R(7nnO*gAPQ$tCL;(&J5YWgk>PqL$HAq%}jjO4^%nWjW-}zPl z%z*!m)nzu$1~dEk^c)Bh{C<=A0+(9NOk-ZtW|=$$jZ?=Txa*>~8+I*d!$P}3D8EUr z_8b0g&;xt^b)3jMRST;Ix7FBf=je!s2Fs(0oI$C~(djLqbu|5gX4&Bl5BZN=n0gvZ zN^v4&`<}q7>~a0x5PIuFvs~NTmtq~y*CpG_{5!gMG&A*-lv2GpQ<|Byt17@P{cB}e zs&tLkQ{rS-`%4FYD@eR+c2p$i~#se8yLP>sme1T+0K z_i&6unQQf~qD@ixPd7?km(cdZHHaglsr7$(TsXT?vOIGMPFY-4Yjf5$^wSMcq1W~gVF2xA5k#f^VRJ;G)ji9 zCD>5Sf)sV%K|`1G-Iec7crUp5rT%2(QChtGmG=1tWaJCHl^G`5TqwF%RkZ{^jTZK+wO*_lR8lCGAw z@+OjO5Fg%d`nYAd{WboX4T~Lf@I)|0U4QQ$eB0tA_v+XSU;X0S`CN*A+5}0opJHL!L+Iu*sN06L4%x3=6%ajFk?nxbUlPd<76GCj>zTaPJ~90j|tf8eYP?98)`0t2RZiZ^9q z>nJ!=sIz{{)D&&l1D$cLo-wQnOafpJ5KJzV`C%Gsp_SYI_(!xF{LXfxZ*{!8s2|>$ z501Fl_aEM&y7s+it9!X`%m`vmovYHC!*LRA2;obo1OzBJRaU730k#Q zC9K14jTK+iXz7vWJBklFwagIF1i{dFGgMdW-rX6kJ!1qr7k>Ln#YK&fzkWJDnNRiF zM*(UY`pQ{#xUTQ(%*&?|Tjtzw2E2rsfl|w&B13;-F6B)_SVD=jt{Np%U-|>`2q+>h zfhL*v?xiYSn?kcnimS^wDRO;qpWyi+GmvDh5~~8qmhpzyFGemVb@n_x6=n#kx=%0X z43pGI03@9zao(bCJRYrsmHctRF|_ko>;1Mc7Un3ZvU($1+@kAD z##boeF3~zyzU_GDQBzx%;!%5Gi*s(v^@V9?F`lRu#47YN7oh|M) zUTW*=({RgHF?jdliIbfp9?iiR3+TLZgAYj<-l{?@K@f@XejC}S^NvY{g9cGAW3<{b z##J}+!s%^HE%<-e&z6PMwC;Bc>YvOB`tT#Vw3hY$rdk2$aeD!XHs-&oo}-60hadvj z7C-N>0|bE2`R>v_d2a6=rjg*sU0?Nsiqy!h&d&!$ka}X=NM8FYJ_|LnkVoBgrUN|< z8~RQa^ITu`Qw;o^QsS;_1~?4+*=fhX`m|;SsBO?(U@v*>%Cz==kP*pPH5TVM!lvl zvnGgp7};Mp3p&RfcTcSBDF0c&A$kwopVOJF7&e$wFr)V5VLNplag1rd@@zNc&<7r` zrI(B!whK>E_{)q2;Y{~<)4rg#2U!t|tTVXHuqN538{x?3%Bz!F zN*yKw;}_kSBsH^CQ;e)-rk+ygftaiZAfxM+fW@ktEmG*s>x>-62yCW6UZdxN?ccCE zo7r1B`W8Td!t!#PurM0<8#K*e6EL6GmX&Gl+J)9L6oHzQQRXJ&T}$7hTkn0UCEKaG zNY7%wK~Zb*LI8)0S zx!WfXzl#Vt0Y!S;-LBBZAM>z zzdt`<-BKN@CX<_4pS0THTb8)#m@P{k6EgTSl%B~tu4KlC9$pZ!tYf~H${DCOhx)ly zeyMd>7(v7JLXt|@|G*H~+p~jKo`~(}jYwJJ$API@9_eOX5k8G3nIqd(THZHMoPEy} z6%+GFlK!*lK?taeNbJIszrTHw?~H{&*UZUHu%WUI8&=d`LGI-7%RL?eOxs(>GZpgi z%(f~Y9cU)&gNL=oTcsyju2giVpNid$m}m=}$nYg)ez7$hs?NywcRlyAonCyPG%WR0 zjYaTQ?(Ypj?ba$Aemj!i6QvGMo8pp zuex^l(rw>Tvky)Fao+LrI!B=0)sa%1sA47>ew7v6)9urLy?$O3uGGGfgyd5D5^9=E`GwJYOKI8Si14N9>;QVb-(uHhxqZpmCPdbN zi(0_&#dXVg?brBy#|{-F?9Ha)Mi0WLNR{8_kV8BZCyEOaeg=F|-TR`uVXFz{Q%F4B ztkk*M?*y^eS}Lk5IhT4o@~FdCxBmz)Cz7hS^Zlr%ms-0^UD5!O8(1Yyrm9=wwg3X~ zKWv(7!HXCm@0dh zCwtdTbVmH*qIccm;`y+5>6zNd)exUr%eksUf!Qj;u7}Zf_Whc*Yx56P+S`@o=(*|t zJ=LQDue@>DD*r4F7gjFTC5!1upI+~R`C4wa!u)U_v{x^tZKPB*#ZMG_D5lF{o>?{v zevDqG7MNj%3tr*yLjR zZ$difvXY^m8xhqOXSN!inA_0cbQ-4(q%?siy%)>fpLv`BI9g$gA}c>+4%dTy<5U;C zrWFME@W~NhQqMIe%Oai@^`i$(lXzijAoF!DhN$;nlQy-1%wBUJ(k{9J*G?ckoF0_9 z8BV09>n9t2DLG&w7|Q}lT%m4l5m^bE%(39!-vHGy3W!(S;E2)HHdA@s*?9;^@4L|y zS@-@P{%TVc$)`8J@-ppUnrm9D*8WU2d=^d(A7NO_ltgbY>mM8p44y()`;V%YH!OyS z3mD3v1e@vd^l>HeHQH&iV73}S{1vX5&kEgYc#Y%Wc|)V-E*rFSq+*h6?wh+C}^8D$H=+)2HM*fKuG1jBHG6t zi}`Vj?3j8eV|e9@YPb6N4EM%6`A?sE1rXWjnZ82t=|Md`UL#4&)8(%V$_NHyhMjM9Uf0-G9m zDn5BVzxiK^kyTS)Yc{pQ;hc)0DXttYYF3`OA8B2-Ecb873)7^F`^I|<)4pUuK`_>dH!Eg+Fw5WVqYWv z$@xOCddO>06`yG&^W1Y`zagyVcR0n;?UO;kU=cU|uab|7Exz#cA2FSj3yGMvVn`nhRU#y^Bo~ zYC7-AJXpVt%XN(H39tg!)xE3N;W2f}Y=G30qylOGplPmUH~-3RyW_4>*Vu5ivcBGB zcFu#6d^j}!RP3TkJK!M!!&A-=9r0nMYVKS=C2uj0K#S>Ab!|bf&f%ZfcX>1S7ymrZ zR>^km-uX9|xmn?}22PFPR?QyRSg16R`K-WX=X-U!&(KA0BUnbvPNrm;>im&5 z#w;6X16cRY(B57&HB%t1qK59<{Z+rg^kZ6Jj?tF?AoIJl0);9M=q>vUu3Q2weCCdL zg$ziJ`2dV19*%gGnUOqmQxVM^@L*1m^`O|Hl*&akJeV~hKiXkkuMJ-uZN+yg+ewVn z9S5d{a^%`tSAzpzoc3pDM0n0y3ENu9S@a^Ct`Pk(yh=K6(Zlx8&O{acwF$==VYi*m z4e7Busbg>Lf*ma5DZqBQ7qe;yn1BUvnmxXL-lD961a!Fx23m=sI_O+In|I+t)mjt! zl1upVu!SvPmUL*w$uqqOh_m!%3wzQ?xS0K`A*yFrE?>W%-9FK7W(m*}AgB({Wz}76 z?>U<_bjunl`+~jHZ1E+-P|nZ?%t#fnwxe+q%*A5^#VV?__)pDDim7w2xOg@(0=a~+ zBV4=E%;XUQ&)%Ce#kP{oQjJv4f_%74`*l!tVpiSSGO@u$8u0I+RAPqWn4IaXXDCQT zQ-9yw{MO+InS*;z{WSUb@gL8wUfcf@CakAZK9nLLK(HqYVIcOKD3&HolWe-Y52WwfOkTAmI5m%~Tt zYV>-=l*4xUs_@tM>kb1lwSKw&i>cnRy6`tk;{zJb-U&cYd|2 z@Y#tfR3znWA^;6s0+(cwUsB={f8xCdZ^YL=WO3|9vp>+UO^5w!N$JMIZvIqxVC2Gk zL2sE``AA(xMn-b-ygjS_m}kJ57+IM~9^ULkf|=yPo$aDX;FQs)6P0ko1m-iG_?=v! zHMqh{Ko|A56UsvEnP{)M>U@Qe0-Pj|D!>XmJ&-gLxk-{Jz}m~JJUO+dBOC27(PK&L zc>JT<0MU18daKTrSNy`l!r}LIo1$L#=T@wPZ7`SD-4=Z7Seh;^T8`GXCge`0g-Q>o zJCt=e#v6QZm8n~bC7t4}&1RcjrFt-&=1ROech|8b(j-Uengd%<>>#$eJS^gBQQQQk z=S67Vlockk`AEaxK1!U!tx%t{A%BAMdS;Dp~mtb<*$yjXF?i)z5S zmz<{b$PH!iEml0|l5WVKfqs@%l6cQB4FP2(cQOxT1OL=BWZB4&e<-!;beMoHUsy7q@-muMt^05UImiH~x4Ch=he|B=ikg26j4^|Ijty_hD%u zixO_Udmln7#zvNVzILs8nZB~LG=1~zSGVs$k;$PGr-nR5g9dm5p3TgdaO=!aMe`P} z@%(erFp{PkRccLmE&FM-!~EpYD26V1t>uF&KbqK6%za+v(|d%uI`y#b zJvzEFoNvLWGDBE-byG2+lg~D#d9^2ElY7MFNu)Y!zp!h zcxZb$SK^*8d6zea!)OT}lQZVKF%o6aISI#H#WYHaH0CMurC zz8M}S@XFGUv0oou(_43!u(yQ5Q^kwN11s!2TVX4|Ip;d@YT^{%@9^nl^yhCXb9N>bzZRF*(JKvbDbtNZP zwJo$Nr9V^->G><;a`j*VQMNjF$|TYeBu;+H0A-+a&Ka~h{I z-U(+NZ77EeA~2n|Wng>h_0o`a>CP{O6!A*pbP*$PX4*)bM_R6k$$dKDe*(jV&puJ& zA=o!ij&9`128AC#(_?7vbSmMGo)&`MM-}%LQ<46?t+CW1{PeX+r zEW)-lvC41$@0l&~h;1F)^~W*{kdh3QnbA<`K5Rs@Vyu@Jkc+Pzn_-f*K}IVMXWiLm zU;sE0%&=#Ibr`qtpz!@3QJ(>KBqX8h(SnDNUye<}OV=c>LG5zdz5~iW!x7c+C%e877<@o*S!W zMv5k$H?IOw<1YSWIP7u7ai971al6qw_%F1`#zuh+3y>H_G!TphvMO5hf$2k_KRseD zXYwuhXSG^7$}v^bt!6+7S5wSX%YO8*!c*;P+k>PzdUs)O8Ia2DbRyTL8w3o95qnM* z9*EiZKwihaE%~q=@1=~Aci%IGUSg;Khz$X2&3cbZN{)l+==4mm>XcE6c;s6qOt50; zdUshlRZcXoaYAjT60i{mz$gbrWh?WK^aFoXwTV2A<~Aa5Tw?IBi5f20&)QVjwYHF; zbrmRHPF2&57wj8xC!f4|OFibP*^v+#?3;Sjg>*1yGc4{I+;ao_%f+;lPo7pNub-3A zhu#!Jebiqssp})B@IV9-OhMHM+|elxkvnB9Uh3LjU*kr^ExwJeMZAB7?j>QkprQiy zkvYP1D0TLJ+my2y>#=57kQLP9JjjgT;STRS0?-equ*CS-9@iVrh9M)>)U#n>qOmYm z0KAw9VF*|z-=k@YB(?|?ZiAW0BB{Tx{zEVKn;L-uGO zS(81=tzv|>43t|WcmiJTDz`DAE#8Rp;Gr_j!`J)$t2yMvL-Ar<;I__qqvftmG7-Aq zRku3J)q8#zK*zF&wF1d4NdOU~w8SFva&ueMGKTl=`fAtV-Nk#2w&2eZ zQH_A=6S{pA+uW_Xqd%z4#`3#mh=39&H>qEI2_QltO`t8Sgu^waiT4pxT za^eky6PxOmdI&xBAwIZa6gzx!tH;n$nyH9+7!v*$7dP`VnQO+4;oG0-x2BQ$^z8~5 z{6Z6bk8A6r|M~Wz@YQ(JyI^q95N(+VOSs2my`pPr#F*M-X^m*7D+Ibg)V@(Ra%g^LFKR9N8rHa}nh zG)V+?S&MYypp6MX0{AJORC|LyKNgTw<8?f$65`z-dw6fmLvussdW=6b13v$y67iJCuL(t;`R(p-s z-6YH}mO~6Y!)X+}<5R)H$Qz+f8lpn-r>W;>YZujZGbe^zV;>zkV`J0=wG<7&)JCVtqKSB6v1#P zQ`yd8;HbsEg>oog8HNSCcvmxD?~4ay1M_V5+H^Q?P@jBjK?E&4+ttd->dcwtA|(CM zG5tLAnCL|zO(_e=hd?&Om;rZfCWoFtogWVDqe?!+=of%1Dn%Ue?1a-Gv54DP$v*@| z0dO;?88W3n&!zxrBw55E8bJ9GR9Q5~>7k{TiF))F+(m9#ca9b`;DY>o?^qUe3hCco zLwo(NF}qjmV+Q*9y>EtBQDrlmj_f=FFvsliKoSCsv_ZT)#z*ZeE#B&5h3|1BhhN9x zlux_jku*goBmF?euMot`vrOw3nVK>>gQa3J{8Y=6nK$nq-&Zuc+{jI&SZaOkr^tvz z2Y^qjHZ_lt4eWD;!TiV>$Jg^Mu%X>07b~<`S;$izopN28#C;FQ5`O7f+-flR`78vw zGkhaKgk9uTZ{4lYF+WVEeX%DiRfpDK3>cx1-ABU@?@)`U%oKPef}5IjVHz5cSNpMp zseedVSW_SwC!F!|DIPINFkSs=ii%6CWrQ2HT(K`I$}bHmpPdx~vA6*^00fI1q7ERs z)uUzb9tiL5rvhyRbzC@OQ%FRV&!}5rdcEpOj{P&lB4qqNK!iq)- z2Je1TMuI)<%Ywea0GaRo$8WwtvxU-ZA#acOyFXgyA^-q)Z@pi4En25y@_!#k{@W6Q zA8KqfdqS%Y(9QWF;^V0g7b3i9CyEcCfEMvicSVll7)B*p=5oHVa2L~-p&n5!PcVAx#^|vnvp*9Ct%AN^E zIyDtidwLwg7TXcP^MkeKGFtOa#cG1K`4S_nz!21DZ%{OJMV!%ta%O9{60d@~1N}Y@?7bE8@#C8r28Rv5}fuDx#nkiHYqgu}m zkP8=xUXkEiUx}V0a<- z+_Sa^2lkXAxw?bfXeo~I#Yd^@w_vLO8D{sLV7dN5Xbaxq%ln7Ry?KpNsP-p%#v4pM zAHxXD5ATkx6E72y%fkgF0@L0Cv$RODY)2Y7;u=_E9H@TG+y+16#-Bhi9M5%HU-&OI zANO%iq@~RF4IP8&5%Vdd$5FpgX7B54nY^PA0K8aOtHdQX5ewnZSBHg8mALDNjuQoqwRahLj}~Z!7}vK*Y~9z0JdeWt+52GM9atrPK5pN z&eVg|IxSajHqy@7_B>r?saj=R1^Ca05kXHm#i!`|j~p*{EH4vB4p;7)Ybf*SRSiL{ z@v4<-Ri`$aPVxGe!rh!D?tv~QL(&Hz%WHCxaa9@6PsZa;|&Eh3BtOhRoQC^ep~ z)@_|yGiEQa71u1ekP@Fb(TNVED?JEJgiui=iAPd=HzS z!N!}*!Uy+M)P49v9qMPc$p-~B8bPto!i8Q3mVxq#sLiAdkU#S63fz2Yat0GofR-a8 zI+6*Z zvk0`<2PY3~C#l!C^QMlrO|&O?<1w=mV!mY>9oYkKotj9Zb;aqsKLl<$Z(s;2EDA@L zKAp$vd3T*Mn0Qc*7_OXj(O!2Hu*t#Gb2VNs7yXP>hQC8~D0go>k2!5Le=Zf6<#gPL zoQh$?d!{5ExUBJW#P#dfLH5)Yb|;+ndlPRiE3^>w0(@+VHx&UrSV8~r)=XU7$FP=1%PmFA zMx_F`3O=woJOVg*7|m6#1kL8vi8kI~M*4|`)qP=C$r0lU&EQ=VWYZ=O01K?3Ze%-$ z0eUV71cph)ay93|KaIGk($3dQivB+f&SBilw&VQ$@ZBwYuxc2Q?Aa&h9?F@n+#m$+ z6!WMm2IKc%78W|;KzDsB8kL+@ODnCFd|QB9rq5S9N~^;&hk};=0cQ2hgOk9Mq=)+3 zr{ARa3LstuqgSXLCju2Xa2qQ;f#7VPxE!Z=WycC}+neWF8x--OEq+2$&dDd=wOr{x zjraElo{4m*K*#J0`r(eO__Sy1rh%uS9KvaRwdX!dZ#F%58tcui*zxI?e`q%m&07|h zF;=(ICFe;Fyt@JjY=cu|5$|zI%;te7ZbWy`Vt_s%7m$9)5g#T6ptS2W_sB3*;?@45 z4B&wEGz>M>IO-e>A1sSuEq0beo!5&2KY-1Hpee7oSkh<{Cj2Gr-EGI~Ts{pi5lhK7 zGk4dAT9i#u(JZ^_dUYrx+&91)xb9!aZ-$MQg4-#nL=kCC)#$gG>o z#PD0fEV~wX6&y|kPk%T~S%0WIA_%OSD$d{P$Pq)(v+FO8GOasUrh~?JB`UCM+Z09w zb@|v}3WD2cRNz0V2sOYUS=|JV(G<}7b}5FA;qLBeo&Eb4QS5U`>&dPrwNNI;4)V^F zhjO08MB*_56KyXqdeEz{r5S_n#YHB7@Lg`^Du^p6!fa0U*p8 zLs|?_DVeq3PEN{rgD#p+O3+0oPJmV>>E1o_-Y3mJf(764W%ct+ZFa`ali?P06EbtheD;T>$0dtLg zGt~WQaKm7;d1hY@*-lYh04TMwadN6%n$h`Qi?lLpi0IV4J^%1uUH|}l!LF{X3;>8E z5W#5pVlK-Kc%ZjzH`MYU3AP0P25wv5$0qS?=Ca@Fvf`uEqeN(ua^Ym_o*N0*%;{MW zUNFUM$*&5=2hX7{oo4=^N+D>Za5HRtfpYll>S??c@()wYVfB zWzd^w-})d7@!kLu>)is7^GC0Ca2utH7*LkClyX zac0Sx-a_MWEIjr)v?v-bHQ;Djt70e%^u^)M)z^pLVv34ci0L=)>ew|(-pli?ys1oX znZAOHb;ABu0Ly-EjZc|jH2Oj$B951?BTc5_$PYk>`F)@^%g$w*2-Z#lt(rqo*u8$` zfZZtzv;6x$WY^`wvPe}&w~(9AYCyDdBcZ>amJe<`gs(G0C!)(5m(}LRMAJDoS2<&0 zVRNMgcAx;~=H7K)gA0B6z*rqdiavtuC~dF#XA-ubWnpJgIJmter_>O^)Pbp+m| zG(ag1Wks$Y1g6~K*CfMd)%n;eFbaY{=#{Kr{7f6r`Jq>z3w1 z&ym(9&4Jx$4kT=e>ZBL%k?tHYtlOs>3DPxOUz`9C@1pP2xKsr3tkrqG=677HsEvcJ zI>wIf2AZ9zN11;qT%WOFQS6sl27wbL1X4N#=x3j4LEuRTcx1v6IKow!g%*j|u7(F^ z!NdlQXr;8>N9%KeFcrqa%{DB&&Q#!yJdW2D=YZbp`gQkmq#nR2J>biXd0m#!5eDUh ze}(OJ*(gEg8;#%pQMkr3>XR^Pxe^1(it-%p=Tvwf*^JX2)+|O4ETb+dfYWOuKL)*fsEUjrHUt z8<_I22Z81xE#zU!-RX-!{etdX1rEe0F!8{M(Yg2E_*Iu9+8bnent-$u*nTVlnCU7q`-gNu`UkR7 zMi!b=tADK(B7}kYNEH%3pwE4%oPzY<4(0szBqa(1&2?glAfW@|my>Kvb+BvWTOr>) zpfR-U?E?p0{;_fs$W6&g;^_g9prki#x+Klp!2<4em}ktYvlx zDp>HOfD#>0j;S*}la}&^!M6g);(B0bxBKUUXp&ZpfP8TYkV+9BUdGhTVHp~h~;Kv~FHXNQ_QUO8btEil6KB5JGxA$#I&F_2^1`yH{hFX`y{?!#($ z<8QfS>C4?`eZO3bSzTP~H zm15>_Mr$9Udy5@w=SgVtKFreF3l7WIZ-65j%ye*9RvGiz9UVK6FTW_r0}H1gZbL*F zd4X*Jnx+7XtWmHHfB^8MNcyFZwVpk~PL@$>FoBe`Wi}d|?fH3bfN!Sb_Y7nsUsG^s z1SjHTCOQ!$S%4Zmn^*-{hSzZlDF@6oA!F=b9ZgXCzXDqA(9nTTmJl}dcgv(M=9ST{ zx9@=@p@6BWc7k+YW8Nb0ac`10TvCWrlo|RKNKJu2YP!@ZabrM=`JepwDIDIc@K3@y z&UBpMDO}tIWFy~3gvY8a^DHjhain@K1xWb)255eNE3i?6=OB>TlAy(ZqIcS$s3RFq)))IZk!DI3Qyh+;8e*$2nd@$#Q~4GeX_+FN`U(A=PqUH0p4}nzFXlP9rchK zB5a|>anQiUJrddt$tfY7H(N1Apr?OklBvfYUSb=4f7AtK_0KSAq?)XUgU)CNAf|LZ z$^^6rRD0y8;|XRdmzzoWfl_e1gJXeMp4T79%*(x6uHiwt5}i$cVj^HlGXV{r0pWC@ zhR(VUzHtIy0VJ|q&(@Cn-`o^$nY=W57L9B2w9?oUZ zk?d+Scp_j8x9Z$@J7d7gIsjbhEno+LgyYNSTm;M?v&0h#TB(-`u#B zGqH~I8XED7C*`afuTJksHwtxUgwM@@`z|cXsDYyg`tvThnV12YAZ@N(>dwG~o(A-$ z*N3+}DN_<6_}c$x=08Sd8-Mbf({pspr&QJ2)?x5!YD{FgJpxoSEQ zbSqPU=7Tu20qXz6(7x1A@O4Ay+h8@ttBT2>Q+or#$57cK+XHQ7j_`Fw4L=7rHj;bR zPwY(z&#hUi29lQ{LI)u^47D17nn(sNAzj;(pXNJ2v`kGK6+y?#SSgN@#7-@;CUkfi z5`{m`-_pi(a~2@H{hot#yqY?M`ncsEZ-7!@QT~9H!10psYu`b(7jC5BDnzye&6DSh zgf=OWv&uDIctrrv7`PYFHgTR6Y$6db-D(6#m}i>MGWzdNoqDfC+v5Fpii9QI&tq(@pFUK z_*o{=!n)xB-RU*hwTjR8*@2`-fj10UTr0ojJVnC{?<$92#wE;J=HjfZY*0H>GQ5x%3 z_jD~7=Kxex*P|gP$F!dREa&SDSyUOUrmoEQT7rXo&&3x-H*PkHjp~S&V`Sgm!L<@C zH$t*P$lNNoPIJ50uq(Qwa};729`ss^0+vULv4AoT0rOz`r>HbS1I;;$B+9=!P-2Dv zrm`dz|547Po8zaw+8)Fbf+mYz-{1P_oxhpZvtXXb1dxPONZ{|eD!AZ~{Rnbwfqh-y z{V{QD4A{>0+)(PCns#|lF#pKf-UdP)aGiYm1%AKh!Kd_5zuVZu+ufZSZaIL^jdUI5zu{N&tuZUI&*)q?i`XJ0i{vc?(&C)b}sUM>V4Q;1?hSOe5R|3;$?8cSGB z=;{x{uEF>J0?V}ApofDg)kX-jfY<2S{5KIDU~oV)1C4J=x{eZ6lUP+O#?1B+ud|?J zv@8OI2bxPafR<%p+ye3EvH+9K6-bu@#RL_N`UBB28VJbS?{5G~V>DzvCvj=qzfAK0 z?kCvj7R+NmO~cchq9}PAr5c9QJCOaDtN|4Ov9xv`No@$* zdYzVUh1P>8<<3+$5OY_1ay`nC8`v)gvLR(%XDT0}e@vyqXx_@Wjn~j~Zb8T#rngbl zoo*tE<9q)pqYpi11zw|x1ya>ryJGTzvJH7V4F`3DA^ZW1J-|1o4b)}wj|SHw)Us~{ zLy{_R@B0J3^;d8Ntlwu8)%rUAi%k30c*WVB+scGv;d62|bL;jttm*ZrtJ_J#yHV_i zrsu$v7(i2^yCJ7VA4Vj!9c$f}3cww(V(bAY5_pTCZ1)?vxQYGlwG>@&G)rPM5?Ld@ z*hH28K-1mli4!&4>Pt{lISHm6zUATr%`y0gn>OFc z1(1kWOE$>_rZ;UzH(k;{YuE;)Rpfj|2)?ue1Sb$I&9Z^X8$DR2sm4m_ndv!G1H~Nhw78ADQhxOYNJg6z0;7|cR~poJ z-UB>Ti0BQND?ZizX3KWS77Gjz=(2_&S9c7*TBW_phvr2VFSoya>0t>(Kaj8g8&0HJ z2aNtB6$3a8JK$7c3aMns)(K$q@8FpYQvdHeUpo(G&-CIg$&oUXogSvg{YR?6X;{{v zJCH{Arn%{9fE&|0yu$_bv(iWP7waq8#DyjMFy!2x*#U{u3<1C>Dq z4!qY-&J8^l?fNL0R)68>YYL2wrMSa~eIBX+6&}u}Nmm+ufD}dtaId0nxv!w4B$zP& z4mw^Vf_ht3o9qXW%my4|3P%6Y2&hC++^V50NqZ!+!wUi8UZT z%fRsclC<@cy^w66^=$@zFxmv$*M*jG*S&>JR*1DkrDE`2sd@QRFf7=_n(-!80p9uRAU>@f{2(IGb&Jz-+a< zbog=%2kZne-tswmZyDV>?8D1Zoty6)eVC+B&=FMyxJgY0^PXsA`hr8iA}D19L1K|R znh#D=5Ch)GyO8&r#KS>V5J9G8$b8Z3&6fx-3YTKYZP09rUsrG?Q*-;Te)nwASUD)@8Y zF+?$-=kvvYr{l6AmI?3(&@ggCxtzM? z5xfdqA`#q{Kk;EH3rT&T;kt4K6vS=&*Q&!qA!hzRSon)^P#+CgWo+ArcL;DO^8lnX zfv26&rheNI-(~L@7YI`QGvK9$)bg6q$VbBjB^scPi9Z?u8Raf`;F1=2T1qffeC@49 zrv*(27a-j9?I!5?4%AVHU);6(-}z-@I;YJ zGV@p8ik0y{6M;Xadn~kX%#;#xK)1Gr_Bt+;w>n1##0R#~zV3wf(m!riUAI)s@;?lK zw?)B5$B3hrfqJbAd>v*<;6XNZZ-1352<5sv)dpaa2uZ5+lpWUJfEM&R5QlmfXN!x$ zQLx<_lypRPc7>-|dc2}~rb(b=t;MP0ef74z#|;$?vl5?}w@wJi<+Kc4Uj>A!bptp! zfdl?^7I;{x9RO`M(4VIhOtpaHis(()7k_(en)gK80pU<9)jCZR$D;&B=AAOIG03w4 z@`xF9S`s{?1v_xDE#d_C7eP&mI^IXaoBsdM_T2$d-D%fk-RPTG_>56w0oI73BCt}0 zp{PU&A|N0jU4!%{AdHj&TqDE=3Iftm1f(Oqqalj48G46NDMKHn!vOQ0OWfT=$ouYo z`@Q_5Sb%%)@BV)0InQ~{DQ4YWYz`7&*;H0l;bglt(wU$1-O7WwJt!uh`81yrI()*? z)+TB=E$SXFGo|vE`!T_gAb72QOW=_afSDP&*Eu#F@XVOVbRN}1?;vkY|29GWPzCNe zMZ>Fa>qc6i9dcxJ2jdv$Q-pjUlasfp{M!N?sXMyQixx5yQ${8&6KORYGnkA+wflkwiJ7)t-klz->Q~wK_pXD8S9|<@nVgDznXf60 z#2Vr3?+e!MyS9$I0{2BPFhBpWPmes$`~`Tr!PrReUWy*u-_B#A^p*{0U?x^ket#&< zWPbACTn|sKX_>dO&lI`>YiGA>{#{4ZvZa-InjDeX!@#Q<~#J z=WpKZQyq_&S1Tc%36fgj=Oge;pu$_-hvVw@E2ry=8~5nGm=QiU(QUNSTCjH00O?Qd z>x&&Z6xPXH;n=;|FECK*pXjwHIB4|mT7ZwYc=s8Tz61;N*1C6dw{8W{U#>Eq2vJYT z;F`}lq6H4DVz57J<)hblid9>iz8Cj7phc3gpyB^SFZbHIQZ!$fs? z^e2kszIV6Nj`l{rHyrN|8xCEVkj8ruBejA!;JDnI73=#b=fUUE#SQB!?>{#Fn&YVY zp;dhNmvrUJ64e!=|9ps_5R@wUA2wP#ExYugO2eqM*DD|UiK2}SeT|14W&XjPU1S@S zsqs1`3xOEhi_ys$F&k{3dMH2k1#>H3WNYRJreZ>N>?$?j{e~bZ?{@WVUehW(2NZ7k zs&Tyeel`11UAx8kyQpC+&uPz8)jl$Y7B!9gN{)ix3t|-+bP2^Mc#03V*jelArC)$2 z)vf?IEJ9;ZG18{WeI<(6EC*_jBqU17EY2I;i;s$I$w7 zXNRhNAE3{Kh||y{aoEhyZ`>j0@dFsn4wuC#2C=aVRKhBz^q$jjGeeF&(Sq-#uATcP zoxUcvp_F}+cq#`PpFDooyVwk7IUI${;k~CeR6bQ>1o8d`3jE`)5br|Mx=KLUiB7D1dCI?yZ!wrBO_M^>m}(6{o1+@CLUY~=`(tJhDoWii;K%PA;bF-By!+^J^#4$fBvR6@_)Pu z^V2ezg_^i--H{_hlO@SnwUh)#%J`8k*uMQa)}{A;RPT-L+%>*jgyNafmTg5|c9l7M zAZwuLha+f=yuJImHY>Hgz2vS)U?7)2T*OW8%CVWWzP3y=)l41V2mKDyqpWKf`0NRH zIIUd5UgR-(?&W93n)WVp<;;@N%UL+sQ?6PaE-BJb>WR?_cX9%n{3}03@5$Dd(GoZx z1l1q;l4E2wd)KV)KR8=Y-~9i5wtktt=?~9Vw}0bo1@>`1MNl-|G6X|G*|e~I#T+Jc zM>$uE7y)ro^gP2(-tO;D&ZL?CJNwaC(;ni!NJxbMmz2HK%ku3lT9h9V?nx zDq>Qy@!kl#O$R8C!4>P7%TU5P5v%4NL=l|*@jCnoRqi{>5hS zRryz#9@gm8Z6WT-T2AhJydk24sYSEzs&FqDpGS;jY+4u zu8~6wzs2jJo4AT~nz>uIyQ2Y~?V8jNJ$&3vIvlz`{6`1fiAVd%T~o(gr*?)CC5WV> zNB3h9e0?ypWYn|w;HUKy9HP>MFaE5IDd;X=%*;PI~qTR1#v@=*a%n*EkFv_vEk$hM+9!b)YiNc7MfYH+U6ZyzzwEy8$v~JJZ z2U(#!M%S9HyNkZfH0`?2pH?K;kz?cOWB28f5_@U8*tSfqbKymF41fe+pw7?am!FQq z4yn_BWf$1oYN?x|ATYRB>Rh~?Ru#G8$@s>VSsur0!EaY{Shk&y_K zH(LygIl^SKYh(xOO!!p{XGsBo(o|tE6!S4rCLKAWK8xJ5 zER0BTa2#93s)OU0TYWCGBA*e-a|!?0Wx8#SxaX52E}fq_uHP@0V+cfx8GlWrCK;fsCd9f^RZ!_zcLe>A>LUueo zQ)ycSYvd4pkOz5h=mE|B9$8Efy%)mm5?_k_Ls`0fPRB)^h3Mo7oRrltNc5H4byRc6GWMu_0#N`NN;&KP)T%KcBX{HCH=ri9%Yv{BL`Q ze%azSrv2AEW&wN2E>LQvy<)9OR%}TOO8YlygKKV({czP6qE@Y>FvIi;LQVyBqtc@q z@rGeU73(%LX5)}k;sHvofH+N~k7q-f@8%C_Tk%0CTtkE?7W(2gt_NTKVDQR!@lqzk zwtJx*7P;;)AINc^s|wiU@=6w8rDmw9wECBuMz5c$^$1^hZZTKJ6V&UkM1{_wA=9EE z2z>TNjDzZBx=g^T%Sj=%XuJT`%pzCG!S>s~;#5dI(S7D)=8D_2_kvI`-f`rIv#0J# zn4!}e zdT?7vRt{t>X2d;VrxgJ|Qx}(>yL6L3CV1&D^d8Fqf`{M?2ja~57Y{#N>nPfzR~ZCf z;ImqWW0oPkgb(u<9`|fIau{u9471VpTnO6i*HsJX=ZX4Bj|V_N9Mks9ShJs6oc7E- z1(u^{5A1HvM=`k5Q;?s(3S4WbOphItUr4CK&To8te55Ut@8u5;RLQbk>$9c}va>0i z{5%R#0(wgifLTqc;UTajvD>d&QrL)zm=zyh&fnwd{5KSb1onAo?$<*`{~~{Bz60vI zu7mufboC!lbDleQ?ukle!`)53edhT8O)CY?}HOe|HFbsdHy znuIY)hE2z`mf70@I0GrqHO328yuL3_93}2dH&saB17m$xPE>JlWQoild)pNQ@}ZUS z-1Fk6D!uoS_OQyqTmq%7U@8|@Y_?RX;XC^oR2lMjYd4Kxbmvt6&+E%gXWw7DYX#S< zFr4fCR1%ZM>edd!VumN9OY8EFx;j_tvENta-#A`Qgq=Om_&{ovefL-)ls4VV@_|QR zgIs@x`s>1(FXa~Ck=A9W=v7yU~ILVjcED{%($*h7L?g`d2%Rpfmm!-{oJZz=A}#KX-dE zKeY>Px`FU;4JNuWvB8Ei1J-`r*EPUs6%-gDEWo-sjozArEkr`?L88cSbeDbC6lyU zF!A?dOmAfkCg4uGe08-`;qK}+*c2C6a@f=Sf2HL6u5`0cT<}i>@d^jh)y#6Ei8g2? zHmFT`1X_vpU0d64K$*ImKk(S^35drym11$sGAK`vb#?Sw;~5bpNt#jk@#~h39X2z( zil`DaAd3|?ny=5ZZl6@g(Q=22RbfgqK%-16B{UfG1_Dv6&eBV0bKJH1v4jZYnw{8yxwMW-Cpq^oIkeOD9lKUIEL7Eg^R2yIs2 zr$lpbyf<1kjHEq4GpV^3PCI9a!su`7Wy4WTnsgORDN|MOwx}Gvc)s9DLXAM~nX06; zR2Bcj=hH>er*$RHRZON@*X2ue&-R_u)?WUjyR`sBl0?{pKn@tlO3@*4yAPSN*qW%)2LlSuV65HSM_d@|{N%KR-!yNgochXbbNCYr}&$rV>@s z?5gpz(UPQ`9J{26pI<<_8c)>E=vSR#FHta|&=u>u;(}`TKS*RgO|X63Po3>xp(Q%q z5mQv8U%FW!j%Xac8Bkj-iKjO53^pLhm`?c0d0d92-E?&%g$zua%xWts-m`V#q4=Y1YWXLcc_ni2H1`DMN1Z5hrX+ z`UvU;)H>V0B7jMtu!Pi9%jpuB?OuTLM-vqMeW0b{lWo!2{|0Y)I`teyL76Dw>g~Q$ zS{6JXb=^H%y3&F5SP~amzok0D%?L}#W}8507!q7!D)QkbbN|{J7KRz_!vf*i4q5jL zJ-WHJ^*KvF8N_ z>x{oA+QMy_OxfOvKd8Fy)T;j*$Ks0f@PU59yzizN;-Y^sS{4EydvyNrHl>y7o}kvC zOY#ZwqDiQ~o}Jeo`sTTtN<43u64&Fc?bMkJrctzwtLI7!-(#d&=Xqa_?j6OB%%GS@ zeq-iWPnpX>=X&`&X)Udx6$_37y(IKSL|q~rsdD=i3!HXceco=BR(u~5qKsd*_#hK( z>s^*I_6Q75-9r29lQ(er)7?7=sJl1z@hU9Of7-0GE6Eq+ix7S1MK`j4DJQnHxROY^ z$`?9JTCPFZ52i__Ar6oRiolTcmL$#s6J@Gq)zGHEyLd}1nal0W=x zjY^eMOIHwLxhzeq^Rl1mZ*0H)cVbcL(UBE*{PGhte~*e%qLmp|mT3g+ML{f19qleA z1qQ~CVcJ~6QvyF_g(BbG*Aq1Mp)LD6F{@1b-e&X$nq^{lGuOE-^=EARQVcOJAw>P0 z|KSx=l(ivi<8ljO)mv8xPj&dO@hDOj-(l{`1}QM1BVC2L6>anL`-ol}D!1!vn-DEE zrs$8eAP%xqlak3;`?jpXSRBJd*Q+M7(6(zHSu{eJqtKI`$@i@A-WayCMci)57^XwA zGhJjojP)wA86z8^Mzk7_U@lm}IzbJciHFaOOEc(^%3ZmCT(K25G4IUe{J&Og<_)3k zN&5gD!kUsiLvC&mMo7cR{wHRL;Lq5_pbH7r@^JW7RP;Ro+cGW=1qzer+- zuI#NassbAlZuU)$&fm2F5O&m$P$n2c)LUEAU~yXq&#ecT?${6GOA6$%tWDiJnI<4W z0|0h`zMYvK#--UNRUTm|yO16IEvWKCYA05#NVuuVG^}gSikr5)d2>tJX|S#O?jPoe zT9oFw)Ij`N7elLpOJ`~u!%ngJqkKo8@@G0h3cNYH>XIAonCb0wq;V1j6mc>o8?dxo zr=`cd^3gr-zpvookGT&~l5=upXa0=Xwves2Uw-}N4J|WR^sy+Zzac;!pP$hPEfHF7 zHr~)(ZZ@V+mHtI_4lcHK)8HDLIm5ob<(^~@GuMu8U;FFnjJkBuiWQl%PbVoUDYQnp zgV=pqR|_{ZF9vng8)9Y%a}Q4HR0};42aSqO+qJ;2I&vj2!1t{|!8x<8f{ie33b|{w zS78%%yf3Zyt&eB8xA&Uc)uC8z^qS@r&x%YMm1gIWDGl&x>AXY;ceV-WMnXamjM-=* zg1OzW^PJo3uQFF2(MP3}A70i#B#Dx+L+?ETH6ZO_3np|;5{y@Ftf=nq$kqlWVKvDu z$e)Bx_c*2=N_*)`qil;QLuHxLvHIy{busN4`{z%kh;L+i{$nXmo1@#bv|xPrc-?aE z0S*qR_Hx?hpnGQc$Sv^9&9~So`f(Fx2=%}`Gkc=a^m2_S8Z|ggo0H2iN)Qd&aIiCf z(5!c$_E#0a&NP<^4-A-JmEzYf2|B1z5(Ja*sfTC6#3hLmj%bAo3uf_y?Fn9`o~=E# z^-Hl>-}kAcospZ(cTl2z7=`qRaBNQgB@`NlD4ndPWS4Kuj36PI{~v1J?{M&7bBy)< zEQ|kRPx3&`jKj&76&B`KWdPh1{VOXqGEJ%~&uKYdZJ`lzxiTsuT^?OjfLlmMZ@Yq= z)L{gu5uB)LcM&@fjdWxeUI1u6W6-q#zMoGKBUu-T!i2a)WTDZCzZbXdETS6{<+BQD zv=?D4k`e?6BwjIsNPym10=8Bn4Z9M70g3Lf|T)Ug_3Ws$pGj8PlvpzHk# zeQdU+nm3B)YKTLY_ZcjT>_fEa(KM5xM$d}AV9!ynakCXaQFLGNcgj;6oJL*G5P~@P z-Pcef3K2r0WC>vK$RFl2h<_*cxqTeJXPR)I&YcxUBz-=3Rr{KddqrZya%QV5^TeDqRm>j|trcK}jjf+x5YH-EN)_dFN{Ef*7FEW8?aNk?+TGsE*50bUB^N)Qw0JzRu>t& zs4^{5-l1M`@A$$*Dcmk3VjUSG=%`;r1c`Fb$cxC*{A7IHu{>i_LeXYh^np|(yQZda zvVOe?=wpl>b~w!S8P(j`ksF6r`)RKAz7KP{*R|`*a&R22U+pN%0hD%w#S7`pN2>f- z*#8e56|v7Dheo>4IYk963J}6emT7;0oLoNTGOo+YuiDaG>To9-;+ooB_h2I|7ABGQ zI$4L@TgjhRMW{umY$c`K2jqFq;6FDu6^HB)F=ebb${XkDvFvHAfs&^c zdRRKe^TF^{?BJr$}SdodXAL0eJfJ-*W=gUyTT?^tg~fmQJ=}o4$6Gc=bEM; z(PGG=l(4$&#Ev#}C+9>y;=IExBMco=rG5ES<-zz3w*KzvNh4#F=t4IvtLel0lvmED zb4>Hz-c}s4UPzkX#DvxNBtSMmB|-`YRejlG#aN||wZ|z9iJCKLIuHCB0}5yR{--gU zXtj^x&FB<8(Sc3^Hpo(ZsoP8bdH~X^9g^c=Arj1&cDp*?ksL2-w$qrTW3w^KC z=0SWLRpM7>ktaZ!2ZX{~pu6)-o>|=GKQ)vjMEv2#$`hk9@;nda*s;V|vSWYIJq(9S zECXccZ2!2Gw*4G#kPMR;<#J0dAZ+gu{^0wRx)tG3Yn^G=oSct-0o`snS&D?U(PIGqcU>iuxJ;{{A52BzgmPhO=VkZoNb@u7}vZJf3Cin=(cQ z84TPc>@`a(A%(n$AI{n*Qs&)Hf|9I}H28p!CYD%M6RpPVcxo{m0V;(&ju6@rjW2Ih zirRJWOR@V$$HbZ(?7eYD7m{+v>Mw>6!JN$dn~>MmAS4GD zwjZ=uDS75d=fhrej+=7D6)uoubPR*eJiM%M`gBeTt1bP}q1f11i>5?rrQp=p0=ajR zbaow0CvQ7^RT6XIcE}ENscd_9jj07 zGTG>1+F#9ZXfuhyc!eEePmLq5{%{JWnYkKjbtP1@xHB$?VKnNJR6iUa8H%x8zQrb& zPwyp%bF4uZ`CKr@`V~Nl8W3M1lHz=Peri~gx%6s!?`ZX=nDOz9kxW&KNOuR*=2U}V z9HMNS$>jEKBYHiZ`f@wl4W~Mf@Z~qph}(r$=%eb0!t`Z$pT6FjmAeYT7Q=PnOI}>}Q=&z7IlSJpW+I=qQMuGZ#;UnJ+bWGcZ z@E~MS>;U+(3!@0M@pymTF5ZqFGaL?c!6zHIT<5julzx%&xPk&H#A$cOGFRN{_bshW z6a^<#z+P7d$J<~&Vgg^dAZ$Hx-HXJI(2`oCGWyqf*o;D{T~(hw{DtHjFXy|F8XYX= zNOEJh$RvQvrMEw8!xKSbc}6}8GDvjX_#t)fk0WN}p9`KP{Nm$QN)hFgRWlwZ$a9SR ziG`{4#yvYTr6+^}=fk4P+bwauKyTE5g**@U194_GyA zx9m3{ca0LMmpP;@)6QstWHo!9f`?&-qT|YMs2o#b`}hZYdyP{{k4ay&k{$#(Q;{`b zxzzdi<)ehe_SPS7=1kYQzrVO~^c%{Dmo1haOTB!4N={BrHFusFGhtE`a>RHnsZB$V z&%%G<-F6L!*|&Ug$s_MKeDgjK-M!3>mFW(Ba2@a7hcREvqy-W(Xedj928ElbPZTxC zubMvN;8AN|g_YFJGB?zyEV%HZ3eGh?5PdLm+JIL_+0&K9<_+0Wf0Z?2O@w%vHW;toC^yGUm4Z||D;Y~)K#&D)_;e-b?}PdX(x za!+11#D`~zf`E*JiVlp3Qbdy!=eFaz2H(B3g9?uyYCL1;0!z^hPl95;#CDY z?vbZL=}nkL^1_wxbc7rKAy=@Oh*ijiA#e*~^eRKM6$9epG8~RcT{$YA+bty+8yjKO zY+4ftf?Ey~NOf+POS(0>cq)ihAfd-taeUnrW}5t(k}*oH4JS%S;Ueg9ys> zT;bPW2ErP{KsW2CsHWzwfJS-RF|; zpF3l&;%XsHKriRpb-QcDjb?w2hW*{`$x*i}9!nD)yRP)leh3&JwTp`CF7~U&aMab9 z@drUc{YgeL`-puj%udjvHlO2IS>P&f)=nmo*IvQL6J=7>-|I#%{1({5Vre|Mw6TDU zwWoeq5OIc%^-0<@omYGO0*FK-nhV1q@I#MX4dDQw6jY=!QJtvCMEfnZLe+DAG1+kO z1fleVX&gDjW6?Vlso8AF+uIiR5T&eOes2M9MIke_^4=as|1SDBk(x=7s?YL{%KJn{ zZr)3U(Wge{MR`sCOAE6%C5MxR%rKDOwcpM_1h40PnA?cTExav-X|^e9yGUS(3^vP` zA6AZm`7i?8H6=K*L;x@B^*{`GtGg|2CI{|gAVb}kl&Y;FX>vJWrJ@v%YucI=!X-tL6+1R{A*DbYxBt2Y(6Cq68rg^5qD8-X)oMGK| z>#*U17W~rhP0-{V!(_6Z{JGsm?!(p~9r5gPjJPll|M*B(@usGx4jH@dqU{=**+)=v z@|XE|ip_M2*!xWIDq?Tpc7IPM07KN$m3S@>JC10NRj{?!E4{3N8vUzpbq?}(2=PTs zhFlqHJc8$v=EMY9A9;P#P?%Oq0mFOODcJlNs`?}BqzJJ%bYzj}!%Z{<_DE6V*?5&P z{$Qw_W*8smGC`Qse^1a1&{!+hL_#Wj09Hn5ORZ&ek+sd>zJ>0*t*_M3dz@gd(_OhI zYr1o6W@yC9xw$1CQ91pQZu>EP)W?+gkS@SWjjK(>6XL1bdAgBL`EiF# zk9PD=cQ7lxJ~IN*uQ?SdMjXyOOQrt2FqEDl4iut5a6W(#M!tBL33((SNH7TU09S1{ zlC*}y>A)&TP}GpHK9;cdg=Mu+!bUptj_@ag=UhGMstP#L_oX><8cp73J9z(x`|DcMvA`#mf2>`Pzw z_|aZ(R=A>Q()YqC+<_DrX?K|zNAF8=b)(v8- z>xRLl9_031@K?!E|Dnt~5gS>YX3{vOH^0TSQmQHOzCodsi4g}gJlujiJEvkovp-wo zTX9=Ru#yO+$967ywB`A=VmRPb%^^?(GX1-HDN|#VOF7nU{s`eZjy-6YBppM1DYxmr zNsqtIN;W7I+^f)iA3jF$MMZ}fSv`%%GXhUMx6r4?yui2Sza?YrA-8ME*YoR}bQ zy@R6|7kjp!YS6mMWGS?EGS$?9H7RL@$b21#eW$-oe^X0EehjhOW1&tED-|MwDi*W! z!EkC}@(Wru&MLt|>vNdyxO-01rkYqlpq-o#9cpixt43UMAC6b-O8OJH{+rm;>Ww1S zakS>=EJ|2^`jlnGHyw?(L8XRjp$H*$@x7Q~f9gbk^P)V~jAjcPjB9h94DUGXqGDED zQoHYgD2}>VCP;hav*~o0cuGTolZ*cogF z<9UROCLD6zu-PIlcQ|xvg2odnpqB{l)E>9^@nZ_qFmmH5c3t;w-7?0H(usdYKWVph z>!@Rkfka=pV@ZTOC)Wdz1GZvmN0>j4#VYs(0J=B3!?9;S)JkZ4yNkt~qKGIG88$m) zTrYWZmVQg@43&AGUNZq}wRGF_)vP>6tuHXMAygY49xHlxYXW-pdmj(q8j&d{hq0&g zQonBGc-LBHD(K%~gP^duAzdWFsb$GCgT15xnOa&6r^owI;;fBuga#dU_^R}wLYjMj zD~epi?z9z7a&dTZY5X@>`iJ1i_o!(dCu)pEu(yHV0U2qGR0lN9tm;eIN8o>O$G~Cj zd;3fy|7m5e$D5rOnSJdzyHqrrtPTnYDEnub+naa0*z5VW<6u}{lVd)vs-;_uu@nB{dNW_d@x4iMe@sod`#x_f_v?sPA4 z8ZFLGRp;5uweDjO2O`YsW%0Pr5*+?ndTv6ZZe!!-XcgRZ;>cIT+~Eq8OX*PW zJ7RnalnTGFU3ViwjvWZ`|J;OUFt%R$*qId%{ld(zqs!ihR@;$_5wZ-O?n|A*={`(z zEzV8GlScg=5Ak!VI8B9N;Ngq8U-+ic7@lgb#|r6iN6Fw2e2JsA>0idDAtF&q^b+|D zW(nafMHHQk+eXqPY~F2VQKgu74av4@K@yBSaqO=SGefiwoFfh-6_Df(;Km@rF?Q;o zACLqYSGJ5*i;Yj7WF~{E2DGs)aj_=Qu+MJc%Srjz2bDo+siO@8#pFRy2Q~_+Uya8K zQ3MsZUCx(GMC=l=ZtX*xn=D(Uk^exrEFra|IFhkTyGCv!64`F6Hdg>XcmHsu$~Ks^AEj-t0K+R+7qU&9ZRB zP+EPKWfLqfO%Cy|YjoQ#8fo}#tG%n-XB@M6t39NVF1UTm;~{mp!;&a24&u23+YPSR zARcb^u%0*{I)Pi^AFebJT)Gh3TwE0-6O7s|NFXc#8#V*nS2(ZBOqb>k;oeRnf+Ewh zY2x6v?XXg5z>1%F0@%@3G&cp5fz7MU$28?NdYz#>OYiC9R8nR`nh_14rcH~HVz|hF za^44lZg(-!J3Qp>;}_}JW@2Vm|0IFHWr~Dj0;c|hGvS}TpRmM?Jb63Lq%)5P=ObH; zAgBHa2@f4sGG#W$lvjw|NaYcHui{S>*I->=NG};yw9aP0d#4w0-J-+HBU?VbWMzT@{q_`lrp>{bPl8-TNIWRy^nP z9S~MIIx{y$F4e3GagsBlQ(!PSiNjRdbG|`@xJ4h{g-msS-Vq=CU(VdcQHaXcU#&{u zXbr%9F%>L&pZp+jG&n$I{*08<5RsGBQZQ36Y)eD6rJSVI7Am5L?p&|MLDAS5{UF5M&wN>D=&%-w-~x90%4+O5~Tx< zy@AUo$>c!j{k=@{q3?fpe~3>CZ(s{K7yNNi8u_Tp7v34FT4*^WBxKd-IMt=lk{X2w zxKhPn!9WBrc?4oW%zc?X7c}3!oJHj3_XnIbj)J~Ag?PsEHEs+rJ{3QHL4NY&Qb$45 z#$Ac(y1JoSDV4~|x`k1^ywL1xYuwDhiY&KuP+#Y|rv3wYO))*sn9>aIDR74bVK>I| zBN0XIso>8&C!sj?t44Y0&=)B8dS9?RC8-#e>3$hlnm94;K;ZGW$>A1$qyPkLH`0Gl z$n^LN;Jx{|wRyngv>j#(@@qFqi$lIeP}2|c9k+`+t*~$K+MkP;d45K2Kdey0h+>c+ zai6NmCF)pN`<qG5US~C$Mv8R`dUvk|`}_4KyYgL)$z;piTs{gaT?Ob{9t5 zFDKrN$mA^Qt)%p$>;4T0INF|#81Ej34jXc;(=g#chN*zG0A6i{w& zRZN#|-1mYl=UV^-{k`P{)B(jMP6hV6tJq#25PuJhC_ZA1@U%Vs`#q|FaNmTW2DqJc7g3w1YRN=hl+erISHeKUX=D@FZb{ zRd>3XdEL8OR1}^2GL@7_X2_TzfeWuz?9tS;!5F;GFkB)%;ht{Q;uw|<%eB4y+No6H zWJf#etO%3X;w|DMGP^>Kl@Y)WKqY-)P*n-ALOWDy<-$u3z(D*dWd=VYV~)pv$aqTYW-=uoKYtI(jWBIh&(EQkJUlj!NhBDiHcEj8ZFMt!GPVgtWzbP_{B;?`j-3yQ(y;XzJ?)_Onjz=+FSx)A zcK62|`)0+RYi$ZmT&KwX>U~SQ4kbE!O1-(cZ0@aKO@fAJ)L!3N)`MVj)fapV8jv*D z*Ax{L$XHF(IU-rchk#tUuI0CGb?cOR$QO4#**Lo9hBx7e;U2xp(O7%+--3umP4o4! zRj_T!y0dHMIudV6kpic=QJ3|UOgBJY0JTP<92qQjf zLYy^BV#AFR@hp9F4N;MbD2;A!r*)3WsO56YsHotXU~|A$lB>H5OK8_`|%-c)RzG+{YBom`_pgPJu{1nA$o z^=u$7VO#@|RTYv7C;^S#%Iyzj&(3-g!;x;QuXpphFeuzJ)wObrmV4LOP61dAhr2;& z$gJaPl)$2*ZIrk}#k_w>(|mS|Ap(Ch;H+R)57>=SwxYEul*Z~1N5@7r9|EvvGD`%U z50tIa-e22Q2DuRtOc1A?ra{n6MArZs)5B0FJ)xRK0$}>emDsK2lod?T&925Eq86i@ zHv}MwIG*_;k6s8{oQj~wR_($38@p1hrPCj^p@H&!eag&tT|_?_8!(ClV#n4?sfl8% zqf-o92*cNKFc)5SIzV>QJ#^%W2V^ zj?6#8uh~uk;&Gf`%SIF8%*a(h(6Jt;(RN9>r4KZOqOhJG+kg zZ0``M)T%`lnFw@@4_3lrytEPuR@q(R=bBtpMXCP|tq2Oe#ITZ}6@yI;x*|79{sGcF zLWxdaHihB*Pbop$Fqu`bTGDYV9@}89_i(nx|pFR(`GJ8};8dxL0hG zZVw~s*6A7vnHb1~6MSP^=JwwE3UMZ*?M_g-_)ZQr$q;@+u6}-z4TUrJ+cHd)F#Wp@ zn`D|@c5aSs{j14qKTi|(v$E-eE%my*l5jvUuvDJ|>`xJO6-=(mKw?oFw zxoVHkhH8eageVxBPE%E=Jn-ykrfB!UlrR@em^iUB!nNYkc&|NXn+uWNJp+0Kee#x# zm1l$bQ;!P4P8%k(1h>)T+P_M7{5EH415OND`o-YoX1a1%GpNEM>f$QopZLJS>gda< zd&?A3B6{(?#iA@4A}i!MW&CffH>!E6MqiG5@g(T{w`FO|FR8HLS)V^>F<%I7$p5oj z+&^zR@l&uD+4t=z!x)|29ftb^>V~v)kvluWacNFI(?rP$cgr@Z`1kMVrzr43%dFB= zqIKIsd7STOwNFmZ%$++Onr$)k>&Zx}nCInpuIT+0-f!%ioUISoC+7c z(f)!^*58@GE@q#MfrSNw;LbJ5-`GV%twDf*6sxF3YdSLVjv-Yh1o;kj+0&K$s^DbB zi6k)MDGhe9Uh`HE9Ux-kK}e)x_2z|{?5iL2VAC@je)$A@!>;MTI0Ra9 ztR~kHE#^GEfqZ4uZZst~2xC~C{X~g-H*^Mok2rf_6Bdq?FFB$M7vZrseMYCm-30w{ ziN3xrq^#eAClJ5WSPELlc&PX@WMLc8kjiM)j9}nUz$XILL8hFCJDjRR6zLdl_0PyC zgHM$rY{x9btOyRTbaCv;NGl(4NCxo9X=TDM@>9&$d8I+s0Z}t{fu)ZhlOfAGYt`Rw z%3INxP)ggZus|Uo<&RsI4yKisX!csA;h%lyl>7ZDUrWnd+Al@#H1u7*?9)zx`;tZo6M<8_3!Dz8m5${3{D{`tipJRQJU^j`$ydZLSNQB3tD(B=zKVgmy@d2usbFq5Ygd91lv^BxA;N9g(pI}>j%=L#>IF5%Yzv2%U(zZN zf&g4~4rn7Ox25)e<`}hZ>30Pv{ek|YY2f+gK^C>RFp&uaA#@kgl_w)9(vFO3=Rqzm* z=r4XKX4Py0ffmtmLoeOyRPlN@9~6Em2d{Kx^xaKq?x%SkBs#m{l0ugn;%by1tjOm; ze}HK|jNBmM(bo};PR{b8IgSZR!SdtdRqQ~k)ck!&;3@L_y1zc*!PMRIZko_i|84k zgbYL#vlY`9_F`59szLZc4JIeB2{vP#oTn*y<0ruDMLs+sB8a*kNk?G>MPZpjX6^+B z4}$9^Cw4z0`r93U43Gv_kwv!I7}IYQm;W1hOjMGhpf|&1qxyFtW^us$xaZYVHcO$@ zSo9|Q>y>EKheu*Cr;t{0YirU_O$M>|LFF^kI&CNU_v4U*W42Xtw!th`8H%yRg-e}{ zalYCyR(?pfd}l@2LX^*M_;@SlcmM0c#y?`V6w!o*d-6}k zI&c#k{tZ3neD&1oI>^%7*ZB{%Gg1p}DyFkHmnv~5AH|j=<&iW-CqjSkU+?oLk6OoWpLVnY_Fu z$EPUeK2)8RFss|Wzz!moqQkoi?R|_-gL{leaGq@h-Nu(*Qc7A-8iH?k`62>$t2e*9gl%pmDc9L{mx&Uc!dcN9RM ziLAL&>6y;;ViRy6o(^|zm+Xl6f@8$x_hd&Qb=a;bK*YP7IE>7?MPeLl$kf*qInQr{ zDZ}x4qi%1Ol7yLUbvnhaHr`QgdRi%lnO#%f)Dl*Fu*u5~HYq7c8SiHb)N|~so>~H7 z=QHEas<)06N?&Gcx(wY6o1oSQ&gBk!Xbnf^)9!Tl-{A@i{6clV;(oTNmVa-u zh-&Lt$nteO_eC51!7|6ZK{gJj2>}$qjuHr&kPC`=Mv9MK(~W|{L$x0>nnn{05_7?1 z?S_Hkrby!C3?Z~u7k!+l9|fN9A8G0DA~TR&kXmCvUSzH{d5AKP=t?e&{&A|_t%S2& z65Q&R#L&za0Xw{uG)&`o^wEjMx;tr5UYREbAbNlR~=|NN$-yfhHaN~>4h7npfBb>B~Q z*A$Z-Uf&?3^J+uclevkWrYwuR{i+@sg=C&{=g@Wek+tsXdwziR`)mIfDSjlzw98?c-r*sU@Pg#$`wCB;I&EwN0MmFt}b#cn1 zkuheEANycHLejz_$zo~Ys?Rjiv*a~lOQoAV7I5+5mC^PohqT+F7iZtUKrG@K#rhJ8 z!j+k>jhBSQ1SaycvNniW_9m5c5ARV`<-BntyXDr_^}BUN_UutiD-zb0NkpHlzPLa~)g?C*baIqYeIi-K6T9Y>>ft37&VL>lYs8>`KfV=rX#9^~Vr4@s-i*v zOalezu@^6F#N4I}_q`G_sH`7sVAr=kqaFPAhlhslM|49%6ime94+uMWnHXv{t4cYV z7H$)YOgUm4_dpkpXzPX`K09;c#tr``oO1f{Km8<&PJ7Ewn**}U>&}>%a5aqgO^8{y zCW)`=<$!7lT?^=M*KhIWYc6&C4OEL#<3r#0H zefqR|Y7>v_qs~rgr?K710|VDkBt7d~{GP~{&`Rrm8Xm6S+C;g7)aT-hNpF$211skW)(Ep>J04w?1mc$$Uf zZ@+w{qH*GczZ7|s79)Sq1SF*>!$uW>OHqO+tD(6zH`wY+M0)5N)hY#oC?*#?z%z4F?K*^MnHS~YNeqt2F zD`tIGK~T`U1Yz8RgTdMFuG^Hl(j?OMD3-cO>w9=iW%0gsR8((#(_QNNk8w2Q#hOtq z5Rx)JJ*^%k%X#Q8(a+R18d9hc!Y0?SA815K*-Xt3KbuFahteI@@T0oj-G=SOta}h3 zc$_@7>0W%iLW0y5K410G_U@sUw0J?oYW3sCFW&s%<@L(4tKbI4x&;+=OWd;~-Nt4> zVtFCJHulY%B?q$IquSd4T6`}b3hkX;HXZgL33|CILtTYQLyZY32^vO+T0CwAk_{RU zmw7K9hZMOUF$9gJo@wfc>GBlJlst+1{pEVvX@7kKgQ+$%rLDV2${TmhTfX?)zM=Lh z3#EtA0$+Tgc*}RGn0N3dA9pj}+{rRdi@qVg15eXByYz;#Tly{M#wGfO_0k^et*Fj_ z_n1`9?r@1V@^CrdRp^soMx(|$NN`ouX9aOu@@q8WZJ(bWP5tGUjly9ySe9xrr%twY zMx{iKn%oEq+&iyCE4{S5gSav39-kD6ke<5>$|$}mN#YQRq~Kp!t$kxcr)q;hQ20V1 zuEpui^CNwX?ME?;!|}#ikZSw8p9G(1GD`aIPSxG|BGI5jgX`y0!&5pugKc{_hesvY z@xK(At);17ZoNHUV^@@YrA{f)h!$UMuV6(tpZtlh@p-OqJ9%epZK_a$vyG~9*zSXw zZOK}rhP6gT@lm4`^2+t2(0xiqY?D6+04_K_PkN-7gl4lMR|OvlpILno+hcc zhxo8t0m+~DTrmC-_o!;hdbn9Y^vVFcu08vZ@7%JWe|_{^?|kzM+CENyH4!a&`6rtUGdkHr|(7@#xuy=*RIW2vX@kw9^X*(WV6aG zTgKG-dV7OH(ryh6gDJdqGbB3Os%PuhUq7{I=yrBif^bz)KfLRA-xr@8Qe_&B28IMZ z71%{jcW>!=+vnd8Ni^;b}vYT;Y77dQo%u*TNS&532rRTnsgH6?0Je07mxRRDr_q&_@R1yz4OX>ge_SSJxXW#!guB)uc+dx(jcq-q*e78Rwi( zzuqGInDVu|F`2q$nN#gUuV1KeNww=Ho7}!FKz`euw@Z zOCVwynYNMLf4K-)1(p{to4mRxt(k1>Ii&Mwx23%Yy5)TRl24=LUc?nUK6fPG)p{$C zQN0D$mX5m3(NVZKQ7S+tGX8=m(>{Mb&STDFBCluORXq*-cB<7tK7=T$nOlk49?(;R zHSzm16>*x>3M5@s zRim1$#8zI!i)ncuZ^ zap_28512x1eyX(KiHI8bL32o@TrV+=jY z^HE(uLF}EqnmI;+rDbKOX$EgBEaadKqJn~sm<^VexDxR3si`UH2E?#0 zHoNFr$sV!e+h_LouxsBA)_*1Garf@UYk3?bsj17$QqO+-^&)Ky&*5XfEv*T9eUZ}> z6c;Kl*Vd%?Zmc3@A&|Os?}>g}f06Ct>U<(t17QXlkNXaMs7y$*vxoexJ#b1?2x?U- z`%?L!5rLQM`N3kT3Iz(M#s>5Y%PdLH8>k=Z*Bc_j7Ct^5uY2N|H|O1W1ir>>B!-8a>DDs zfweA4O)mPyP1mP&xzFgxlHCV-WqhQF0oYJ>A*f zx(R3cy*y(#LVcZV_>~dr^-bqCp;d>@m03Nr<;e=P(`=_%-^z(7VUNs)vbc>4p}!PB zVX~pXp2&FfE?KkbPBMr2vVAJqezhHN#}m+MPIACejSbOMzn{T}$R+#P**fi|XMVrB z!W?c(JVy>+QU_G{<7=`yM#5syky?xID=2AeJ3i^lTW$V%`1Qv6L=2`$MoNGQ;y+VU z($nYqRdb9hwC)N*3~t;sBk#U-=@|8fv7vzhKbKR^a|%xK6IY};%74W)G$a5VIenJz zO&m>jwr5)yXCFw17SJ(E43+D-eYL2#7#%yT71qcMQB_Gtcvx5{_ucpOkh7qqp;43T zzU~fP+0l(zqo0JzKEE@Uy!{H>o>DLk@sDNGcGBw6s`t_@ZAtc0jRgfe3^@5DbnAq6z5E2~K{!#{J9;fh=Cs7?m_F0ffKNYm|ZHD%Ik1bAnQA+1l zB;q&vi$*Y>=0iEx3*he4o>Sx~ z%gax;@Xho(Ua#?sQnax_7L^ppo0vTITsto_(G*p%-Vmxsqq;h$kMGIJ`5hf(msAt+ z07i?EFgK9b;qEqF0PvJi&NBfKIBrV7`-Zd_xJ^7pJ3`n8!~X7F4ns96umS~D(T<KY;MVhV&mh;c+2;`tYZjf7wtAqkcZf z(*^c-c_$~oe5-+^r3qwE1u{W#2`bejs-z1;F!i*qULe77`i$1Q2be1njpUT9G`vR> zZVKJMU#PV+^XXGO)WSk!Yhp*k75!;GhLH4slIzQVKt3qp(yb+O`Lixmlt|Oey*xFK z+*P0L6+#1Wj+pp)qR4&y8SINDaKIcmaKNlLw;*a@Ph-gMm+h0`3Qw9G;GE~Jk|qsi#BuZ#`^4RZJD__ z7#Y>9tgO&VH4QFS_-%orV`?C9#v zTcG5%dJ>R%GW&d;^WeS&^Ls-ri!9DKVLNJPr$wxpNwK7gjHMVPvS~ z)fJ`Ss3`82l|Lb#{M4lN1d;7Ov|NCEkGyAR) zQ^1M1dw?fLmJWzj}POt z$~J`Hj{(spLK*1mtAkYXQ~&%lLrAS&znyj%I0JI>sP+&xR^n2mSMhvuf+W@z{bIFM zrBh&5p6~&jeI&tEjeAE-JTudRu@>C2nhP^iA#JjS^UbV}N1lQ`5g#D& zgo)Yo(LplL?Dx<&mkMiLz7CGhVQUyLWizv46p9D-X11hp$u_uAN$LJrDErXA80F(r z(lCA+(H20*NSI7ekdzWoPjdSfsSV2-d>yOWR)LUD0x=P6+ShbiTSpw%J|Be9RXlX) zsif>66P%BRoj-q`=CZ_Fug@1;#Qw1C{=9mu*cQD>ND8-Kg2|hN1^g`EneE@-)%4n^ z@ghdI>~(_+70;FwMoZ)p#OQe3G}*>V-|6~S`mcyj3+N4L8aY;)B~Ce__Y8575I4(2 zJgL7d&BDT>h|yeywNq?!Uga32LskRZrAt9R67?sW*}n(pStre1zX}~RNOzN`;em`5 zX~2PNjwhw)p}r~r8PO89)QF9>5oF!>*u13x{(UEp>kW=_w zm(3&WAp|%0_2T+kuEOq4Gqcb(iXJi`$hgfq3=nNQ+Y-Q)Q(v=DMseAV>Yb+DjT@Lq zLahqIRcp+Z1VK|_mP?mzMn^yR3j1%Na*dnMdp{BX(6{NCXWA|i!84mBXg@O0=xen# zI7qFi*!JY4P;n7AM~Zq~b!M&(6Yc{=w4xicI6G%pW>%c|C;L1UI0V4nxMLM26(3bvSwN?li*_o+O8 zp+G_+->_$&@X{h;Q;za$VxFGoi#O*Wld7Vk%a7U(_|XeQsKh<36V^C>vNr!LIWDV^qt6=G)3v_xcR~Yxef?jI-#Bo^t{2m*600>6$c=W@E)}V)^I$6$NT@4E+ z1U5zXWL7;lHg9;voKy`bmumdD6vr_Y#kPaM=1HPXo;=BI@IeW7-rN#dP0c67sA;OL zh~LAtq$y_ib;IDW`&XKT+mbRf-*P{p`<{^G3r0p{(s&~>Jy~X(IQMPWFbFu-rB`$xk^1AHK8}T>m1ge^*lXayj2#}%4;R|hL4Z7> zlWW;$1^MxoB{)D5Pz&+idXe*drc(@us(Ul1nX+=xoFee?%D3KME?c{sdzI6_&O%nf zV3fqrNVoIv^qfJ9*G@h`fLx%0mCT_-OGWc(`p@Rwk5(#Q(3VFLNfO|7q6A3Op?ZMq z)W1_}{ewjQRF@~2E-bQhk;l@|z54x8yERxIX4e*v^P39|Y|Rz+=9O7SKgGc6VDf0n zA7V3}7v!}bOzY@y@YvnfCEhWj$FhJzndYkaKIloe7pN3+_Yce~!uc)}#V-p4VkmuB zqY*r6%)*`_x^9+@FkKwI0&Ryz_k&H&usah~uyEJwL_mr3bWh{s-Eq!xw$2_4 zwTIv-U&FaGcd>r>6ARJ-agZL)G4(hmSVv2gY$iFIFzf{hn#eL3=H0s`bK#t?Z{7$FG zWrE6X6Fqwrk_oldpK87w6?LEXpr6>c6Nqwt#oF@9%jG&s@tu`@rf>Z zVx$hO2!~D%IZw8T4|zt!(|XO62`)1>0&Qt6`*ap+p2t3H9Hm^@@*2cm@j7) zfhE!#l;m8;Y4#|Q-Gr@es^IVf*qAYzgfOQ$X=UBPvly*{!dovju(~?A`jt-G6CxCZ z7Eygk8JVsp$95IrkI%X4vbaF0Potco4(cY3At@o($6D`lpNUxZ9Slh?&l5;o9Q2Oc zDEU7DIO41n9wRcW+pFclQMIP5#4S8!bZxWOV&ZW~)R^hCkR5L)>$J25!Ft+FqFbQ( z%^SBo$}8?-R*mM=Buf&>}FRUqQ$D2NJyB*|7LO=LEHiEx6G)?UF=@9AgDS{ zL?uLZ!U+P_%~e{WfdKpgjxrG|`41oU)z#GWT{W(L;p{-bJ6aADOKq-DE8M@IHE>13 zY%tYIhc-&cO&2vfG6I=C{lwi5P=rp7(e}{csJFh{49xV2QHPHl;nsh5$@lD1q$qc> z!&EBNUL!sa#jb?nWZAJ$PBRp+e#DqTGp`u?eiaCw_I5du!UR+Hmwi zZ+&%!$;A}R3%S~b)w~|G!k}}@z+|;^O^?7Sx9y>=ouY`IdQrA%!Tmo`n$L8{X~G?Q zZcL%CUcLIqgsX1sW_?@jUw_#fOE$iBR>&#V#Fp_WA~*X^V>C{sl(_|Bng~ngso=Pc z(Dht*Twix2in&Di&M>~V$*cJM3>ou9qlZx1A?3-j={{u9L0j?`EGxEp&gdd5r0c@YF9Y?H)3>-v9 zntAvnppDI7i44@JTN*`*n&aL_+rPty35jd6;=@DQq0f>{gJ3U7ZH%3|g-u6W>Wl3+hePta28vihb*hu~Qq&983 zq=?%Yzw!4AgZQVzO`m3F7)VlI2+F(lydrNL%?)1AXbAYr3W0w2UX7fuyDIWln+HBX zOjow_;`!!vz_QP!IK6#D(ribf)vjM3x!QcPg63=ju=1Cl^jPo+l>E@CMP4PF!BAPl z5ip*GEc2?mV_;|~_5|HU@M!xzdwO~7Affz*~!>XR5Qa$ zv+RC3Jmcgg5ESZJ)Rkt6t{JLdnVS?6)Wk22jVyk)!DbV_Dl8eIOLq2h^^J{~I@cS7 zz`v1NdS8#w5^)ru1u!-EfRUpXx?HqzyiF!bBM@Qu(rjk{CXJN}&6ex{KZKe7r&J^qJ1`1{!vqI#8YBfUx*&&~pR- z&e8hGOENUVO`L{i4rnCj%fJRfY(|W0B1;vVhX&p(=3wMUj2h=!Hwe4gTc!6GB=Ei5 ziSHvvYD5Y;u0$wxpjGUX61C-lbt@xu8t9b1$|x8N&tQ<+`6y4us^%FF5f%(*Gtn&+ z=V@mrJLSu}laZP#5_}l>4YU7n55yn+CQfS9pIsziQhryLK)}mI?kX_Q(}S^NeXNQ9 zC-ifzVBgx!UU;|sslDouygl3qIc2&m@Rckq{3&c&s`$(jGX(0-QV|xw$=5HhzUz)9 zK2XE^2jo8-Buvup31&N@#me2imx+t(ri8zzrF|ygNRT0k;B{!14}fL%3qU?|ScM+z0dZAxA1Z|_U9u>X0QwcH=PTd1THMF}*7nRhA^3B%z!tvROg&H52<=c5? z1B{iIr@(|Wrc2@M2R+lw0bbH0;EZh@aOQnk+mYKa0h~++u!1yh89u<&!&=s5RlQ_m z()jo>J=B=80qwCpCB`aEUIge?%4=CMAPhUs^}G0VegiSzhU)uwuV?6t(gZ-r40hf? z^=MVk7#;x6m(fl@UPB_E{a`zAf>O)#6BO|?Gw0HUsGcC;;jSI32!yRvHNM^ZON>an0$I^fQb5hG&#}l(4eRdJW#cR|?Gl(qoW2J^23rpIvso z?f*-coh>9`BqhKM2BGY3*xIr}WIU{B545P}PfE}CR|q(orNqXn;s-YU!#O8}puVjz z#KXZwgC8pygCZhQnxY<&Qwz!0hcJYc&?10CX~W~Ff&&A!4EO!$Vf;Q+B=Q+o7yZC7 z_T5jX9rwjgcR-_Fq?qWZGgACpKb>5Jn>2*DVVnY|X+@+&eRed?o(*&Al9j3_vIB(i z2xP0j$eh~!`=nyx<;wBNc@yV)SgqL1`9!Ps;Lt+qj*iHd@Lr;`uC93xiSWn8g6JT? zXFnb(?sx72P|$>_rC8vQS-H6f@cqn6XHr41#ig~YVk>CUu@yV{@7F-48UsQH$d#!F zq;qNjon2gAgV4~{_6J-T@+%p5L4Zp)8L6H{B|@=>>7ofwEX1CZ-LI7nAD`oJUOhw9 zn8`#Eq5I2{>gE7Ey3nUS5n==IllF%5=NVkK)!1ZK9dxvvILk9b7S3xOKvaEpw%vj$ zMLf6crS6#Nf7Cz_6)fRRi!>O9G<{J$XtQxkr$pHD&`dr9p!3$&icor?h4+>GK(K;- zhSA>s`2Zof#7(MfrB|CxZztK^V_L;w`p<*%m)FBD>plou+ph(2R`EP zF-t7b*9-p-0(?elVfUMW(F-=u72U@9=S9Zd1&x7t%90*GrvxHGApaM`g);YEljNT+ z@y#s#{~B1SA2{F*5$4)`DHIUGM=P_6lE2|TSl=w%jQp;#gGi*WY~(dejAt>Ll=f(F z&n3d-39CbCY&*G@RO)jcp?NV>o7e_NTQ?9IV#!&BJ85@+J+G$LWhc}JN)o8e$Xut{ zyTqsjI$B^Gi(FjRH$GQ7y3XeDL9|IEsOjjG<|`_awYSfS>=c1z5g%dgWYQALScl{I z(~bT0rSdIMAc0|sAW+vH&JIBuaB!L~Pa2a0@dpJhlVGWRFfhY^_OzbEb@m#*-hnaY zu7Y((^y7jFh?R*+zCF2Yb|&P@$Ev*lA0CT;fHeJbKSG+)#&D4LCc)&fF_|D291)=w zS03%Tab>o@2vQDez~d21pJv|W$8Y=T1*9w{&CEnzH77!t5+~{wpsK*ez+=>;JbxCu;b7lJ%G}~&&D3=8s zr#lfui4v-%wnAInsDU%YtBO4ww4?S%fVJmB<@Nt}fPyrFn~EzH3l2g;r5z~pLZI$AGHqowev(U|d7^(;WdprxcNCJpN)#Rwi7ITqMKmQ(hP87!=zbob-wrJ=)?@3N zv4OC+=0J!4V8fMR1JXaD&qqq^k1fazvxi zAClbflYy4ZPdsYOYug9@wBOxY13eg$tn|O?cZuVJyM;yEYOoDQi~xH9CcgxzFoxtn zn6do!~a#_>*%it-8bC1hS{TT{ILS8_7_~7pZs?})`vOmFAymWB}{Vd z$iHiQg=JqZPd_fFuaK?ZJ%oma4W7`6_kL!{V4R0}#ivO48`C2IRii47p zWhxy%4_}cggClVADPuH8&c$z6$P`m#KSDHKe&sOV^vu3FRM_=b%>V7ZU%y+G{Rkg4 zgEIml9?{jsEN`!-J$vR_3{-qZBZO8W%5V}@=2L&uRqgvtspu2%t5&uAUv)dyg{ZR9gijWb%_N`m zkLp!{s_O=W*sY_1kc0yY%;aP%*0Kp2rNzE`cVTquEK}uW)3G`^dIV@1a6kZDNJZ{W z1c4xgTK+L{#&K`H%_YJfs~Fq8e^SZ7cN@-gtP6T1q=2*)(jD`=t~FPy`9<#}`n!Ok z_%8xH43@xw^k#Sd)#|1?-OC^NiSo#qODX_o&D!WQZuu@oCizH6jg61imnT- zkJ3SNieOz2$9!NYh(yI$sLi(_Og&y_zk6@m>hAxfWjS9XDscWHJH}5-{_Eztd|wH`bILmoEzEA%uc}j?XcuJ!f3-FF?`i)F}17 z)HF~el(To)dEMr{a#l01n$O2Xa88ujmCa)=%Ec*X79eTh3fl6vmUH3`!p8{4kMO5c{lXT zY_qW-xq?Sh9^532(c78yra$J*!~z)t0w7f8;)l(A!n`|64ZITxAddWCKw~pc`Fr`R z6%MX1LYJhBP%I#@y4u=cdLlUf@G@m|Y)r#U4nzse$|>-zK>t&XW#WmUCq($=mF-1>JN%LjCzGmLf5qV zMJP3&YzImIKS#D-7Njwn$ZgHrWoOu`Gyy9k_{onVER|inX;Uux~v8CiJ4SbAh`h?4BwFy#C^XX$B-LPrrFkd8Vtk z)TLx)Zd>&Hd7V+brdnK_jw4_OF-=PZCXai~u+DcvOxlH8eFHAr&)eBz)3G9bH$XtPo25#ST-B9g8(UB5H=pKp+4NU>{DK^KrnOQ&} z<@V|#G##L=pMG2Hu~{Bi8}#2m<{Nx`5{r4WgWD02QBiqNoZQgRP~eX$T_HVgJ9HW% z5vfWw;O;#dB^Z`kSa=^iQ_{A!3;ebh6vDzZ4*ya(G`=S2Vtfr6x+_B-2{|>j#_*-o zREsFz(_`9)3`r-xC%k;y#s2dBO@L%PO)AE?sK<3&lSZnUC1+);C|+>Bv2!bbCD6D# z>j4Z&3sCz1g^WgcSnc6Q`A~KRIHq3x50m=l4GEZjZ|W4GGNJJ6^x8r@S~E}bf#=0Q z#KA{#ZwPE!PG(bW)}ZKLH8i4WL#yO8>OBhxvVKuZpJz>LwPPnbm~K0AJxuyy`dQ1o zv4xctt_oVgH5bsPTFFE#!1M2D$kmy36-yA*(CFEiEf8^R782f=#^#9Csdftm(DU-D zEx9o=GQ#$LW=i)=$Vc&zPSJ%(5hVDFDs$}&Gaf$tiQ9FVOM=)K>&w@FY2BKUhXo>j zd-kj)oLgiHThWBpDY?vWF}f9g1eo>EO*u`S2O|{YsRV6 z;vN=2CJQ^_*$a!z?M*V%x&EYLI|d=Wir;=4@69z$hrBj)Zf-qfg(SE`^2%&0G%5#_ zW9UmGBy;|}FFYouwsP<*6=Y@oprv%In(lfDuB@+a$shSbZ3)u1D#@O6-FagJ3)IAl zN??U9DM_bugAkGDI8Ed2<1zhT^x2-9rx-mUS@L#CwI0WX_#&vzk_nFk z@pq*vRB<>MPFp;-Kc($&|2-k_FYEl<4>kfpw}j-inqk*$;MU3zLds_Ex>)C*UJbMC zVx1ebS&b#eavzjFJO7Z`5-XjqSf!S(+6d+Ot$Xt=gQ2vZ$!k5xKp?IpAkY0+lua%@ zdYdZtyj<1iI0o$8KvU**J|bHI1;wV^R@A2)@JZ&?UtieS+vD)KrQADj53jF#f4FnG zu70}LRV7`uwIOT-SF&o3R>PLDsGwsHlb13ec6K~9OV{*VM|{DzZ=FVbYFgt>c~-ga zT$ckA2U^reRdrK2Q@i$VbTDfLD6Jx33kWdlNR0+=C?0w?iShCAjb}Jc=POA!TyBiu z)$sHTUTpj}DNs^!e7dSggh^Xop7HEa@g?)hCwj|cq0;~vX<s`D4ZZF?=W8sK-zQ(ONfbebMLsqg#$U~L6}}m#^cPf;oRxu zZpi#5@oT(A0=Aj^gU(TedTH@jL}-=$ef%x?vo0N{T#VUN(j?|1KNm|55ig1b7 zyjh#|1w2n`Fceqq?#7yUF_0SJeO3) z>fTBs_(HL>ZysD*Usq9VPbM=Tt5b)rgSUuIC}j{E_0rX9dLmCHRc-G%CBLm;xP*O4 zIfJdUXwK13Cu3i{ctrFKcdls#FR6Ce7E{TGPFhI}@R?h!XjrWj1~8OekQx0&0Um^78E3 zAUszoacaEKtk2acZ#Ac|{kif&Fo&Kj%3;wU{^-Gb57eX^vMw~otQrb^zs3Gilwi$1 zueO!d@IgJ(R5jjR|t+6-g=n;MS z+bpmv%=RIJ=^@*%|T1_k+m`WgMD~+fdRXZu2+) zp*Ywq?3~Gh-rnhm7t@nM0acdP(vrEjY!M5s=|QzUHh#s*iU8Zb#+}S*EERlU6u%k{ z?TvONT!z%fvZA5IG4n-32irqTT#R-3HMB^1nedJ-Eo&L&`9ZAdc*DZ@&D!rb_~Q#3 zUe>3s5^Z_K*8?v$5-$w)r)hV`2Nroc^Ber4op=iJkhhAK@e(vs>HYoMP|#1};3Ea7 z6`NmJj_JMKX?G@XJOINl+u7M%SEuSvgRQGc2{?K%;YIcBrC-fg59KR_{$ePD5Ug*c zDcFFygw#RI@aW`^m%)G$_eEa~J9)Si+9?gSwtoNrb7qkvB!yZ>DY$P6rbn4L;~W+J;K;A2Gmw;r#*JWu z(%ny5o}iIaJ>6earkQo;86;UrsbwZoOwmA5hZ_L)5_@tO@xf~1;Ili`Jn2m_*!id^ z4sPJTB#2u#l(*KEX6k;+W?)JUEV7yQ_0C9`GZ=jA!@l!axG(fsp5*1k=#clQTig@BR}MD7hMM(Ni!l zVE?f*GLmk+*J*x7rm3l^x7-7pot;g+xBA-m8v$ibm9n4cVd!;kh@CrrJNejTmu)(I^{jcs4c2%XCosH>?-n=79cR!tlAEANKg&9f?#Nc3bRqvx(1Jv6)SvERmL&E*q9X#_; zD&CqIqoRyz%xhd{AO$j9q^wJQ4 zK0(pZ`UK%Pn+^tINa0oD^FNFSbdMDu@+d&84))!w^pWd~7i)(_pVcqt>iR@mTbpR6 zdjH-)Ci+|iR*0Wp5jrJm1q3uQK>jWp%IFjVWj_x zqGG#p?e1A`@#D4*r~CIE-eLX`TztDI;wR*@sJQ2u;VhDpwv#RURKbz{RMV|Z3pKxcxri7u)1!z42{Nd z8Wz|SgIVtpKKj@z$KAXK2Opc}avt$dap@DLZy%C_(;)z&tVN^+a|L6GhPq+5*wRcDc)Zj0aeTs zt&tOs+kCtm$9@8Sg+-b|Nhr&@$h`#uVkW!~rT=c!kpKQ2K`b=IPMP*8 z%v2(wQVGu~I856T!=UU5FGhoNsMVI_bl<;+ng#iT0hkRNXVKYfW;FW>PB~80JuLZH zag4;OGfKmUo%LP;yWX{#$zPe`3u#v;rzWb?U2f}aXRVa=6+0Ph@;VIrpW3)*oq<*^ zO6gv@W=FR2%TYXbbo7X%V7_{0=Ga^}_wcXL&+>m^#3{vB+uSeFp zoE&1WW;kHxe%7a897992_QC6;GClwv#b1V5Eh67+ud(W6m=-K2U7CqDjz zB>j$cCF`|G4=Z3wh+j4}OvjAbAwYB3*%B@)D&oJiU(&B^fjSf#u77_agFbN(U;)1y?EpCNj>;j2L6 z7laB=Ee*hhFxj#%WQo>oT`gXsYPRgtw?g(VC@Y z%$zG*@bA%F9XOs8Efq*Fv?(n>y*Bdr_?GQWlyXJw5wos~xAiz^;~KR*+Y1lrO%@FK z15+nMCe*EcZ>LA9hp*_2Jpq5h(w3HrcecIosotdjBSzSa<49?LY5m~r#>Bg7_Q2&Q zGc{b|f(I_nL+SZuMR{4Oy&e|7EkutH@Z14?tSo*wdEc{5tA}7vRY|JOSzJnweY9z$ z)nKzN1btdh$;-2Tpx|JQCcP_Cd)yUk-Pb}VL(aDtx*YSxo=HbchkE2}sj=Ub|`BJlE+%>(>&=r`H{<-x`mFd<9ZAoed1c4$p%b zg|^8c3>E1pS)D#muKw-Bysi-xV0#-*S3kc8C-2S|`6by?>J2>${~ZivWLQ}Ct)!1P z>m?sJl6-nS?BL}Y%Z-A~D z`F)r~^NSj27v*03)qNy3jPcb>UTt^hGR{j16TDpszp0wFT}`^k)?T~I-q9|X+nT{Q zy}aIbY<+`6XDgp4GbXY9DYL8ds4*%LXP$4iKp`^KGdJtF_C}ldmnuXk6KN}AQa2u~ zB2YWsRO#4I|CWG+r>^5ghafq=&AP-GuSZ*&+I*(Uv|C^)V?2BGb3i+O&_2GcEz<;i zp@b%_0m9Tf0_9?L#!oKJmQk7Z6YObnyJW$x?3flOw~40uSK+zRo}T3Nh^IAYSh-EZ ziu~W!%1?^yms(vG7eI#T-tpbHEg*#w);i6I&|GgkgHXiMD0pW+&916#{Q&^JnROhO zeg+wC9M<#V#As0uDRd{#+I{P|x3?pBN@8KdhfW!qD1*zt0tCx2>-YuggwYa1QFu3t zT=PL-R1!KdeRrZOhbp|`-KAwz;P0%M20(~tqw`+|&vRy7a5^1;J!oay7derR-Jwgk zqNtLd-x0+8+IudCY5FH#$kO#OYSg@hfO0SXb7ckDFRCfM{iGx%RfAc|UDrOY*f~2Z zsjG8BW*RP&d!kt{lXvJ`EYwPzjIj1(Ql$ZMPl%vw3>yj_Dl|J`j{Lq`7A^(9UvCDI z60KiJIyyUWTS1{2|M}z32t}Z5K!8#a7WRc(GvpGN%${$k>g^U(N@-HbGmAs(3Q2?V zZe4CQ8!5qZUv_Lf}?0V%Fhcp7=^eHQ#aDMqW022I)lauP#)6^2gaqePA zmUQjMOXnv|e!r%_`({*-V06+kjHElAiE!N#*PE-t+1_bCV1fIXtBeFyRiu>_`=Ty% z2*x;58&UA8=gws_IIw^@PBO!pMOk~+y6PyHbITs7w=l;RZUWhJ9g(VvQyVB2|IpKe z2n*|RCHQKfhei*m@UC=7?CLTg_}3}PZ4P-`bTg7i`ZCY z974-J@;vN&E?#A52M+vsWOvew7ndy8$Glz;HYR&?yLLqGwvTV0vDaAICMyXF3&u$B z`_wgKA4Yp5M`_oGwj;~?;`$@C5vU5JnB$A%AqzWhxO|y=Nl8iOn|`9>d>MItoCQK> zu@$_X>Ln>FzZ}X;7(efrXEe(L`)zxjib#EZz0Ls5^DMXHLC5luZl+JY-t2vZU*H+@>E$cA8uEqQk<%T?faPKGN4aUc=yMeVA$;E|t)#4MUQs@VG9|ydCr2$vrsLNGHmol93&V#vv=biI& zS8>ZEn?KTZONvko3<=Y#nBCFaowg^hzH|9>`m9mkk@{D>JoBr5Qnpm& zT+PYmwr#sQ+vBDz=k)wi+qP)WUM^;Mh2LQ^Ro68w6zsGeukTSod@u9#sqx|E=l(-! z$ud-_(3KaqFV+^}-*G&ktlpAUGVjL@0qN#CP#G1Mri6e5(L#DCv6M#Kd0{?3}J=sc-M5OHCkm&AS%4OE^pXc`4&4Z5n%z<1{4P~3wz{lZMj2A^*J$dVbh(qFz%%Z4z(CzIVlv4$MU|}?$>H5vJB+Ra)2g^ ziaXo;tmWDs9-#fuRdCm~;F~a6SlK_b-rOuQuV`Yz2`LqBPyqula2G60PY@hb2a5BS zH|LS~DCdjjRnPLoSaempyZ4X|bZMXMqf6~sq%JkCFEy8RcF8 z^1mk@h^sEhaHVs9a7Gt#$IQu^s6>QJ+i0eX;HR||fd9f}8=4zj4TENpSD~Uo-#CrL zi~C}L`SIPaecx3+BGhw~SY%qJSEpfYS7yi!Pl43b&+bCNGE9A{V%03Z8#r*UKG?o< zb7T|$2Ah*RGU2bhK*JHtF>&|x{3B{n5LRXNE)AuMBBNQH21x~;e8_X>!|0aC)Yeig zuV?$#HPm&AMrnTNsnS3Ny?TYF{p7^@v0JZq8Mz7aQDz7mK5QbZ)2g|jcX6Ry_0$%Y zbAkck8SF7SY5+LW*a5gbHtln2Gi91kyN$+;UVCSUoZEQE8I?FF6mRWH-R}~4?nhmx zU-!X-eUQ)W4Pq8VMb@3b*1>@m{L{Ad#lWKnAK_d|%Y=53B559*HcJ%0F6#PHkwQt2 zD9z$!EvQo@9Tw&TeK2R-k?U(4>z0j5%3>N6S;?@3H48tp3ax6Z@d;?{)DL%ZS*WXd zYzij?vl>r-s*-|a#NC`;UQSLasgR7eKI;k&fLg#n!J~{ziSg7(6eN<}z$|SnH6Ozd zZns_-JK)vF#=tETycMHhGWznJ07ZM|fDaBmJH41bs3W6wG`qV7>*|b49heR={JOyu z>R7F8)fimuc~$Z^oXPpL&zuxm_ZVs$8*y8MwY3Q1wi>;3dsiSO`+Zwv{)$ib*w*@U z$UI2~;7zR2*P4*R_iqpaek;?X84RgedS@~wJZwdU|E%2JR%M+&?%|2=b}4k}fBq<$g4sIC%WT8_<7F3@ zBGzjbS=BOY+Rdx3*V~2fU(u_mAiRownH35GZ*;hT`EM*L%j~%WgGE^0`@;$7ndt;0 zHd1CTyWdJn`?+0j$$?~yvtE2SNKMcPSTSyf}PVgVUxW7(r|Lu+o+nV zy+I=Pz3N|~*l-BwoT<=C)C80^z#}XzSg2A%98XAL7Kh>|b*o9i(_-R0AQW6~j4DxX z7T$aX`+KA1)YMr}V*>pB3r?O2UjmZsWe2SY4N@@1#z1-=OZR)VWpb~-eg?ho8XsrUDj?#5Hcn(y+zcIsa z|C*;W{9TjFGrIW<(~)Wn)O^Xfa!t`57-py;=|g~mLuoEc z|CNyVXJA9TAj99v{t`I8CTtZ!c+AAeC{g)DrUkGWlSAg~dm?SGu2`E)cMcHaZ19+) z8+W{M*Yc7J-Nbst04R%{pvFn3A?h1qJXl>OHZLJ`b>}pT*DTZR`@2OJ*6uECGSc{W zobs2@JXc^iLV6%G&vHQBQj0g=JW7yv?gpy2z&w6sHvIHt1S?j4`bh3uTwM#{0}X=#e0&}##92z*ycU{#*{3i%c_&zGV8 z{vW1xwDJNlVL8DeD-rpYT1iHYl$tYYMRp7r?X&4*B9RG)$tmk0G6wv~wxR_@|Cvkb z=b=OhkW(%$IuSXb34^N_idJh*za@2J5fr>O)Ey_PC$Fxq25*7wLNlsnXh;sCb*$(1 zpmLe(tV)g%cZA!ex?-Y&Gm*dVz|RL9fh*>!;IUW(n{4Ch|Hv!J2`QcrX zIyDHTw>i_78X#T*Qoi09BNGtS9lQ$3T){r|bf-*=R=On;G@G3P-Ye3n8uEctwMa4Qzp!!%^v=NWmi%MI_w^OKXAs zFvzKZz|h-}y-C-0A^}_pZ$jS0?oh5Y32x6$<>MDDNF{0>Jo*(5yV}E|<+SJlv-uWB1}#Fm_i-F1Pa;mdAYT84_(dUCr-# zL8DRgxS~n@b(!cExmlX~N~_ABJ9Y**$3A`xx3=hbWNq3!m@wWv+xzp>^cY>P!PG(D zm_p^0Q%Vh%G&Gs~E)H?@X{cRJ6uq%Aqe_~cKh5;X3x8!wi*k_cC51mTF;4u3TOkVJ(0A@S zZ2XBR6bct#mB8<^7~1#hKZZBN#gqF7_Os>ZnNuVmqY6RWjonE~jI8f+8onO+uN>dg zwojgWTt0tHVVzf-!aUIyVj!8-Jq~z$qIn+jJkzUIQ1a>niPM{g2IqpFKd)!2fA}e< zfW7r^sL!Rm35kZtB6XrfIn^!!^#&&}omYrl7BOn+eoZ?Cb}UHdj)Qb$C3w6KH@*bW z2;#BX@EhH5p4pi3mH1IxC~rL5yK35B7zf?M*%@p%>qziT8ukKaM)36~yM*OR9)XjV zRHAxByV1LCk+5c9r)KyP)_s3IJ#+UX4u6&1LN+)H{?dsXBWU+mt;i zy~3(jk@S41GZWJe*p-1WwHv3{oJ8JhHQ#S+mnx-qu%d)4SZ=JO#{r%bz9Yh zO7DhC6R>>3H)s1+qt=VTq_9EQKMHX@r_l?A7-7-i;9!hppC@i>y&fz>r%T4a(Tabp zm~XH8X~2dtIVB@>CxI-XOC9@gnji&|9Im8nDdPt9~~lKporkEIP%@C7oD3&{)m+`A>RXo%{dj2+0;|gEQ@`7vTTm4#{W~%C+9* zB{Iej9|?eJ;d3Mx3qmg?kNb6^D}|hpahz5*D}AUi}ULB-!;3U`HnPu6^v#oT8#_}MGQq? zONwwxunSiEZQq6}3DxdG3}cb*9uZSe7o!(6>YJh^b?rG1D7o>ekm=M7b>xc?ohwTp z=sX~mBOSWez$S+W1DcpFMN+fZJ|XBEcNfvtiE#s?V=#C-nH;ng6|;%4P$gMGD9HZr z-i4cfe3#~g;Y04&m93AH5WQ1F?n|Pt7YhpuC(}~+AunD z#K6!f-AD{b4D-8RclVsL>p9==Kd%3u>+;%l)T#G*pXa_m_a`(4+lpPCN(Xp1=6bS% zQ`Xn7_!5VPN&EpJAEmGL#}+>YAe#L;yUrDZ-MG^pea_O)o|W zEa1nMNiU+$ODfy;3l4T@1O^JwU0%N6>HYkMK{gm@62PS3l12vFSsE97d zixaJq|N8ag9FmWM;x~dct7EQ~%u6S4-i&K~siUX6e-(qw(-2?In3ad_R(roAUu=5OQjf4hrQ zWuEXnOL4vUJxDF=^gfVHZp zHqv*GRC%&6rugdW&9m_oWhunTbaf^-a7=MzTdF+RX;Q`6E4u(xkbPM(J{3s0nX(ZX z`xnFK&cL{Je$Z;#Yb(FWsOP_nsM|U)-_udV$~Ctd_Upkz1pNE2 zYpa+|7fueDJ4n<#81uK{rL<0;}IpcP=pTRiFx)1V#qr@q}#R}7aI%12%SVl zK#ga+6>D&N#KYi77ClgOB;^h5Q%&)wc|mt@y$@2&HVfY}zw7-MB*6dry>E45_Q!vN@#xw-aXv9XzoV#I>G z&Krw63ZMvCpH4W3tJ+DHX#O$VE3N+trh>O$Iw&E_f;06pe`H<;XFA$dHHWbyOhd4BxCh(gE1*(fo_tk9 zW|>R6Am*MMz(DhFkW1QgEOZ`0SYtCCo9oT&$#y`^gIN@aRhFrz->dVZ-xGhO+kXD( zQ~BJdmps5?(?|gaiUK(80ltNLy#_pRC5W3f%yy&hJvAII$ru00&U&(UATfKj17jN}bWAK_--qHIy1$P${`Ez% z9LR9|B{4g3iI!}LZV4x7*MQ7Hnj>TOh$NfzpO(iHowCwjhPwW{3ID&I`N#h$4V3;~ z!6zG1&S^|el-Yro`M9wFnd=RKzw5?IUE#R_W^L%?US;)*dSVL(YLBm;;iw)cA;mDF z0sH`8fsWn1>K$#cBE}0hv_|>Dp97+vJ|)WEPH13|`n{O*I@F1f*c%&}G1lW671vTy zVnbj&c2qQhnL+FK3t)FKh4e zG5|Lw>3;&?`laX?$x5AnXURLuPH{GACzV`dmO1Te{#HBlY&1X~yk;-VApvG;OVU3! zQ;K0xb0(G)P$fi>H#Cp48v9xmIa_4Vexq?A4#Ia2#-N z7aXmsIoBu+g!9bTukT8EKTvmwE9s6eWH6FzCpI2GJFmiT(I+99%#9fytOx?_R@CGs zy|j#+T-Wy%TW}MQ$<6IiPnOgH0|B|=Dp%}WF)OOQeJ@kL$`68nZu`>*d`f%$GqczR zHo2^~^;ggOJs>|u)SqmLkSDtDZ1h|%L93(&^I7f+5H?{3p$3w!E`orAh3wo7NyA99 z-gV7;eN<5CcC1fBaT_A9C%SoUE;l;ijCM zxtiU~Wj*$_U)vC;)~2M0?E=RAyXRg~NUR;At-jkx!(kxWvQ(Zb?f}g9KYRV;D1^`22Z! z=DmEHYiD-B70{Cr#EuET_(}1hiZNN}>xW%9a6iQH4il|=a))n1p{Yn$3zzxVLt@TO zxZdm7^&Y~!axf@Kwpj%Pv^Y6?9$o!DyEN=d-LN$kLjvAOnVTDpd2hCa#%8w)X~E~n zLHvhjA_NF+`r&`2ytVomV3p1}hZ84)fa;>EC^jrv=TpYYl2NUZ9ckDdd*OmPs2;_6 z&DLK)yhei^s-tIKbq&$oVo?3`hR>_@x2IxHL21V#rUSuv@{4%OIAFS2WXQZKgf!c{}6xs9#smEq#Fc2bwY^upWa zgN6<5y&Xy8syli$^i04WEB6%Zomjc+x>T|mmGV^J6mxYB%&>t9!>Usc=9cezbB9y% zYVLF&ys)`tu zJD>N-#|z6q9$mNBVTx;n3hPVSu;My&tPZjZZfC%~48)RKX0EWf;{?_UCEq&fc^z_3 zR1}MQ7sKz_vpQJ_x=dH}>6=n_XhS>Cq`iK9fczgB$)??+L5x7hkyB7mkdYJSO6HaW z=X0ZmH?Lo-k@l8BlzSF}G-H7OY-G)lL%{k+D!qqvE+tq{(LR=k$GFTgbzy|L(Ovc2v(A7QWo>$iW-&6|QjQ&$)9ZUBD(eh!`D`Xwhu zWNFX$y65%v-2gzTe~I|#J~5%V#Z5Alsb6#if8TH4=F8oj^Wby7e7V^CEZKY4zw%Fx z#(!RSji_Q^NwnfuLwvNKUruCuWzx%Yxc+lj`KP`J@C{W79nbkG=;5#b^voPNPVg*{ifj zG0*3>ZuMrGc9Q{G+IwLo>4|TEB3u@~kP^pBVO*n#z7WEOTql8uhfrMXA@q;NZ?+iu zhOzOrJgysJU={1ZYuY7V<#J3B-!NiZ&0&~^3+JP$&5kd*e$9SQk$v3tSgZl+c@O*~zKk^v=G7d3V^K!h>gK!ImY$i7+;069|yePTinL9z3ye@pr5uV=nx8ny4L z!Q)-QOx+lxwlurN27gr3`slNekdW#fOSD3~FnF)t<~43VS+%pI2H5g05G3rAhWYrSVUHA;lk4c0u`a*u4ps= z;|Ivd<1Q*N(fyIat_6UISn-55NN?W(&uG?*!YETd<}!d=IX$<8Sz`a!o$isjh*HzG zZFjvt9UGXvfNDZTKAHzf4I(TQDr%!z8dVmR&AH#6y?zgfqR8`F4wa%me`YKzy`dv= zTnjB$Zj5~Z1Im2#M?^r$XM)tpy5$W^ZASNRh(F1O%@6@2nV6MQc+O_U>a z{g1zl%Q9DPOAxz>F&9fK8I5O@hIJ+bWVySIav{@Wuye3qeGjHq<$jE&pI(%}U3{_b z^H2ylhcWJ8x1^<;G#cd49C1_|@gxxu*?Dxjwkepe!tpHuH>#vgaio}c`umNy${+Mq z)2p-KOhPLroL(GgA94-&c>gYObEGQCY+xOWJ(5?o5gg8yfU?$^#g=!ojzggK8<_~s z$C|oVvD?flf4y6k5^)({^Att3lD>SgL=HsQY9P7HCTjbvU%lPwXGWnmA2FRQAh)?Q zSnM8##zN>)5!4t1`G$VhomX)RLRoo4_ z2qcPd$dahjPV;Ox^&~2r?>wL|UyI2^ndL~b(=PYs2!RKu9%#buGI;KmU6zZ?Y6=;( zw!6oY0wJFJ3w_i*aABc{#P#JD0#DDiteo0?rtdtJ_1|Hv^Nv7ARffsE z($=aUk5Q{!j;?^FgwLbul=l+8l86aX_3o-pUESmAdx8>cQ^z3tN{d6UTzhl5AARIiwT@7MY;!AqPXR1nMi18G|K;lxNSPx^J-P{$JWH%rU}DAjWa_ zc~b_&u>s(z`GajES_u?|gYQg7Qa1%H&T*HtB^)CCB1s~w&wT+jS-k5LWcg=$ABp^|(Cq4XR5c^Xv+^9F% z>}*#1s!n4kj6+8f zNCh>>2YFQ(q~=u5oB&1F06Wk6ow?#<$*QEp#N2XsAxUm-=2ri4aK>bkO`9j+rPxJ9 zZ$ezb`!f}(d;L_WluDLVXFW!X7un&fy$iJr-j{1n4~}<@R2eEB`#FdjCyEE_{;uq{j5iZ+m_Vb_ym?@f<#{Wu2&fI$wp8gmdN_vMN8t=U*h=e>Q z@ML7gHeCtxoG^ZO*CSKv%NMnI5$h5DB4*GKOgtlphQ1H(Zxn$t3Zv1Y8tmrQjCLXP zB9;{ekFQTos@JC#zisWIU+hfI>Oq#MdC{FCn{u`TI-akvT& ztwL|KqvOIEm%0UgMe0p2(W_d6xNxYWoS}}Alo$pYaXNf!a?-)+#-_+Q_VHsflp&H- z9CKr@m6TtQO}pgDre zd|Lm)C9h5s9znss4I~0pmt=kH?0zHra$i5#M@aA=LQ!I#U|^HQrm+rb7@AA39;E}7 zDgzMXM7+Lh)1#f*q4QR=Q=75XEA%g=`U{RxQrW8ptEIJG0;fYHFNQhGMEP_W$DlY^ zdZj`%2Mag1bOd;q`U?)4@k>kHVWA^V`Az?nL! zA^ZV(Jdk;)0=4a7PbU5~&=W7^oHY_uWizIA(sZ6KYKMRI^4v)(2V5M~VsxbonI|G5 zhRbX?#LX_J<&Dlv9#eEbv-6qN{3W_xpa3I2G(l!CbB znL`WnrN>}G!F0$7bIgKyM3jhyWW=%WFnG@^vaTmX_8_bNc)5Cwuz*rS9=- z1-z$&gJYUZK0bd#ew;qF)|($mxg|fMmE6&>JUOd1Fc%3QYwM%e#4k3CWMqXyvI2;y zAg$MY6ItSuG2}|DzB=4woekCz$LqF#BO}`hGB`limze<+ZTO4Mhvr$xScdK~P|;4! z(J3EhT}}pVtn^$jDzJLbrEqB2H%mI?5a=E=lW*@Q>pKsJ!7fn;4O3O9I+>)r4&9p{wSR=>St*qZ2c^3CfBcdTp8)-^?lQcpL%A+IVC>Ke?=ZDx?S|0Kpo^1oqcF)$ zN@V4ZJct%~0JFaZmaozrp}*7Q{<^w<|J}6!iO1>Nz2DQk0HFHtEIBxZ ze{|OyM0B6VHdLmx?`(+5e$wSggH06TW3q;Z88jCM=!%@zHgKq}ULhG@!Mg_;?B6JL zobh|0W!8e~*Hg3`F86FP`L%nvf|<~*n5Bj>du$fIpXJ+Xi@@kfu^JzcekL+_>Z0FC z=rJnW(ulXS{RGRA6=Xh?U;v0S>Q9LA{NU#X=9!$5`X)fxnj@?-Km%>Qvrz?hEnsF} z4{njCkgj9!&Ye4OogP3U6+^iLswSGpG*Q3mRr>s?{{$rDoa2AjgPA9*B51sSmmJV+ z@~di|K9>ogsb(ck239&xm%EA=`k9QgFg?Ko~%9)8U8iuK~yQz*4e z28t9d1o?XF57L9AM6v1qH`O{Hd9`$fVhToH%h9EG1b83j%V`hd1J|y47_m)zjV#4lKgE<(vj1>;UvU zcu>J8eU6_K`f9}b1HNt5Hz${(rmmNue4sX)U+71H7h?3Q<8%?*P8S2IygfuWI_T0{D37*tU3r zhoA(%)n4Xgi?Qj4r<-;pk8Hh$*MfJqPJ=5@B@_SL8h&?uGUnfUW@s0{foxFF?Yq<524dKt z1f(7a7YzqV;xi;-OH~Kbcj#2ATmrH*^G>T}N5=HK_d!1k+gtX=VocVfrjy`z?o_^E zJwDZl=fBG&+uP~Q(2hr|PNROm@V)tlI&g%t3hzFBmuzJUux`|L3Xx>BJko(X_ub1& zDw@u54(A9Iy)~+H=OPX$|M|_XJ})%pnt}2e(ymHDSA>Qk8eC_WG>@rOj__5Ho-EDI zrjY6NkB6^gEQdE8s7@6ZFGg~EslR(sA?RqCb&lVX4a$dLf?Ine_w9)#{h^82wECgK z>G#?ga+Sei`KGn=>N1w~us~G0tY!yp-BxS_smbBvyO}Ru+&L`qw_@jCSD4J%H;3TQ zxQ2NF$fv4>MQpC_5yXNTiscIAQga#{*K~7xzDs1ll)C$b{&Sy*6QY>G8z{1$|8lmh zTtoBzeNDY8nxvzrIs5J3W_X2qf?p8wB1)hCuG)YHv(MdlpU$SfDoqsmXV>c|nXG51 zGiQ|pQ@cK1O-Te@n(G-gZSd#y2@uOVc8(e>@@A9je<_H8Xfl1b{1A`b70+3HtB%BC zfQTWV=tOV|g5ko31f}>Df6cs+abRgH0mlG~4n&UsN54_gC0`rKV|l;zB%~(!{@60? zoU~}kW1jA-P2@5xJ1TuKW9ivEku0Q|p0UA=AZP%zbZ*A7Z`~e4w9Y%qlx4 zJ1Bz;qUUOSDOulvHY#RS0LGt2(>%OsRa;jmElVtiVcNcI25H#96@LYN|NKlKypN}} zwl~%*ql;a1^s9%}^E$uKM%A{IRIq6*#$7V3b(r9Wze6@Su_5>dJ15|6i0mHXWAq8T zSA<9!yGW?&)7U!9>#d{nh~(o#eyIYt)dRm@9>u|R%53Bj$Vvdq>6-T zguCvgeM?lkb&$^r5~Tm1;~?}wbl;mIJcLBx-Ngl}uf&$qU5aL{N9rYENe zz`>UOvBggPOa?i+eiR4%gA2NjiEdcs8Aj3}s#$O>4^j&Zw z{Mp;uTto1T^NN}GFeS}SH4j&(M}7GO1S>7nlb6WCRy-tDR^Ct>w4mT}T&LS9k>YZ= zCtQ5hen@5G5}V*<0>;JfP}bW`c5;#J!P@~a{{MCr0T6&il;O4|2_g1r8o9Aq7bUJm z>~Kd|<%ch$b<3VZnpFD1qnGD47SBjXO3DVygspa1)#w$Ri77+1IY+wr3$#V^;{54$ z)?2uCknaiNyj*9vm7g3tyNZZyVv$V=d4fW0buU=D)6yZ9?tx6btBk6~xt&pknnf#O zr|j0hH25FKXD@8)4|i>nCb+L4?~ja<^5EmRM?N_TlUj%S`9$H7rwa`tqW%W13q$U= z6h%cOtJPgvraR*^&t1K{Cbrf4VCB*2-q-N=;E-k!D2}b94kCoxXP3YuO7|#3c|%!? zJ#vlx9?_XIQvOohD^#E%o8i7Q1*fqguhocBFs{=15(F*087Q%`?A|c#-??(c7840U z5*YyKz=Wa)nZisNK0G>ie$wg>|DaE`{r_oiIC@MZDNYEF48R!Ch7I8Z75r)-nQX`5 zA>TYpgD87R$^#_(xlpCVYvjs#s+I>R3aICeXai0QhV5yUmCb#)aE<Q(scT?10pt;l`j5E73;Bl6U8Uj<8GuYnt3bX9 z7}Lu7F^CDPCcuei4oJ}4i2G}BqrF{SJp%nfYMR_xl=Z^DHK?|X4)Fb*=c=%I@RHGV z9xJtO6ToVAMoCZ1Lrg%^@Y|K~q?y;eWrD`~c1sf~&fzOh?9K?hs<{qsBq=tN!k7oO z53`6@CUsgTU8#R>#{;2!h20OC90Bql$mmBycm?xIWRcZqxHCTaEEW+BGv$```O@JH#!1jau2l2}$DU?H$B2TL0o^2f$b;@F+w3dqcM#5>zK zSa*dp9G{)Tut<3usW|NAkgts*+=<5H&KwQl`1d0ZtALY9mJ){VRVpKv#qpkm)kXiu z>Vghqo4A=}1_cHqU~NX)MKec1(A%1}*j=!xbu!$H8ySf~Y$-DAvxnLOS{1;>LW=3> z3*r0`Q~Le@FSTfBd526Ne2=I4_~~dV1SQnr&(3{6c8qw3XLK zMVO@zqsWxLQQ;Go&uz(9jx8l9*^JRi9o|pYcwKiwKeH^^>-}KW;9%Hq4USqb-=H6c zKdv+gViXXdQZBOzm?<-U&SUDvFkEeG&|_W|Z|O?P6piLiO&BOfr)SKZi5bY+((KsN zv%OB_ly=>S>k2i^vR}9pxinOkKocRtt^REV_ZCgWm8lx?TC9e1z9I(YR{0og$>rI* z=D+N9FI1h!6M^=M>r8D4${clizyBP*BNHpBGsGo5P`2|zGr>>?LUzLRjy%)5v}2EeB1T)dL|E#J$H2-0n9P+2cfQwU}h7 zH5-@@@GLQigvIWjSG_`tCOWF4$51wV!A!n`?s5!g{4N`gRFLe_#;NJ@>nA*L<~;Wz z^6r?;hoduf(Yoa|wznPz>y0NrdBR1N_|9_A|9Gv!L4MM0(ZehYtMi+-2RAEYb>Q=6 zoxM)Y#q9IGcxNB3IqHjE){Gp6%$bkdMYagwc?jVn(@(AFZqiB@{R z?IojozDq@#oKBAUS5J|9Sx(k?+~L5eosp6L{-y~>VrQqLoJXFemHzpRx+gI;B*mNb z<9RE}PNAG6&}Y3rg)iJEK2l3~)X1>fEZ8)JF`B9`oG#og3z3!2)f=I&inRZ2{`_vu zuCqwQ9gibq_w_y642bovIu~k1L_M1qUEN+)5h|mLXp_L+0z;@#ReN_Q-)_8SWlFeW ztf_PlV=N!#V9=U_zwEzk*j?T}R3)2z;sFJMoitpmd9`~fZ=x@y(w<{%gw*z+s?Oyo zHKqMpuq&Dms1rv^MNg2eUSp0Rw_)FlOs;)D*e2F1DEQ07yUK!B&5QGtqqKbz30H<1 zY2NUjlxwV+zcfcTNjZ0ZUzuOIt=8nsG`TWAYwVT6%vWiCoW2TPnaSjO%#ud%_4irqy!LglJ5Tmh_;Xcg@+Go@%c=JtxZQe}reKil%wVOI z)4-m!*8L3au94?r(37R!5fWF=;`BiZioEl~XL<+Z%bsxGf*!8(Qb4JB_x6!Yz4^RP z^u_fPdeueRVglNvGn-|Cj%L}nU8l1ohxY-mFz4H-CIkja1YfIifZ`3g0Tc3uGBr5n zOo$~FOwv`Z#f9YtP=55Vi=?q7R@W%J;Kaqdr>~nW&-w%?s(Ei5E1Jtf(Fo6I`JLIW zYz|c*J+YFW_0i${j*lU?SdT2JPh~Jwh87C zhtmm+dn?{o85uDM(*povNN6C*p-PzP0ZjxRfHXKP$#B)|$~n-Ao*$k4kC4$*(rIih z`@ZuGw$?dpD^v`t=mgKmG@~4;x%H?PLE`+yF!s&YoVsrV8PEEh$+om&GqBzmQa|SE znw0t7dA>EjPMb7LUnOLuRleBjZZ%q|g(@5+81L$fu$NKWmW5JIa5oyBuNiaUBWv^p z7=0@&xmSAQtufYkD`O@SR~(A*4N*N&CE}9L*n2bEE3hWWU_V);)E@a3)#mq!T@g{J z{MZbti`wIAGUqTmSvTE1F(^I71NzG;QZi-cU!Jz}HH26vU`Y;bX}qau_x`<>yvyVA z6}e$o^5#|EOWyff3z!-?`Koy%wv@W_B_Z6;Fn{FMUM=AbSzrVe751R`p4ypQRXG`% zUOcJ4j;5*kUUtyaPkw@T$;g8%*LV5MUfcqsr~4G`G5yTr`K_|c0gdGV4}$-w;Ow&J za?o~krwQWc3MPyUX!R^{-|02DATTXe31bU`YWp9AnND@Y#UVvHWXiP8$BGhQ3>w}{ z@IT_mW#Ny`jwGw7W%u0y5J8o=jbUV!wO#7VH-P+MWNyfaR!%mC;Wszp08D}!{g_IF zfc;!0l3)#<{Agq$Og5JRSIy_GWHO{G?|$@;hXn@xY_$%s3=@fB)0*xvXVG(T{qsfy!nUCq}K7XG3@Uf7u zB&Kk(;WjjNc#t^DX*Q0AN4l{)W!~O7l&RC8$jPZd9`HV|&r#gV45{5>sz>u?Vz{+y z9t6mQvZS4k2TE#<$M==@Q2Z3RF0*clP-knpDn>9a_Brbws!ga96WiN#%-r}sPi`Tq zd#-vg#B#}&If6~_q2Iepy%`Oo12f(X6zrlGl=TD;0{|bY@1Ba$^(~*N(K*g0z^WCj z_OU^g%MNv!IsGE_G`5)=!(^n|wIr45qi?kH*xrpyd?}wdF9iIk!iucNvFI8u{oPb3 zyhC#X(85apPpE*4atFUWT?k~b;h66w>cEv+8WBM!cxb0WX)7f7V}EZfRH+{4YtXHJ zxZ`gC<$-vd5N~*dCM#hnXz#05cy$T?rd|yzMTj}Kx9}ln1fTp69IMUTFQl&_+LEqU zky0o4+8(fIvOvuz0g6jQci1?I!B+Fk5DLKS_QWGq{1!T_%MM84xOADVvej?jo0Ctkj^ugJxQ&2^-5?cU|68- zxodqF;5}`t%9UzHuxn`>9TLP1eZW4PXQyvO8PL zS1teHDL*~W`^|W zXds2b^c5y)Kk@G9n8#z&O8?;fbpc+i>EY1^VVL*8FS~hr-|eK$-8DL}g5U*NKsPQO z`w#FzlZcN`rK6(`MCNAFj^kR*SRe@<#l`;kXbsvcby)3uvn;_Pt3>mfysm6hB9k;l zFhj~lzD7rvc7>MF(uoqLw1;(jY?Vr%l|C=r3;&h2Lu=*8n`k~mKmT2qn_N{Lx^=!J z-rO>xOj()x=Z@1VzEYA>*`04XUe0w1+&!>uud=o3=@DVSS7J`Hy>TmMEZ>f{yZ*81 zUN&vF_nUrd0TIzJcbx@DhNiTxYXTdszMl+%AO{$^dTL!*!p+=7%4L z($y3Y^p-_lA`m8;t>Jx88r+g^pg_nr>4k6H=}5_YRNrr~OLY?@Q{Vqt2H zR~erU-n_po*U%3^-X^j?IdNQ^&sktV@FSKhorPw7 zoO{G9*CB(kEvitBitU$XfIa8&q`+;L=Cwf(+^0hnP+B!T%(3F*3HHk)?!LUY4>7B* z#EKGQ*woE35#ok4K&`pujts$qke6{3SybwoMRzwn!oo3|lD5mv8?-G&SbmIyTWj0l zhI!Bww8tIo28dL};OG&ptZH`yh0ThxwX*f;vT}3$ab$cm>QRvbri5mT1M)C!dP)9k zHZzdmkOD}Qw0kKgLEUUQHW;$o>3LB!_w5&2B5Li|XITA!;uRpQqGDM8YcGtJkSPFc zbP!OBc3H@0bD0PV2BwCVUe&gD&mv8Ocx5@y7@C?ZG%S=L~*J)lqamkLzn{~ zP_9#YQ@rUkJ&?r-!oC7osZ?nu6j!Z z7KD$>srq;5PFO3!27K}2ahCIm{%P_%cYQB!0*V?gRyP-=g%oP&(ShlgDm`FBaT^ESX<2Un0nu`r%40Jzoj-g zl7`=Q9u}meNLhn8A8)1Qwphj~KLKa2?5*Ov!Q1N= z36`|BJ3aZgpX%!At@jKV*li5;*bM@`#B@8hZKexxTyM-`Nd9qY5CUT^_W z1twb0)^P?=B(MaQN+Ph{09f)HUb(9Rf7F|^10GA(U~yP5yz{$miTrS00h4;l>zm-v zu7}SX?ebCASsbXA&njnA`J5;pL&0)wwOGS##kSl*c62aiC)c2Px5`DsmSe+BOp)H} z2cU9(a#yfPUtmis9_bQDh1wM>v+DL;B-mzJMX#UJg8ZIOvH)A0|$Y~H)!4}40b0B(uM3t9<}h%>o77z#U)E=#*}2KHAx}Fdz#z&Dq)fSzJGE7 zewuYW9kSn8I9Ro1WK~viTGRVDhkBXK!I$0!Sez9;_jab{vNbTmp93|v1t)6M`WyTu ze0$vC_4&MXs^7l7)z!-aI%rm|zTe%d3>#>KR|&^dnu6`uVY&pRhI5EDWc1y8_KZMS z??!>#Ef-vVdcW2-P@S^n>W=MQ7;y)q-XC;c^@2x70J zGyb}o%z$2ZUP*O0uoSR+w*eGW_HJkBqYV+beMls$Af8FRX5|ZRQ&BY!sI=%&b2=tz zdo!`jLj)pfVA*Vss>r2hCsVgcmQFB0`RuZ$TKx_c(fym7d!dKs+;W>vfZ9v)cCgQU4sj3 zmV*VdPwb<-qw5)2^WOh}QU_JB`3Ws6*fC(tv_*wyp5mZoAUmjhvqB+=HyCA@FA4h$ zMJ@1Rg9*{mth_gK@W<)0VYdZ`Wu4(tYt=Vzq-O`%;r0ej@BGeki8K(74btJ%{F`+7&u z$=6IjPF2958X8tzuh2qeAAxyT$B&R9VZ4Zz@U4eCJp2*SO&_R(HVzy3qg<6qSkaiA zu2k73lOj>$flbm8VK9%m0F(lGQPIh3ghGc0Dc;^Yq|FdW^m>OHtZv2`Z!X=| zuG$s?%S*`sQP_8DM$??v!qbb>>BPogMukuPV$%&gP+SeELc7KzNObSJEo94SLU5&_ z`)zpD^D*k#13FM>#aP4$ukapo8IRKSXPL%j2#E&;l~C>Nu$7gUN4j&A{0{ee+2tMF zoJ|Zc`t`|O;o`=l4adk@O-FO9p(jn*(|>n|J4c^T-BhjI$sN3RrMJ`*uUlvsZN7PB zJjgQlZH=r-cVYv^n)pI)FxppIC5`(NSB}i)j(JrA4t^DFM>B@4x!eg}clo9oHE?Co zF^k$BmnlIPg5nY#9h#UA(!B1O5c5uco*9=$ICXjO+EN~7tI$TSt3i9+>cfOxn&(tI$0X_V85mZQrLGO z9KunE?JB}qb+w-#$f3x=)!A7Byq<1V1JXia9z;PHP&mD(#==gBkT)vEenG}w@54Q} zoOhJt1Y9LzYT{B7KUD6V<*ZoUmsWp4Y(JU6CBt~ex+Mc6V;9u7L}Om3j>5|g++k~t zk$E#c$#E`(M%`cZ3t|(3T0QYrbFPH!iGvozJ+Xt(`!))ZH@h^$twxz0L3ZACGR!`h z10S7AhaJJLrfme-2cpIgJWaMX2pGqo5^_xF_%5A=lu#m0-^3q_$j(9GYj&ZWkB1P+{YVVxW*3RoMM=Jlst1K*A1+kTTf%ddy6P1<0 zhAqDMiv=SEqR4&htbS9=D(c}p&07K`*~HsFkm0K4@P~enY5|yns-yNjhCTxm9j!7D zofvNJUH(^&yKT8BtmSnG1OW%MW`vN-p>qi6z6@=>|G;QE^jtnDmsHQP`p!Ft6stGg z1{NNXew`!6&wi$>N1w&J2?Hk>(z;{mol{cwzK_4s%kN(uF_{xt26~cKrYfpsC4W+c z+W*vVN>U(y?C~V+_`*(X_W3oQsoz0V%)!Ff4EGH`R%ZY> z+xbL=a$gJzX5W7um|InUxqiPb@B&F~a|w%VXo%KQbZ5bwPSCEk^8u89iNBS@2oi2> zlf~19kimQ)ll^!2X=1=@M1|Z$%jLbogC|Q9T{y#p2!_&QWQ}5GIJ!hfv3qGXRYNi& z2B>+2OL^NFIYR&JnHzv=?zj}&QEFX_8ZiQ@9*wBmJB5Tjd7#>D&!)#A)4SD;jcXNc zka3p*q;X(*p~WD7M-n;>20?n*vu?b??6zRcK|aamF-dpUC|=wH?nN?jZD*9oWTW}u zJuakneR*J$a6>~^_qoeKEN)R~)6N7dOeeH!Rkhj9?&Bx_#Lnb0)l>}?*@UC>B7H(7 zH3iuhE@ut{7%+m;egYX{pI>e^pU)Z6=XxK%pX|ndqy&EIZ3qe=b6(!=0*pfj6Ia!G z5m1)%fPu$)qcn!G<=?v8G;l3Wp7(?LoJ<8>fD< z1`7%;7@7Bg%8M{$OUKynA?j#t-5X=Ip#i99w2&hjkvyF?SV#fZ!|a!j2FU0W;b!0= zxCek?>hZ?8*kCX(R$UxEXJ&7}uD+@~T+Rzg+6_P#_fM1Cy61OJ>;kAJ;2oo-0v^Dm zS3)*iE&_svOogP&L9*z$JffMS-IK%cP5ARq6Z)q^>bbG$d=~dCFb4m9#7-D2wT_bX zi_Qk(!u%fb9#Yr>ORxvt7O#nt4mA2osWPI-9Sc0A6iXp9+&P5kiAXX|+1~nT$lt2{ z^%%Tk4y|i?c{QTipar542tJ(nvAo=#CTG?75m)?y>7pp!fG_ z?!`-1vcYcji>$NQTTy2X<~JT51sB)!-n^Z3ZA?b8Y{Vs~k?$Ym%S}>G@GCAiK#m1( z)tat1AvG<-!;l-thx_cg5w4T}j%KU>%8hNZ3gT)ixsPHE&hhvPX;sv z-KPowbIkQuJY+H?=E5ehbu;bW9`DX_rb^x5#4gNuOLihfuhPiZS2~hrLC(`SW_0tY_)=5L7K810UkobYZCtE+6O*Bhu~TB}mp zyzX|L&MGl)F6t?#rhjasZ@WbX-jN{!FJ@+hF0bv|m^$}d`_~eoyx%vFr>y?>6dzX> zw+%QQc_Yo2$J!pJYi8qTcx0XlUO(~4*XsD#nmpq^9qfF2+zy*TZjnsr6ChUB;nsSJ z(f^#sP8Au`MIiH7E@uDbkR_|fxe1t^Fd%dQ8EfbyD`RyyjQL^cm3!)Zg;X1OH80^PWuaWo;$sb@!ku|}N!WroO)pYz z)Z*OzpEw@$!F$41gF9C`APr|WIUqa(>S^lDCN*KOw&?+tD`28R8AfV1eu7hwQy@}UdX=bQX$CjL>u?GA-8@ophU-cyre6a6DHq zHs~VV=7LtM=&|QIsT!|*ZgPHUAKBgecKA)}uh&03!CuXq9{)Ig!Pa2Y-qXm1=XAo3 ztsZ`OPZVt7r>EL|Ph&gA1H|&+-D_lN$OAG)E8UfJTMA<{DJb3bPJ%(YIp(&GkYcb# zu5KQLXo!Pj>y)b<@8!aN3%)}F_gXBWz+-xN^_Z{W9Ni&dHEr$k<}mX|D}6a7l3;L7MNUqRFSBKJ_9yPH8NpN) zq?{X&W_%Pj%cIg=FOJw2IWAuSOPzkBgeO#!_NbvJ{F?TYpD3E|%JK>R78=+#y3p+v zpwKovv*$8kz?wu2`sn^*Xz(b^h8TzLwl^ zeoNY@wVnLY5Ib4?>Gc55@8*-M`DOzt3e`?WeOOCf9uK<=`jY3U>bW|$hcWV+iQY4A z*Hs!@nOr4s7_MNdtZU|q>BsFzRh`;ABrW7MqrW%Xw7DB*ELB$Jv~7k3+h~>8x!=su zc{%H~*)ZcBCpI6{px=F)!a-%z@+%F`g#0mv0ommEGEH^1!xPmJnHYKvhYbVgkTAMXy^I);t~kW2~NT*CN< zuUOpZDAuesTvXQSRdWvmkKixfdS&k=&YBLj>?aGoLXyc*@8xQpxk`J7cv#$YvU+n` zm+0Exz$|BKDg^;ZhK*ZKs|YaO5mhNP@}s1r%(a=^rDPygRaR9oNdu1*4{2lOOGDBC z*ji^1qU-%?fv;;$XlA#X{rpA@m#KbAKF$SopzZm?8;+b3yylW7GaU_JdrWE)S-LAM zDk=(y&*iG^ITI1E?WPqbSGU zd*GcA4@UcTX+FJDBu%@adxcguyAs2*ij_Q^{$t{;x5sNQoHeyBxB04xh=)p9HnT|A z6WY2HF2`VzM-#pLNdR<U3Pmu z!MC^Wd%R1x4s**&nPxV6{5VAMBS#qFXpm>>hNSn`_n~5%XF<;LL3}Bep={ejs+e+J z_$`=0L$Nft{(YQ+g?08VY;>tFUeGa}xgc{A`|_LJg3w{I_>Xj(Ng*lYl85g{AVP!s zw_85GJIeF;h8u1k5L5W-xVsjeU=d`)Qk*uo5cWF$ez9F>$9!X91vuq5-{|uT3F#;+ zZ_fI1ji}zgKY6m!CWy3WFsUvFt<>Lsbaerh)-JB>pSemFF*QBSUMQe%WLr5l#+Ttj zNE{l?Gf?yV_9db$FQ#q9Z0LsEvu9yIt$cLs%vEg&`0?=@220j3?Se<)dhYwhAEc%m z&-G8A&an1PS7R?q7};LdmhK8NP?#(}ay&PCY4{VBc6zzsaF<*JXDX;{k{uXIrdskg zSa^B$C`LSG>Ova(oVR2y`(wSP9 zlr{-V=b_Mx!E?15n<=*b{tZ4=UdXA@&9fYiDY6{qFRQ6pYxnl?w~R^mOZ`8by$4iO zTh=XHh=`)XRX~&|0!k1A3P?r~5KwZC3JOZj843fUNGy_)a}JVoP$VZspd_Otp$HO- zSiE)6{{FtVd;ISkug2)xQUabjXYaMwnrp5(UGD)NedsuCQh#9+%tGAgh!xR)bIRkP z#<8|=k4%seeB9vM6$X~O>CVb36u@1k0sO>nlMj7Jv0`EB=dAE_IPQ?c*OXi9OcY38Dy~9)IHoSDbB8?Z*i!?DP#R3(!bVC}BFBtE{nJ zV4l?(iyiX`Nr)r$0O`Y}7tn^-}ae6kqr0LeRK4H^0@}^qgbU7{jkQaR{&t8x) zIs8#vhKqZJvLy}vI5s06yYqbdiSu8vN1^CZyaD#zVJ%ddk-RKLM$*w>aXVqn1c@(@ zRr2Ly?}G3L1r<67We%(zJiuMgPuAv^mX(^SL*GS?*R<&CJ^o^B=_n!o1PcMg@*~r= z3I`n~n)8QBt{Jy$_|wkApk&h!feq+lBEGMFa-;X6pjYJt3{W?VY0fjU)-fZ6ET?h6O z-|V|>Tc+5U=ptpY#t!VT2n+8 zCTjVHD(~(lkRA8G^y31FCf~z52HcwObumSD)1qp5jLDxr2OWT+Ou;16-UGc!(k9Tf zPHS#f2c#iD59{|@D}}~@J`s!wegc=AQx!RgX1}JTf$@&R+_xi@xku}&CwdNm5v_Iy z3omcIp^%wF(4N7$pkmCk4%#;?#i8eVtm!2^kkZ^sBsxYx%P8m1K3G5ddmj+PKCskN zcpNi}q#ZMgLwl>eFFBz4?^Y0t`wVomIp4H+yg9D+d9l&@)cpxl_*E!j8zj&pzZ9cL zJADD8fFKoD==&F&2lOS6o^p?eR)!qL&pgq5^QD7|?%znMP(y9Ay?Y`3^m%O& z=x(gRWsPdrVul5l>(_7h_cyKfYP6}An4ghp5Mq4Wu|yt6j4E+H^G}sGFP2-kOqXrvdN%h zMXgqfA&*nGwtmFzyQz%nskJH<-qmz7D0Q;pHW=mEdfe+IxYd>+JRQn0o`X#l&4|Na z{=C36qCAOKRqW>xRXJ*-oK>*1@JhTQ{!#BZSLX zE)GU)QDd$D+H)~yAm%~JFtz))v0=wr%Xqx@+%zNR6$ln z%mY6x4||!$87`56gPU7#ez3I9i^yYjp^}RsKpIm}SO@}ahf-2gsZFm*pmOs}6(wC< z-r4VLJb}QeFBf6u_;aT-CK6oclGo^*X4>B!)rP?35-^xas+q-nXA?){o%8*MvPS&= z!Blsmvono;vOW5lJHmha1!)<_I|q@aroEo==lWO*AvCl$GdK-K6dtv1q{E31$_iE* z!hPRDnzY<5ujfi|w!0!#F)HOvT89{GAi_maRF)mwxxLH*f20bp-e!>L!*jxL^K(9{ znv%HBpv2Zw<6&koQYC`NTMj2$#Ky6hbR~bU-yU>lZ1lHukChsE+m#M+Yi$d+*T;nZ z#L3C2ySwFo@z*#1t4qg@;y>9jqo$3R6j5HEoKbbfF8{IDAQ0RKS@5s#9|VHFd{u>0 zEqJ+NRSkd4xX+hZ3?_whTdCK2;z9gYENd4OhNWRtTcym!$9L&cAd?TM!s|! z?p3SZbrdcg-dO5lKU-%oTsfMUo^Fl!kpXu_j+C5U9oOdst|L#+opZ(YWUw*_IsT|9 zlWK;m)yqdo+8~+S(T0Ko&o;Y@mlmX<^fbff61O%tUuVCCik*NgBh#(aI!AjKZTihV z2&|w@^M@ro(lQ(-zHT*%?G!_Cqn1HG_Sr`6<81He*}%pO%b3}ULTs%X+XhI7tURhT zDlq=^S)Yt7J6(PLCD%FExBxVvA3rmij2NGAJSfl)T9%sTc)r<;ia= z28uaGjX3Wbs1NE?dOi?U(YdO#HH54fl-CNihQ)=nbtOe;m<-61yUlW!Tz{;nA-22v zM&HvVRXuMcK1)M|VM7(wbc}K56-d9t1JUAVZe_)kkY$EG=y>FGi5X;<+g->o?G*OleoA3xlycPR=Z@5IiPgqz^h|3nO!0|o8Z}l&O_$}kdKJ??3$0~T z{6-tV;y^OXf_xJC?{n_0N}cT-%@N3#T^}oAF2d~N zA#u5gsvtLRw;%bRMua=N-SV9nlppkQ!Rdv^RwVz63xHzR6SnxD15SBh>dyi9er|a2 zPC<=^((YD*?7e#`P!#Z4jif&L+6YuV3qcniB~UFVJDO#*vO_Q6cAk1w_?Yz0uX=sS z+eONOj4{-IjpfON#C`>#D$r4-j%u>>JF@V4`WAB4sJq61QGkNQ-(GcExw3*r@b3Nh znV2DqoA!joMKr;#OwcI+SVkU29@~u2Nz)=FcW1tQIZt>Vnytw$DY>A8C$8y5dT(qCys` z3Tr`Yf7(Taon2R_#2x9MObShdtr}dpPdK-m#NO`WHdKQpfPlLdUNiIfs1}MXwy7W1 z?F~CB#?Hds|5JSIStpx;^7He=YGT=`hNactf7v+wA{2Dc3b;O|Bnj(pVMldBgBzX(<6^7{jMv2mAWF39gBz>wrEM8h2ZKM_c!s^zwxj(E~8V})Hd z_&k+P=-j%xR)}Y}pyRDcdEQU8){}*rnmYFc3uCEPUODi#OTqX0>g;f3Wy5I@b5<=h zet`+;nijBvlIObR&@aQlrj?$nf?l_7B!!BVo@O|RiVxc1D%Q}d{VS>7`i_(~jQwsD zkG({p0w+Az&{Ks(UR<`dOD{GbqQ)|Y>_j7OdmQO|pPM!@8j<4cn|OVP|Jl+|q32pu zuI@K8MAU{t%JZ-@`SOi2&RH{*S9f-{f!e0VthxPZ@4B7aW3Ce_*1Ui_Oc-QTe|d1@ z;$)Ya6b!Xo2@;x?mh4|lkFWNPbv7(YOw=|TsltPOX@qItoTLBgr7}qWps!*gxJrLi zr>p+`)YkM9R<;-=SeBtd?oX2Vy?8fyP5fKrD>p$N6 zrF&RGwPHPn7^OKLu>D!4+Xhrjyp&U$(R+Ez9I1Eywp)-d(O@kN&M0-^e*vZ-mL zySqD3a=l<`|M672H(+Ku_x<3CI8_>@;ZdtRe8>d@lcTq4N0AyRV1K;nmD7`+z*8p!2e5{Sez zdeS%VgL;t~SbfWQFwGdkG?j02t+L9dMujQzkOId8qCgFN=%x+UF)?XSGbe;fbudGf zLmbY!TmH1Yg@2YLoV7g&(`9?1QpI9mDCe39My=BEp0wDxcyag$pq(x4)7f8QQL3IZ z5YH^J#R+*dax8*$m9q8wwc2e&Dh1Fn;9547=UEKXW<4b~=z-Js4HZnMdWp1dsVQmI z9^QwZSZCqFx*FVhP`^|sZh8sX6g!CIo}w7*Y~AXO_as4GIlaj|cBFaS>xZPran$|V zWhqir{Hx|PuOBMqg1lEzzsys(#0s2hlb)?Zj>4_IN5EekcA3t>B>((b5-GZCaqr#o zGrehpuD+Ek^t}lZbf6l3bfC=Ujb(XCOsc|O9Fl2!q0muHiWN(`2y+(oi(VA|k$=9} zHH%H3g0@S{fk6@ywJ(6s@|yN=(OwyI0e4&&aTNg3x*Fm6XV0_1=M8(^dhCs?Lf7Y& zE{aZ(K9E-P_Tg6>u3&KZdXLv0KRirDgHO`4safk|g_g6jzP>)V+oA>5&*!NmN%8VN zw$6g;Y;B?P8dzjf%XCAY3WlZ}K;C{0iiv6j!X(s)3f?v3ikcl!qrua72#0s3J5zp+ zeW-WE55L1CL%e`yA?DDr(>Wq;eHrHcJW?;n>gq|UKeOEVuHF><%AtDqnYuhNo{?|11-`_DSz5@ z(C9uzcz(dQ4zggQrV=oZ4O*z(Gl0%-NX1NQBp6dkN=lU!3@F@f&vf7G8n%@ag3QxIC!5T0 zg~R8M-c0F0{&kE&i$HX+R<*WP<2E3q#-a&oF*lZc&YjaX?JwXb6MotK^#6K9s+d=A zZoJ^&!j&Ev+}z~t*<2x=oSxP($6hkOh&rnlraJQTEWK*g=qL*17WU7h1BJSjY`aG4 zPoD5s=TB|XhtY71t^Q2v`iUP^m~DSo4U<%ckA;VbiM=p&yAm2detv7xJQB*c;D*jE z^1MtezLaickMg#b;y_y>-2`#wO%2n$0#9bZHsi-77d~k5lAB&Y-M@~{Ho6jb9HkqW z)_LI#K!QGuw7F1^RNB(oo<&)cEW9O^>46NUq|9WuwRpNA_%e~#g|lJ@QK#EfaAs0H zZOu!ly2A^Vu@RfEQDYUYTiy^J+>+|qUnJ(?Z0&OsxhL+R6uXD`#cU^C7*O}cYJB7( z3+)0h&c+NVrwJr_HrR|dtJzSHd0qA0!S8YA8Tul)z`wof9$?QKnz*z(Jn$9^9}~V(?R2Ig0P($HNYh#Bvcv*O72K)U*)nmSyG;#SMQU@&ABo5ytra>%5oWB zs|Cs6&&ox|0qS~akSwoR#0Rrnn0`GP?2MeEQvMZ>K_jwZ>Gu%%7?0zkXnb;VqJ_Ls zmq^09Yc!8Pz6j=t8h&W)0^+ZCy;t5c`F@kFr0QQ1ZKK03yj|xQu z{u&c(ehS~9t09xK#B6XtII4Q3mAivd3>pWZ#s(^z;+AO2^X*D``ot}J5Hy>3TrZsy zU>tL8u7n>Wo60Wtow(y=hLDp`?6hVAjnh2N0vZJ|Z?bO@a;1N6chk1WODs%iPjRsI(^ zs~yw%iO0ej89pFgcyxAjPasApLOHqXH$_^bz_39OWHxBzYvupo;4|2AeeD6I&CSj5 zpS(nx;dWhCdj$Wj?>^P1_}QI*y#6v!wxok>XG{p*YyjuZXTz--uSr;S&RxMR{o#%ca4`>0wVrp?O$Eq-4cSHn7aPq zgIZY7`+8$H+p~4WLB#NvWvLkLafzCP&U|imymBj_h>5u%5ow{la=!><^Qq}74X$40 zDt_2s`me+nIVVH~zr1rU&ki0B^mi7z_a_&xLD`3rIG37k7lPW5*@YXG~>f_)d?b`kX(w*P=(J7*o9(j7n3t#S0!MVLq%Dp%;u z0_WZ9$#p9jOlP`wu{e;YwI*9PId}@f!Y{=bVR?-V)n^)Le4uH3YHO~risEnqKO++p z)7s2e6;IC^u*Z0zhuxo4s$+29S(3`puTH3Qlq|o{Q@#Q-bi;YSe#IgwH){)Q>};*0 zu-sFY#tpC~7M0X+piH*%VgML^)F!G1Mr1&CQHKg0!MWj6SLSiZczPZW3lG24vgCFj z6@aM6@5U`c%1pb$G_+K#0EnHshaxsD@L$dpL>Bkw+ch!OORC*zQVq%3> zaP<+0!Ky(S2PODLBu~!Ozt{2AC&v-jSIuQ*W%bCIzzNMQ=vt`{4)``iCMQGi91jaE zlFQNI|GGM3C8u12Q%@f=09)b|B_DE&&`^6R=xqS;`rXT8Gf{b<7VI=(=^I?=mG%D& zu4U*~50KYdA+Z3ZD+#T{pKCnm>m+}$t| z{zJZXBU@elg)OI=3h4#LN79vuE&86#F(B(!MREJbD=6_R_7kS~>l}CeKPmgy4^(5G zEl^PRc;#dL}}rIta8&66nH#SY4@ ztKcfv@F`n)dp)PO(1d1R`|FA?SBGR3Bb+Xt$m!yBf#Y?#FP266z|hf6aNK*}FtRJo zhFgmO&jA1_%y1Y4u~?tiF@xd4@>(a{DGLOwFiUtzvAC$%`5=d2v}iX=2D*7Sr2{>) zoJQW|n#8_43zitdIbhJ5_4cjSgd{}AG7^Pvd;P(<b7&WMAVrSh`;a_2lRjfsAGRYf9-U#%;p39tdu~*DkG*tEo8- z<(N*IJlo8-d`g$C_oaF_Ei4)gJAhFEU94nqMo!f*h{=jW6N>+eJ0;vd_8|}oH4+5P zSg6P{s~HC~bq-j@`^UCt2rEE%xRov%r2n6(&8uiPby3-Vl^7tLLYJJkZ`Gg>028h} z{n97+1!6%5&Xxpc0MbDl<~T&DW87NH6$gm9Xx4KOd=+*7hQVk7lAoWKr%wSbn>(0x zMqN6~E_iWoEbQ)D^&BQy#?yvh7v9o@w{!lHgRNo$zxb>2?0wa(nY!*(#pLM6;?6mvTu*(inV1B?s974b$M0)K0F^!o0Ba;a^GRI_OUH6YneK;~vUfk;o zq?}rLwLN)EnLRz%K`q@BkTCFp^Fx+d$OX!<*w}>k?*;j~x`GQ&wSI}N*WOJlTVJH- zw=CmS{iV?rt7pG%?hEBV_};q#UnIH0{)vMALBiBaonJ3@;~2^>jgfyVtgv6Y`cM9@ zVgW5L+1Ej<%e{Y)Fr;b#?$VSZlW}2Bxm_qYZX}&6uJkM<(y<6++#OEXD6tw%qUI6> zi8L;?GK)9cJZuwfK=HTKGJ}OOSP?K**R-gn`-k)n9q4281v_kOLhP z%%o)y&p*SHoHy0373zj&ha=l{t)jzF$7Gn2?C`G$dkdC(avs-BRTc8{;}9pb&)bV= zaQ}9_2P+wruXoBQNXjzC#xg7R2tweWcix|IS9JmR{rkN&T;38aS(#!}5-x!1Va0<3 zN;)L#n)85tM8Pe6G1aN3eBevHO25utK40wQhTBk~eyPpFcJ9utK<)-=${`vDXqYJu zacTr!5j=M=lBe&k0PpY8mGl6~GC+$57L zLUY2)jb-tf(%ZPhsNC!TP%WWjnc_TPZ7?AX-Sq;r*ym?ceTabv4vWqb+Y`N|kkJl=^c>lmI zF!Is8UaS4E)t4f#XHt^P?vSF$j z8c`2@@@9U*(bj1P$t(Et$oSJ?yH;GEA97f$BNl=AZ$%`<)W}Qagr?<>aZ0iw;R)y} zVnG-pG%|9k{qy$^5OTiJcIBF6S&lcq)rnqX(wTq5b-wKK?;6UhyD^xy#ov6GD<4k? z5=oCiHSQ!o|z4Ox^2qTlC0=d01LREKU5i zU2*xV31{({o5*avY#}Bx>rTMIQ1ZVm4&14nCtnF!@KpW>F#`873+yJ=Za?3$nIgm> z&k;oY*M^;hK);&$UhIx~{(4~d)7tXyvceDcOk($5?=OysozeZ`WB+dN|0f=f8g4~1 zYz>flJyp%;w2Mu9KN(iSCImezUKk=uq~)7Kyosk6kCJ63I^agYswoRVA2TSNL3IT! zMp8^Cvc(}OwI1F=#R-687IKIId_GUjIHKx+bL^Qf#jB{QI>(M<4e(G2th<|wivlRb z3`7sSNn1FBub+9!`}YH|>Cq4bPSNt2SEb%_Qwk2w$zG?VER@~d8jhGQ*0Fq&HaBMu zSRJkhk2?LK=|cNYNOR>2fP6CBTPcZP@v-;#1nC>;QS59#et7oaNAvo=<=3xHMNzIU zHLVoGH#U@X)?9!wc}cUri#_nrRWp>Y!ROE}nNxv|sV@AEs;XH|&RKnbTHY&oLKq9y zewFdG*N+Iht~{IZ+)UkaI1t-wUw^=`<>5*O1!uqeNW*BxDbuf0mX97idNSKXW3jH6 zr*DB&BhZ0%R9^jMzut$=_BRP1(8?OT!83KrWVcCu`-kt56bMN1zsexicu!{!9N|5K z`gJUz=Z@t=oc`--ywBXt`Gf2(5RPO3Va~D~4se_nosdP1o$2rMTSu6d*^K z7hva1>e~pqO!U5?TX-o@Y$P4Leax08rdeeWh8AQ~e;8U)FOPofcr`%d!h#Bw&*h^D zrl+tLo6uwTr;mwye6*Yj-eu#}2&O+`eR)FK%kJ&n1Ox(a_z1bx(MTPbLLp@k^OPN~ zkd(tzIDmvSnCQ+4?zsj`(f7@Yt&67RUV(!tb#o;R=DC$Xwze=+H6vLz?0|2IRT&e6 zDIw&nb0J(1KJKPoNduiQK01K;|Ms#D&%$&f;Q#6D%wzykzn{et2;cvS;bqL!=6@}|kMnpat;BwJBYv8=>=^A2bhD#IUvB%|WKg9m~~I4kmG zC46d-Cf<`zubQX<=CPYrUS3^W>5A$tvw17JaX&}jWs(~?T4DJoW~k(GmvOMagl|IH z6u_{%3tAa*L*oIT#>dB#T-PkSv+@G^o>Ppd(K8uvDC%Pyk2@K;4S`Ur7cel`Z&sdw)NT?kh2s<|cPtX>weQ!jr7^y2pu9mS(G3>TDzcsp- znhq$aOJfnVq3*roQGgAZ2&jSg%a`CB91EJuZy}lJ;wQjqCkGvuOCc?+GGqYvT&{68 z`2JmlyD|9M%Y@`IBSuy|x~^PWxQlh;W#eTt19%Fe_+PTqy8;^G7Nk;1gda1rry0bV zztt!_vYJmRGrItQP~qUnR=pKp;2J_!u9cD(@W4vG7h>kxtV0wSe-i;CY%4<(jO=uonzET&vkD6PaIxx$+{)QZ^_)cLY$0c)>0k*c3_{Nv zfnfV`2F(i<%E+8@49W>rH-JSs-M^VoIK_nFKd{acv0|TMM-TagRmw_DN+Gl*<%r@ur^s370(i+gJN4OIIoJbznuUV|$uD`EZgmm!-5_KRw@tK!09->F_~ zj#~&!)1SxVoklhlzkIP+DH3r0)i&e_2oNxOzE>v7fPG#&1<+z-V-kV;w5cTSQ)Zpu|H4$<-+A{{8aIv{d;nzI7 z>Y}0~bH4-Mm`&&hYSBra8xQh;g&vpJL8;)^*XTcLy1C3B7F}fk+|Tq|BDpwR;_=14We} z$A%e{wzk%R>mWP)IJ0uW^;VifTnOubgQhm&QBcv+=FsvEfMT*YNsWSZ?$I(|XdIb- zcs7YihEW+XtvCxik;++Lx%-S-)~R%;n6rT00qukC?i9PZ3S(d)Rd4+G3%nq1#d(C| zAEd80BH=>R!?m@mOQR_u<~^>$T7d~&rIe*%#r|GTsph#5z|Q&k+i*~VP~uupD0(RW zx+oyNnSZ0ym25|1CZ@59kn>>Zne0#s`)pRV%pI04TK(W|7pnpIqrU7kx zBvx##S7Qwgplz21=$3}MdidAzL%vZb@S$aaBq*=l)BHc}B!F4)+arJ|^i+>y`h5K3 z^xC?xeigTTRiXU#7!EC_qjNJ58WgXXvU*uSafM=Q`4Y`(;&=GS>w+wi@{{+)Y}uSS z0onIDA!}$6csuq$sQQ0lHv$X7++I^WA0gv8)3Qu6$xir-%bbgNe&JjbG3s>3nH5Sl zTaI|vV7Y|r%+d!?rxR|h3xSL-9q$v{D4>33nqj3ub6^Rhn(Uz2*V%E^q|>lB`%Hp% z@j1lYYY1!6+ia;Tf;d$BmyJCPoRJdTsx`C?uS~lQ}LaqPe`Df|n^^xpI26Lf+=xA4>WZoGDS`<|!5dLrhLV_H? ze*-bqoEnd~UIX-QSl%!|BM40uARI3MgWG_s1?-``xs^vA$A!Jpm1U{5k$AXsN#ppx;!7p1ji7 zb`idUnsbAusfmp04LQRA!XbyG7F6?i08-^Qh??qHY}T_5FXWJ@bmRsFTq6hEdb+;b zJQ`F4VgR4L^vl%^&!`LoA0UgHLqxpwbJ$5kkUo$VX@afI9tZ5e=&CJ82$Z*Jaj_sX zQUVo(zvtd=a*26=0`jF&QVis3I>vH7WK0{+{grAl2l%y2ZE7b=X; zkB|3cGv)E(?YV>6Rum#a-B3}oL-4-WuPe>$s-VSQ+s4LP4=S19tNM-Edl*$RwVO*jK1RQ==%WuEjU z5_pTl`2bj3c%AUA7*&Gk01ngH`sm%!t?@ujNlEW4gWBMKE!^P0BThlv6~(k#0nx=q zc~#5qHD=ng*MJ!r>uQ>9XlNXaEoWXn9KR8iheEM75~EGB@xEmj3#Q}jO-KfbC>0X1b*XK{KS&q8Lt?ScR^FAjP zOGG+Z&<3c$<>qb%fFI4Hg=dA3rm@P5jErpUtTZ4nQl|Rx9)N9 zp<~XV%bbO^fL@Z9e2lCudn_#2pgb!HK$s-t!mAKYiMI#N`esedBP5SSdQ?Em31)rQ z0Rt9~%)Wnof<(Hz=T&M}qL&z=uPtcO#es7-sHAU6Ual2Zfk6AYH!H)tLjFM`eO<_e zp9CxcgcyYMF0fNui{8)$wdGGabLH%w)NO&(e?d>*_WK837ZOW|sTkI1%6vce>03VB zUt4vIJr7ZB>B#iN>AW)A%#UX^4qRl>KyXaaBrI}_F0{ZIN)OowvV}nnO%eJCH&4Jf z)G6f(B1b2?=!7l&cq7Nm=IaS%@g?R6v^$r4y#)w2ELY^o?(v{J!C1m_MMHCJBf}n%1DpX!AxS_vX_EYP+ z|GH+(df4w;)mE!QAv_CEI`%asI$g@&baMc1Y!sb0$-#}iJxAr~PNniU{&t=SJ`5`W znN%otz{#ehG4&1Z|E} zX%>u{gKD5tPg`CqEmeYmms;ky?8UL#CWf|gAR??$(QUt@y?z+n7SM^OxEIn_1{DL3FsJMz6jqtQxCWC18B8)9l5hw_3fM(=5Mb6tD@IWA@xl;WC)t_( zZB;61;p67cE;xJHnm3>a9y$&QDEkk6@XteM7f^c;%tcfJ|APVXIIiq@qA|k1!9hT^ z4TdLIzptb}c2*-6eYj4bc@loTY`}>XN}?O2P->yXzfd{UvH%+{Y4KxTeSt$RE0hIO zgu%8px&HoslFp1!g8u$vwWt;=yS)5|_VHgjrYQDrV3fW#8KI+A?WzM#bSiD()y$Xk zc2qtJ0UHQ-XyEdvq@>`G^|UmeMg>{stbiHTQhB`b z0YrQ|G)N4l0B4d~fH73mhn(R{zR?BnF?|D+kqcnr!WSck{sKrLnsksh4jck9pq-(N z04CrxXFqkfpdM>)-%cdlJM(qA$hhM?ucfY77w$N1PcJdWs$!g|L@^MP;4@{0h8_hq zTXt)fWMD1ngQZ1lA<`j$SA2=xxWm(r@q_~!<9FsKrAlh9%`+UGyLs=>dp}yJ3A~nk zF)Gy5kkp(hKM7y1oGC8x5hh{R`~QP*Q1oM_D-ylZE+5$J( zE0JT?QyOlglKGq{jDko;TsHUuKq0ut8zXqAY7vsn6sN`ugn7erp|cpSP#N_oRe>1y zWunE3x?JIOpfJP+m_(2!R6@G_k5>d_jq!cC9?}t)cGJIo1LP+2b%W#0n`&@}Zgs>~ z-wEU74f);7EdFLWT;T)_VpFx80d2rJI91E~O#TZCiZvC3UNrFc z%@8gEulYAEU|tn%GBT7eOtxvvZGJ;Ve5u-GfK1#DjidxH(*exTobAQE2u=;lga`GF zW&=eu&{u6W6}x&ht0!;N!$rSxGTaf8kG{T~e0dLmARtuEZZB$>&3${|y3%n3oYOW3 z@>x3f7lRI69MI=3!I+P}z5_M21CT0c-wNs9u)QbUL|^w6BDz1FliFl!*mav9&F&WA z7hUv9hx?Q$#Ds5!ag{1Y@i}pEamj(#=;Ojg*EQ2DD5QZLRuGc7PoJt2Q5sArA)Zbm zJi|==eN8mK*5^wfD8y)KI(`O zk|93irw;Kx_&XBsiE{b=J>GL={4?IeCIMlv+KlNqwAzOc^9a)wW@}%vQa@n`&#}wl zcr5?uK~K8UJ)~g^v)Q2KV}kgB0UAqHZV-CxcU16dG3Sf&-xs<6xAk$OO-;tc58ged zuBPmg^xXO{;04|HTdv@UzgQ4gmvGz~etKY-r%SsR%l zS!(i-LwQ{*8+SE)?Vty9W6goWFeo0u3oMg{hTZfvoRJ?n>970l6vSR8V>KsG;+!vQ z_cUCn|EbI!PldGW0d5HfU=daT^tXSPk?=83fBb|0)g3<_2XPo)x&8Spmz+8mAr)t{ zy|>VTyl3qI<&_$YW*N4G?Xy`>b#_6-$d>x0D%CD&&-WX6?o^MqqmJ>XKLS^~wR#5m z$B^0BrhpspOj{t3$KR$7rTmo*ANyFq2+NPylPHbv|53`;IUukD1jSGo$$%D@eEG|M z^FEl@1_|R!fQ7wtU87A`NyFF%?(MAPAyZ-oqlbL`=5oMlWH{FdSk*ALD@JzAoDKec znAsF$^DL_=Dq0tBp`xG-#df~svn_kfe8=W>77F)Y8)jvJF7!i81e01waEEr`z?xV z|3hC0z`%#j&YnBKL@z~t@N^*kVfNE5WT?8wY{iXD{mn!~cxw;x)2F}55|h@X`}wsG z8NnG4%=*aK_{on?{8Xp6cmJe-1I1f>sq84jzPuxV43w zM5xtYn2EgPI9{9depq?0e%P8^<87b^i-zBk6ot;W-^VFaq5hwFO8Ro*%mvn&p>ir9 zT73pDyEoU#Kdfz>{^z)rwcZ5a=EgohXs!sgueq_&wNsZdZQ@McWXg&dPA9 zXR8cX@FNZCXxA73nB?b~yl~$M%wt=p<3x^*wt1QTU}Qjr3Y+r4+~<1V9nd0-;eiJ3 zHLs;IlS4cHx<`;DpgXg*7X0t5B(uKDQn&~MPweufRi?{|6w+W<%i;sQ#k}R>uw0R7hsoA8*PjWMb_Rv~7f5s& z0GqBmnKXS)kWPS6@%n@w3UZ}79+M6=*s84YznTNR5}8XOENPtV%6vZkAO~u=G6rZ! z0t4(TWU=f}RtP$5r8&-NCY|D@mBaiS5(lHx`I59gW7_{;6MrwTdOIK>Q+xXcf$rp; zJBRO3yf}H7C>j6xw(lLKPtRIYlEzYGl=2SiCUe9qS3Ns?;xJhvYkd6K1GXfW&UOna zf4U=gkkZxlEuLb;kM&(7U&zF^7MEGD!1mlqbjbI)-8m0qpcDUo3E*aX*&P-|HAO{> zfp_Et9z(6><)94HGg2)LkzE7a#@U7)4MxxNoZgunSJ6=rzN!eu$nQ$Ly)A-j(Oh9% zSb(ER+J;5y429LQ)RnD&9aGEGm!uF;MZm_dlWk~O%q4xl&%rEd*FATvMOGD+KvGc` zDhQ>A6TDf2;GtV||AQ?kE=mv)gy3;#qR>tkF-Q&3LhZhjK6NtYw#tNLY>)O$F>%?|% z1PS6{8hTy@AnMuTIcIffv@|p9^9z#bwJ3VJciIvqxq$3+Tc#UU1ajBFG76LYpq`L3 zHWsFikLb=@oVg=^A6Ge>ox>H`&nr!2wvqjp?rW~RJ@Xt!>84lNVO^PTz1nS^oHTkf z!>(Hl;2^n{hw?lBiO!unZ%5qiUPWHdNVBRP{_%-DxR>mP5OO>J?G;q`k|Hi$K4yTO z$2M-n1AiR&f3o<6VL1)U!4fslW633F*wXi*u)13+!ed+O*Jv#%wE$(9!*#o<12W3D zKdDtOx8Cr6(cV4tRcz*?HXlh*0Y`6nbv209#nrVLa8V#| z=U2?{Y#s$Mo$R2XD=uzg*L`Tvk%qSy}JG0#?T6 z&PShQ+Q0No8K8dZdpKa+J))q2(g@tCHv=UWfykG-<*AWZjh^~R8CC3F^w$D|b5%=A zONc@jlCJ7X`a0`~c*52I3|}4KhQ(i}X}qdlFG-XMx=o`ATU^>O)zz1kN3LkUdg6@# zckmNE^L0Hv9Vh6qRZDwq>d|oq!JA<9hh4)-gXb_pGz&L0G-T@^of;{pOoE5_WPJ{e zYYnT0k}v`CiWtt+P$T)M^@?7*WRJ$@WtaCi+H$5}d+=MgDOk$>z_gV+9Yv)3XEnE( z=Yp3X*Ez$u;U*R!_Aw#EKJiYQMiX~no+P|_l~v(v69|vN+h-BO3e}Lxg9mg}pxP!w zcI1fKXiXQ$^}lC@*c~NkH+8{dYvwKtQnlo+u#g_qHIOCOOt@tS%_IrEilTePyPp$) zq>-LYM%~)4XA;GC6Lw)i_{Ad~1NR}n(tW`tvd@nZ-KUgpg9ompEo)tIFcaA7`V}~d zD7Gx`$bqsYAOySg4{&{YFk=u59&8p{)e=7H>Uf3Vf|}`H-*@R-k7Rpj=)qlogM$h4 zaK7_8W&!8WAUze3)GoQ^>{8?(xp0lvpO)w0v(``!$leY`>hquO zjwmeV2Fo6hPYw+XEX~yyiT6G_3P!I+4l~Lx>Zf1tjDO3&2h!PK$jQYqQ>r>h&LGIl z&(A+U=%fldg7Uumb8lWd_4P?XKv_s^32FMdK3|Dh2;605W8=Ev{Ib!X?!3gn2WBnC zLfan5?RqL4RA73iHR0+dlnc$ckF~>k=iVI9mez1$*`gk?O5Z=m&Xfc)iQO6*(H|(c zi-S3AHGf(WW{N;@XpQJaT$Y}Nt%aGhvO^O#;{nKt2g~x-rY0Sg)T-3yq=8Wbc+$Wp zN9V6Ge)8A{K!P+L*$1$4%Z>AwH2Pe8V1CHj!|xqD;;M^Twrp;}#6-D}*+BrCzA=7H z^yyPI`{fC(dvAX|RnH@gtUiiCJektuEvRTKZb~I7$wj<0-T391y4x7YlzUFHs<+ro zXm<_kz;^z8zwjx6vtwJzwso&wdH4p@Bxn^r`x@r*OSH(Oi}`CrI{E4Qn{P%<+D{|o zytHrM%FJJZG(}yx?u^!LTj9F3U?*6VLlKl+oER3OT>l-oriY;{qogKJ2r1gU=!V8d z@IyNc1-0BYW4~T-i?m-dd;!=v)nAJJvjh(1K74y?blpHjqE`77`!c^M*NdS#H7vSB zJuqqhY0k3N3Cf^e#tl;)?-ws%$a5MQ<`7BU-3|mQ^PMOMO*5Y{!T%OMO?wW1en!$p zO-Y%>gdWNO9d8IhJmkIkkbXmlC&qE^u$ueMnH(K_#tiXxBWc|2VnoavkWdNmdDkwO z@c8Nz2#R=LQ{;j{`7wgEp`if+h!|9TE_8#fZTGLh5WMR3+o@D<31!dOo{aB`QD$9LO08F)X4m~|aopgM#)Rig zyT7|ev`o_o@+ac@n0LBI#0laE%Pz>ncKMnp;zv1BJ<56IvjIX~obAOyf_p=z`_+kE zYWgbZ)z7KsXg^)X=iMLZ`ueqgb@2cR89NBZOH7m1@osK~_Z4=3e<+^>w7dLXNx&m!{4NY$f$Y8Ybh`da?65aH1*?DJ@>4ID)g9?ir) zkETv(J(tuJ*Ixm2P|yL5TZLkK=8N=mP{3ECG0Oa5$zNVqGkte^k8VEuo zB5>bIDsn)!e0FG4iG(yy_3qtlphLug)EUOKm&|_7kO%{&d^#d-qA7hjQKld{0_1sK z;G(`QL}=i!r44R)?i3+7Ca5~}GzSwyB)i!^!rs!K5O zf%2x_EF~nN0|)l-*|oYlPS=I2{an!c>pX>Vrx9%&|%k&!{t7uW510pu#B{9R$@#R7yi ziQrthCpK8>!^z6p-v9mFn=%tUwHC~rffr1#Ogz%>BuAaO1<(!ZtVy}dvbE!1XT)m( z_8hZftQgH}yJxt6Mv5~A(QQAS3FA;vhDn;p3_+lECA>UtmL(h32f_r=vF-)WkuysM zH1GXRU5d{2+-+osl1{>BT`AuX^mzq6cl>TiVC68UE)!&-Cq11d7qNZ^1E`z9?sjc1 zG=hK=IRK_Hxlatt22WjdmWH=ekqF)zw!6AHZKqb_-hVfSf4QW>K`3QwH!ZG4S+&Q3 zL1;ZI{L-#C23*N)I->X`i4RpJ5R>+0DJDGYJWSo(J2XrTBL6xiVWgubOeQ26DzEI4LmG%Rze*WZ&eUsav~h{Ibe; zafHQnso^^rMQ!Lzv@aCk%K_~ZvH?J5MiM}nuc`*U*tV9gjf=V)-Y>~x&OZMDYSZ0F zMyi&nI#T4(93+aYTX?#`Gb`u*gF zF(NA-)(%SM_DxmK8juB)zV5LVrH;$hF2=a7{>%&%BM9pllJc~k>0&bGUzS1vcH(>r zA&_}Y=rvmvEN_0AlL8c`GdSjNCvr9J>40lVuE%ybG(+2FKPQB-f=Ed?8M%H2gyVtY zz2^ly7k;gATiEC+nGK?(e`zsK9LlXC6sEY+TTuL|XTX{`WN2tewO~R5LAa4Kba#sa zms4t-x{Y3MX1Nyzbt$Z&Ovv8UaqgcL#5__;ETT;0cA9TI|MK{{v81HT>j-M56*F~J zXzbYvEE+OIGaTUYA^@Z=-f7FFsT-^BAI%_j=o9;;ska!)#T!CMszx)$yl9vc!|+04 zVPt4_4tdK~gd#7}_VUl^xhwQkEHjZ`13Ynk4ApytTOeh8i;nNb4Q_JOrrr}7*pQ&K zSdAY1vA`H#R`wNYvaZiH0T&M#TWz*~Iw*{&J9{6+Ua6diqX?#uIm0-66$eW^Qb3;| zT{Yt+r|vhE@85;Mujl;CB1tfkE}mf7%7VWEPt@4VT$QwvOydqzj&8a9*Hn*WB+P-@w1E~73FTk+e{ z;pQ7bhDt5rmcZtg@y^g(d6%oJI@X`AX<}yPfg}3V(}T9*)fSQEe3|w$Co>Id-}L1k zR}VSA#wrtXzN7cU^QL0Y>P>;PP{Hq$iWn@SvLWmW?aJ zRO04aEGP8Er-a`+g|ROM#d>bpz-(V7r)v8B%<1zdDMLW)W~JDD6I)@=af?8pMr<>n zcXk(Q!Zf$WMB7JV^P_t)#|}Mb1qqeco*Uoop!v~jPCsETz+KvDP`+SigNXbJ+CKy1 zmF%0(T?K{cQQ{>nqni&LMbK}Do9TjSg4-rFTnZU%C4nz6cG1Odh^I1Eq1|#qx%o|V z%5v7wh6|w5qFBj*gQ(6R{uLIDpnelZz3=a2CXV&7wJw%Hgdo(F0Y2XPjDXe*_dhu# z7oc)W*Ir!>nisZL7Y#cDP-kxb4*JxXs6vV6_Ypq+t$6^JGBhY#1H8IVSop0J1rl$mlEaFodaxmgY@0VQ}0VX3S-ZX z+QqX%usLo}bFMl`_l`K5_8t2SE=(Xpbs7Yllo55`#dFM9tzy$^aNVpoB+Y!O~vhjmCzq1-<7>*RgEL%5pogd zRBH+KTAuDx4^gTIo{-Z@z6D6i`OQd8i8xAYoH{NZvtWii5rH`L3Ijx9tL+XNJiBf&drA2Bjdxr@vBPZc_f=D7g2(Q zwwpJP7rAfhRtB6oc`}uMls@tNz1f*TtI~(;j80BYh+j@@ETbTr0O_XW0(3KBigUNR z!!}^Zq+@9{8?il-2^ifYDvM2v^VD6N?d{RA>Hx_Rqay3?_NN?g#(ua#4Gkfm^)ur7 zpN7cogqrE648~gL-nl&)T$hB@6Z{t)K_^&`RjcN*Z1~^-ySE`k1vGZ7&4GZvpe9(Q z?J`3hGk!ye8V3q!%1rP$mpso zQ^ygs!nAjX*K+8}+QM#8e?_Xi$2~AzS65b6E@?XckXPF7I^0ZTa*A6=J$dWgYIY_Q zAe&R|)6ju_+72D)Y!=|t-OzCqj=OV-!QpQw6;uDI@2KxP~{Qc8-rxL0rd}M3uni9I#~3>g@$Opu2@G z3|B_v4jG^&v)u3>fH6em8z6cP$~2EHVWR=CWYbGi*wZj-tOAww3*fglQ{lOr9?l(= z^5OZYGTb3nB+~o?MC{&LlY>()_0uOiNO^%9`Zzzfx!H^sRyvYq!Y@P)J53R3oyo#; z=g$fu;@!c)SSm(O8F>G55Oej{unb5TW{6D|7Kkv(srPUhmcx;ddPE=(38}6yAt4rw zi(?FLeX z;m$FnXql|h1&mgEM<9lxALI_07?Ty z!+3znfPieiyJ2BJjnF9mm>%~@H|4}tpZ%5l~5B@8gEGEg_?hqts{T&N>h^=mv;O*{6I zmBivQK&CV3=m~daL&~H+HOEcOI3YfWUK~~%reqLG^!9GaR8EW?bu0#i%)Fn&qB%r) z_%#V_=%^r_ATz3VP;YKWVoi#90thbnpb6Lr>L~s>eCOft3I92K4Y+_dnF=BRT5Z!N zO*LAPhnmi>9Y%TIb3-vZbNpC9YTtkVD2@}fH~9Jn7YP6JL{+d`AO>izIY>(jVwY{~+bQFUSId5yu9$_@@ch_&lQ+ zd8S)(vbzc)lW;tjt93sC9G&hk7lPpHIz_G>g=Z5k!Ot72(2s|$u#{W&qn$V>;vMd{%~>bTo38fPxMe}oe+;8FIhIiw!YVEWf@ ztb@9r_VuJB(ur0knANOK_-5TdWdkBLMK6M*KBOk`2TCrH8|%rAQJ>0F>h2b}X`x|T z&PzGQw7DQ?ynAsDrl%6E<@e`|a2>+ymZAU=!)ut5?L_$f=cKuyEBO&^_iG1u<8W?L z;G3{Bu-02{VvMl6F=>-6_nWx3qeNl~pGfKFSB*Yh&7H2z$+!ZaSu8l@{4uwV&sH*p z;F@{2zdB?a4^58hth7*o#SeEg;=w%y6l&)%L7?&3aXrcjZ}>Y+9DL>no&u5E@9o?E zK(VDhnAOyB^r9T5AlkJ5^%r;dN@7hcBvRP-`ueIO&{rY=|6tv*492$Uza~1LLJ+s= zwO!mp!oYm9Y|hy;D<5Ghgsfk~EX~{{h#$9qI<8YhitCZ%hRvoDpl}RUV|!xno=p5t zfRXPnK_^Q{DEI6gEEw)9Fct9qCAq|7eBTCg$2&IMl$FVVsrz9tA1*wSNlF3h5yWk& z2!>NIIZR|%6?Jfez5zZaIocLW4dk0VkRSdtw(OG65ni+S0u09 zX$ptW<-N=%HVn%e^~F&*w#0uybMgP5&>V`ljyhh9)#d6acpbp98PBCV3GCKn-=wXv~2%w zmV&RB*;c%2$BFi2snI%)pLj&Vr;uCE#%jLnmep6flf|LC%9Ode(n@#zX@I9N(@&LW zLin>tIE9gZLu;ettz5-yjJ^`qU{5Si09`6w`7}CqbU*!-DvTYxEW~ z9UepFj*$p(*c^)XdXQAKrcHe&NFUBN9LK|;@f}+7aDugacT(Cs#?VU@zK-YKvoyd5 z^W8npU$ z5ke&dWjtA|erlr|L}Sk@zWER)9|)W>zMN)vpzL&z$lu2_4UQ?`-TrjAkm=5ZOgLW< zuKH9)1t!k0u(0GjBh#moO+Hbg+|W&}Lxb7_BL#R&6shG>9MtHx?{s_+$S8qp<@QBj zcr3<-13L45ZpFxCvt9!XkdSv?*umpKJIV38AVo?1DMd3Q|a6r@T(f zzaGswX+dxIhu>z&qgOUfk7Q4$5!^(3_D6}`wVr_>Ovch$w*bMVy*s-#?}#SAoR(qKE|vcbY2 zE!N}7Su=DQdi-O%k9s{vOI-wX117SoCsNj1sTs~?eh#0w1bSHV=-oC6Lzj!vM$qUo zZH%9nCbsjqJUV~9M3e#^NmS$C@E~N}C_P;sb2RVz7%^ybIM?WX=!N}&mS&1b4L(1zQqSno0WqR9;Cb>z%k(ytpFVzEg85?#FoitE zf5GAqQ8I@*hYjvL8rff?Op*2tn&;k5FcqMp>H|Z!@QsB6qT1>&9QcjggOrZc7cc0v zve{r444r1^e33MxStev-fhD7WZYN1uC$yVjRA-J1^u(P`R%OG=)Z?y{Z+nQpBa zcAr2|bOlz3;GNP0c(fbIxyTUb{_H`6`ggRwlg)EApIrv?2O-sW072&bXmisV{1en* z2au}d)oVVAkUjz#_2Gezv-Csasm@Iu`PWNzD1ZImF4Yi9UNg#ofPNL|XQJzMy2@)@0h#Pp@1QJC?8?GE{`N;oqbvk?YxHXx!*%%iX}v zy@R?a7brGxKXiXox4C(%XVIc#KS9iHdspQ768mr#c^GC)pDDVEMkL+}-EX+uH`H&F zrGOPGw_S~CfKs%c!h-sGRr7 z0L-JoKj{8w*%|oBWSR|zU>v!3;zyszgw4U>^P%h(a#5G;{ITddfH|+@v@55Y4A{|8y{xs#hT$y1Z?t&Y8T!)bJ+vz zOXe*$vXrnHrfFT{rwM4L>KKz%k;q{8pLJMn8BR^B-i@d%hf5b*)EZh_B_cbxowr4< z$z*+TdOEbh-9EsUvQK1bHur#hAyoXn^eEmZM6qpqbfC`428FhLrrP~w8-$zg*(p0&Td0nRxQgR8|&O@H$A2~ z7=%yd=$1V0D}C^>*qxXoG4s@>#vJQKiQO^iRcL(v-bvR-a$*ni}W6B#Nlg{y>E9&g5+g; zdBjyZv_)i%%J>*VPt`!aqM`phzzTWLz@S0zz*5z~h(xc{>z56(}n7Cb? zbMM~Vc&KP3E5mn)#g?NDWtH4pg)~12$Jr)Az}K&Q94(`x?S)>W<@+JYz+RQpT&^>m z23m?R-Sy`IpK`~G%LpSjn&9XuKU&R@^wMqG?2E1lZg7o>Zx<(N(kU#&ds9C)Ku^qm zCO)Ffg2&iaQ>JHzvsgA{z_UU(q_4!;O+80V>PagN20AlpEw`X@Cof&5Hti&Of#uXp zjE4Kcmv`uE;!-J1t3JVR&3|haD_pHGqr;gyFOJ#EUP!QdR%+>BoQ}NYxveSJ{Xyx% zr>jyPW#+n>6RCQ`Oah7}&jX-+6)oPSJ4%y-c_ouDm%$ceF*Rf4xZ8F~W}{@*CS}Qn ze85%vlDnbu;c?o+HJ@@`S3+lORrZ5iq@MaJGU3&h6}*~FLR*TX;3NRq3zo)k=(E`~oe0HAI^kmi(K*%C0p) z`cRWprZOx11Ic^$vL=j<FPsT~Fp#4zh4t}}4L836od8_?vJsXaf##_SG?!E>aQcW~1&)W= z+9W0Q$u22IMju2ccv*g1+<9q4L9-aP7?5f%1L?xO&w!;hyYe$M)hLAk*j)X2A$V-y zV=Vy4796}iING#F#|}4DofSKfuc%SvTQ*hNqywBo{^ek^SL- zy@QIT_yQ-_ZyphI;NyT-f~T3u?GZwZ7mMaa&AVgZ|6&}svLd&(mYMO6mIZVS_8liE zP(c(yp~0aff^YXuHhE6^L#|p-Yj7(`M36JdIfRtFLsmuwFbM4$3tYSOWnbuGClRkI zKB;!}IXZyH0B*s+=0yPq{ki67d6j$u-Hw4q3CT!jFulXlh|)V6v^*}l`>9$u2fE0p z(LiPZ^PV4CDA+00))woO*~OXZK7JfB=IIuYsb+@$^4bEnKwLU`zvz2C*^NZl5u6tW zi)hczCLZU7?^!R%j{>YE4sZN5gS~L`zNcxA>*s+N66v^Omz=SXp*jOQOlKfAZ~$)H z5le4)^ICa=BDK(VKKTS1-VGX-?L|jhpJ}6 z_Od9u#CEIMWQOwAOEW4BcS7dBCnnAhtZLT1u=e}wz0++jqg~$W>iYgX35gv|t}On* zzV5~0ydkJX?Taxb=hfWreu`fhl1CZ>F@xu7jIWQ(`zqkWW5X<0D;NE4_`i>J^OHKB zmGLY|+-lo17YfM0kfT`E{opUI(-{$KD_7F}#}(C~eyQc(d$71OrP==6D?HxaHRS6m zIbT-dybm=*jAAL$b;Moz(>;k>O=3w59P~^H=?`+bRoY#1n>uR(MR&J7$((i>ymVlgU*W1H7e3_cci#p* Hk*{Gwk* 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 `jTi@3vNGAX8Ck)oFJUu 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 m(?mr#d#2c8 z?;JqDP6p~(uZt~3IgjsUX~n%cCG(Ta1C4vqF*K@OFkuuk3MSJgsyHq33l79X@^>2| zqL}+E62cx7PoLh<;zde@k)z_a1M}ry)t{#pGEukqPa`qtus}vlcY>|2PXn!oB{)p~ zWTMYo`9u*i`=su!|9){kz@TON&q+d1mrxK*MD_mAAdBQRO=yDQ^mKWUqO-*M?C%$4 z4~F&+4nVv*+F!Xx!te9}N*|8X3N66$a#{4hS)Vd~0E{C@PaIcL4BDb8uzhDY=i{IO zm%kt3PLh1W%DD#V;%~u%g?B{vSre$Fd6z$>KN-2mo{QfsQu_s4FCeG$94 z!T+nvNzM4%ZndzQTHxbXqGJHMugICcpa7YJ~>j-jVKk@C`sf{tjA$ z*lZX@f6FIwmVwTyT1Ax)$-0};XhB#g>abl{90?S<`$-iRMFKGj75>QcHD;4Ys?w9` zp97v#cU^}tYTIH=fdh#6wpC2+-8HxliR723^F*Y4ZdVu8gG8&!IUjwvh-2!}?VdXB zoq$V8N0+xY#t3oFi@+C42V7jNbrYI#vQu#lP7lud0pqYP2ewg`|3GJR4CExd4*xw%&*&(i|wWvHMWpTXe>1#Lc6h zM(A<`q4s^f#n2R|9KTB!|A=-?*!J(#bc6n@E}Q3K(o}cJ^6!sDFw+v(&G&>waNm&U zShkAJX{`Vkn>_tgYH?YHn@y0@@;Z@c~-8^=12YwuCLwZrY;G zv@w~uDXvlHM+M%_c#jXuxKf2!u8^uwyq7Y5zV_>;p`qcE_Db1vtD=OKeRx;e#af@c zM$z32TvqwWQ6>;zw#|c9 z`{%_1w~JX%v&|$A&aW3U0x>Qli;d3_DWQA~#Y(u)pX9=M@6eM}*8e}L$@2+!|CL|2 zNf9;V+FAt4V{J{iHMR^!b-jyWLVx)Z(O+}Iy1mTj519emv!6+I9R&piNGtkB;X079 zod-o8VUh03m(qt@eOTE#Wmm!Ey$?*aUlcyvo4x6>Gt%LClK55l@#AaYM5%b4&I#_0 z3tTgkL4yvm1L_4A5vS@$DE}>*Q)nEfLPY$JBFwm!Gaf&~b)WD3;mk?6*hVuy1z}&Z z1YRBRE^ElB&$2MRB>(C%4ucDA2A-Zp-(O=&d@=5L0Ck@4JB0M_l(M*P5hCedTvpZ@ zm}M96Bdp11bY}cpGSNMwOYLzL{!2@wOQWtR^}Nw{X=&c=mcH z!_SYiuOrZfyde3yquzcMf>=abZPdD(`7jDb4os|1$sm{Rjd$|?*RLDrg8ssHLxI$ut3uf9UaeE7CcfAxa!;Zday zkAy$bIx{gxx}WD|^*Ye#ioLnJSQd<>eF=dF!@qk?wz>K87VodN9#-1B7XvCcdC)ud|KA3^R#YsQCy_KIXT zZ_1Cau4XO%QjZ{UQJ=9* zAyjSa{OSZKQ!ZSh2*^}rd@q<=5yM!mzeCSuG3@7btZ1{bgtN-K>O1JrUXU^x)g<$aA{jM_I;pj1hPg&8kPB!R!Pjq~2zLu*=;IZDdL<*Wd4b zWV63TyxS|X*SJ$C+wGf)r&a32zNRLH4NxAPmxAO|LYkU6y z(qc20m6?HxwT{={Yx3=fp2YGv<4h%wokNwXK5xjLf6MykZC-FpWRP3P^7_=w_kUt- ztuqPN*5y`vpEqZ*VCM9j zy+ZHDyXg%Vut@?4_0oMFnw+-vUoIvfAflLk+g* zU(6}IfUT|EEas7oJ%9H{fBuus)z^i}JaJPTi7&B_1Thi*q`U1#<7-mP0@>?dytr=n zvORtcI{y4;E%#p@?pjmRSw8^9vsTM1C#T0p`z-70P2J=3H{XG%RHI~N&)v)l^n)+>VN&jgdcC9t{42j^zK8V}Y>)l}mrI zC4pMr=vlm=8^J$5q-!Wb53x)$)%%E$a$`167|8V7Vf6V3H=lqO2^%w!GbdB)?Gz`ls)G!ZWZqD(C681@gwo7FYVz_M&gaX-QRR_@~6gLPP$|DqrqUjU4##n#J zOCKph1)cx}@(^{J0X_?QMF@jTb%4ok)bTC59>N1jpmPdi5Pg<0$ip;4**Y+pa*XZI z)Fz3s&GlJhA07@SYZZmAW^Mk-Jf}Yvd6j@5Ks9Tow!Xe?oUKOTmFORY|4)Vl@jKgn z%{PfV&XzmcwWSANnV-&v`m%2d#P3gHVJGlWvWtPh4Tco)JQUv+-up9+B61T-;9OL0 zjVdGsFuEa{bEC7`m1xw(mS}i5ni7Csy;n4(Q0Q`Yigo(q+w(O6t2JXG zG1~S9^bPJym03N@>Er8W6EH#@E;wI~FsS(AHj^9kXzO}hx!l9LoonN24XRUx483FQ zuTE69EuQl?IR|YOZj}qT#d?bATYk&vK6yN~zaD|xM4KO{C8V7dV^HSX+|lyIj)=x) z_%7Kq9xLY6B@D6YVp@viC4mPs^`gyf#W^QmzHb{JaXY5P_@{%I6NnpZlxW&dx@80GqPb#&ibnJ|dXFA*Gmb=&^a za=X96kt$QoGSGQv#oWbVdx`wz%ThponENYV@tnboL7@>yxS3PfH zZuE!%fq@Y~o%IVJq$ce0FU2&5h%XH!ByC7rS+RVQ2$I{~Uz`W;9^0s+s;4LTSzg~C z91dI%iTFt9=pURR7lozdGJOjrb6`LyO4 z!;A>n7M`f8!rm>$#i&_`o17aCf2lxlB~!1{KAKMffmFs+Woib9RZN?7evS_OLmdbY z1ZiNb?+lw*g010-F{9>zGkx}JrfE+XmUmTCuKoD1$AGRt1UU{3g0lfHYiKB4^<0aD zW;zniy1S3D3*-WGZDJySxW7mYQXu+zsu=!bc^g(`YU-=d+xUU(Goj(#9p=%MqXJ~R z(f73xDG5?2<;Kuh>|0U7Ueo6FXTLqYDredw%j{SiA9oLSTJPNhssi=O9gQ>$#SJ9V zz=^?a=rkZ?Tl9Mnf&2b$b3{$cz9j6~fGH!>@zSVlvY`am$uc%P`UDAh2YlE+=K>d- ziSyeu>C9u!b4c(Py9hjb3)NU37S+()=RTYZ#8?*1)2j#Pq?jzu3Zk%ETwI@3O75uu zGg4a0QM&VmGO4Q+d1XL}G$ES4zd~aO*zs-u zQM1wV1S*J-!_XPgnXyb@jesaFfE6r zu!AK653!*f<(Zp>GLHr?%6!v$K>&hN=JnaPK@pd%BI9pZ_ZXwUr$wA!Ij*=W)F3ai z_aYGGFGPWEjJ2OCDz*ZvtkN!R3I^IrDVIbF^T5!wSYA7KD4Ku*tUUSs-0TMVi(#K! z^2^)4>uL4NS#DHh<2AKE3$m;$2Q#l!Cnyy?rsy17aZOvua`v-F;#SB$4?*2_JzHE^ zzOKf7SU>1|me84 zELSr$_BSZ_M4^D)^6@7Pcoa?yK$1uX_IcD-%LA^^F!`ic8@bR};xg*-!S>k1>d7S? zU6MrC14o;ACEvzlzeY-UH~R(3m#?rA)pFE!r-DR@I2Z0gRX%h};+r_!V?=$rXJmP5 z^dU7=%s0u(ViuXUCzH^2dH=rY3MnauXVPtA&IKPbzU@o#1_ew4PIe+;v^Z?jQ^uT7hQUCv^$ljz)(LiP4EtM zFXl^q!OkVQ&dJpVCI-#V-%ED?KUSwutKPRt5usI%*u6$TVA)v;o5sJP8fI5V_3xD> znKNPS@K~=Zpz6%e17?U+9Dv3YYI?ty+Y4d*5)k^_@lWZG&sg`pE0E}4Y#*D~c4R1L zeyH+6h87c5T2}RGeU*^q7~zef0wERZ+i*SWu(lTH+1w7Hi}d!d@;rKFL72I@fmFXP zJP&i78ob=6Cs_^~&hUUc2)5VJM`gA&n3S&bzImHX9X1mGY1NK@L##iX0Fi%E(%-_J zTAe^R5eb8#W&d60dVtiqYU1VZT}5@SdS7wsc&k9Jyq0?*(U=j#OGq^1?wE8UBbRH_ z6;)F%$60%_9i*@d62ab?J;dg`7o{P`UE`sKAbNyM=5S{I5g86-`L1*O8@NB|7l-HQ;7=-2SJN6t76g$ zW~J;fdiaeSm)bybZ&13-3ci`Ke+UWcym0g>+180DsU%=>g#N_%_$NiHp<-2#3i~ZB zZP?hk9P2l(j#LC~%({F3Z);d#9Nc7`r_dL7cXltF7!N2leFBk$q-<5dVqcg-S%LYJ z0ifV1!S_Sz?g;?~qP#)03;$I=TZq>9EbG_MohJW$zDp2E6U_Tw@BV4|QEI<<2Z;&h zy9uX;tY@j1mebK6?RyD5(lx&Js`?cF5ow|)*LT{-#HJ9unx)<$nk>Er6Tok7F6{|2 z>+E9IoW!SZk%rk{x-h4+>j+5c;bDuXP*Y>$C%vjj82v^E++92V#8r$dV~#$Ee&9c= ze*rT^knf8ZrLRB{DOC091Xz9_t?*n4fFg$r$`@-YO6}7+S1swsj~!9J$^>n9tavFY zy&g~0b*#v7In3pK02B5AqAJN>9+)@`{OX?t!{^eWk+s7F5>H9>#$VqpFD! zHr`p2c9tp#>Fnma)TTC|S`&tVu+$9=NpA}3JJFxrTCi;R8NLp!eyV3Kg{Pm*t%CZv z6hCunP?~K(5`_l6nJq(s06=W-X%XGcu=s_P_pmwo>QGNW?a& zi&V1$r|05gc8hbXcy5_ah6oF+42t#DpEJ-f;0^c%=(?`~oyamY20|b^f1Rs4G_f*r z08>6;%(7)HkL}fKbnV|h%hrtl2)mop#A7$d{pM{Y<@*^qj2>dICs}zrmBqVUi#sY`G5&kdT?(!^8ZS#Uu@##uR%}q$p~8RbNBB z@GXw7JfWe1RIt&tN^nI0us~$Yf+sHija*MxM?`kSU7LP2eiVG`6Hmhan+(!UZ17RP za<748FY~xPa$=4H)q^O}>88wY9IUF;L}>?pO%A2iJJ6 z;Tqr)a&lW>r!BJe1n?kF_0K^n?%@d0j52VO>l^&`?JY9SS9Ei+PkCyh-eY~k03P4)r8~;`Hec6qo*29=MFQ|C9FGWX8D>-rWB!(TMR}E7vAs>H%z9ep z6^c9K-HP|ajTOv4tWoDv2e1r{4v+wi+-pW+e-!Wb<}sGRpO2HRN7yvB)&FxH&O5J! z_3ukg9}&8+oc;S5c{HY>&I=ac8SZ#^N$uKW-<0oaH64FWPX18`^3d>tcHHILU0!3x z;FL&(YRWYV44FVbRR?7(Hp_!GYkx977iG)PGOk7Ww2lcYDk}Sq}ZG)BnS~+g+NUfGn@&Z*L`#@Oh@KS`Nlws*(e|7S> zoAxb6MhTmmrz?40!-)O!VlVEvl{EVw<+?ZrH6q8y)kGZW_qnW(PwT+?a!=;(#=xLs z9FpS4hr619b=kc5(*PBs8ih0G-%j$lI?+L?*VJS~)m(R4#e?;~RXbP1X+_ZvcB`TX zgRix6(7b&#Mr4xB>57 z9DUy!VaP7&zr-gVB!yb^nbxRVr8S>rNhVVx-&`fBDE}%#J9!#5O#Y42Rm%Uy3_$%M zw~*ts?my}cl!?LOwPQW)it)A#=6GIJ9585ai1h!q*zw=^9xvoHBXE&&av%R%9${EHMD5EV@3@ zT{37DyWjww3dNgV;!yx~K;!0Xpy4L896bAErZulKNz4F>YO(XaHiH8$;dt69KK9(V zk&t{8Us17BqwEFM=&M=#(o&OLGGCr1!x2O}?YdtXh|YQY>&p`X4$DC-=rRTGg8jwL z#Lyd>^}!PVz&Pt8;EYR2opSSEyu_2AAA+9mu3aP*Jfy2Wa=-wb5K2~HR?`E}$B^+V z{!|Cjj2nuC3GV~n-K z14x*m+nV9ov%9xpW)405{mp7_lZBq3t2eg2Qpm5%qM>xc<=TA9%xtKfZ0BTw#)^tz(T{(TCdZ=$8Za*mgokNrN1k9 z&q~BNgS$SDuLLLE@}bnl?ZU#q!U)kzd3)#1 zrxy=@UQM|mnXmB9<3b`MH zaqF|h4Rg}C*-jVGHK518UE_RQOEgkL!K^<nhV72@;`SZ6?ky4A`cV^ZWFCdQdf0BEM2Jgf8kF6RK&m)d(Bj09&;k z7K4h4D{_8Z?^R?6TS4ID5JadeECftil8`H^j+FOWkQ@I9-CF3HE_)cnX{Msh#>O^S zbs$`Bcpe4EEh-vgm-ZXZRc`}GX3fWI*B3@zgDI(!zq;(%RUN)KxW4rof1x|gPjKHP zF`6TTP6`?h1+}eLeAtc)E@}v1nQo7hX42y5`p9NYH()g1g~#c3C}Y3SqaVqvt2j}2 z?6i;}?gY&r9fEWV4B71Geu*cz{LzFrpR4-i`~WQ;bQZ3`K0PBnBbY~#5d($lmtcCx zbT);~j2*6S=YgxF>xJ)$5tt~Hq;ro>k52PpdMf-!Y}kzrG8#kcBhTMxHuUe7NIEgw zJI}tCUoS4OpZ}xdmF;>p@|zmjyky-D>>fu>!{!64nq#a*5#%$YWN!F1Jhi)U`t!g3 z3f5bZE;oX+tQu}oJpQ;LGCDOFwM0CdYt<80T|3oJ*s+?QUX&`C!bEkwCN`?=lbHmkNLQxq*_)+Q9(Zk6DLsYQp^RWUitqZ8(sEX}8_KqEpGp+dU{~}%x zMK#|U_C}$i$fhFD3bq)+W@S^W?`honuuz(0TUSv;ev7OqL+BQaflA@nJ**jx6_@(g zC;R)uWRwgBSHi|NbFz=mdD9nn)t`^oDu&~=+`;wR95?b{Y+>X^qEMkpFf0L% zDTOe&mne^e(_4q~_?;YqoG%FU|Cn$uC*^bCr*jyn{wZAZuroFSM(}M&k|Vd|-QCsp zI6mLNKrY^M$jkgaBtt2ws0glIGqA%(p_E$V+^!pTe4w}GG-?&ryn7oStr$Z2!1Tp% z>!0INUzfei7Q;)yKdyhOe3g}9+>z+(@9*or^8I~SXbRZofSdP3U%!@bZpr%kGr}?n zdd)1rv>K`6w#dA4Gqgp_+=1)yanWWAOHD(e#nvnDr_xwrWQ7s=RJS zUq<;G=D#B%yL~aY4~0?(&d;RTy3L=LC0JAJA-@fG#LnEu$DK)A_*`ddJm*~x2XQv8CxzE^9vl%QRx(nOv8^t7s$YUbBRc9yoAE_I9LIktjM zM-fZX7sm~6)7+@ zN$d$xj=vuzbm-b!DVN?b7P8eoz;=btK|qh6lHe|Q!@aUIIW0@KA|5yBqT#uA9pkba zR9RA4oDY`+#<&XuGjd#~;x{(2Rpz?_ip+XH+qnuA5Epi0ptx6I@X;k~?4E%_5D=sibDGU12MMzCUV`OuTyuj-3ly}v;!ToIH|IKu?_0%A;lSD( zuAS>l=6q6jHraq0pOiG4rFyq+jtHK?3}FMk8I;u~u^g5%s*|l^qEhmFe3YR@6g#hY z=FwIkjcSgTUg+=D1bsr573r`kqllfRaLCf)?nYc86W9-M+n@i^OYy#Df8nv~MjLL9 z&R%|9U7%S@1kKN=ZgmjH_ARhk50_@xFXB@)L3(^c+6Q?iPl0NXP&~`=7@uRVY-HcL zTb9@9l~|8!`rf(2{MN^I94O&4I(cyE!D>qE5$Z_~6)i^(MGtET?`q_n!!e^7q_w)s zzLKX?ChYl6M+E|f%}$AAyQBT9uA7~Am~=`TGq3@}&fv0)Hd!9bBe;1pJc)l|!rf%@ z`|X9nEwT|9rKP$`GE8!GzVx!TYn-}1SvKGl9@TqBQMvjjVyjj6g`vNmh`!5uj&cW| zZTq{vRqpNF2K2s|N7hpd9Mof+9mHkyiLqeXxb~>PkEY(l7i=hc5Thz7-{b9Z@JSG? zfQ@{Qu7~p{SSu(=_Z|shwD<@XDx-e*vyE3_LRpThS3BYrJIK?(D>AaPKRS;~+8au< z_||(_9ZE`(20z04yuQ@)Fne38$Sk~*ja>yqHa*alJ%Ef)&A20;lNT2*mD=^> zXs7(ev~MR{b~HHRq~zp)K;&y@$j{H$Dz!-j<|`mDw0UKuf&d@?DPnX`^}5i^PdQCa zwZdW9kUh#jFw88MfAl;mstr1$^i;cA@9n9o#PKfMZ_cf9m2gI#;du5_hv2S;Hq9fB zp2mP7YN-;dqhzp6P1b~DWK7np?C-aWipP>Gt6J_*PQC_*d!6(!g4{VfN#-*O>nw-l z*Ab{OQAt-IgU(*}JM*_!1X6&VZ|N(kWKgq6w5}eAx^wW=bVD^oG8p1#R?WPQ!DH>> z!%vq8qFXxhlA^hmv+o&;P)h}n)TCgZ56pDh;$n|FW5|w`E*WwwYql`5Z}fml3c1%% zl#>4V+h8=8W%bC>xW@2o8vmA*ydQhkoOyi~k(g5`VlJf3VY( zI)|jCsY%;@QLX#Wbax!@EG!pJI}?iDOjR*`*R8RK9Xv5j-QCJ$ykA2~N+R3itHyg? zO$df|pFA%X+PLGqbI;iMR&lZPsx#xTc3pLQ)V3^yM1^RKLgLkYg)ra+tQpek+?mVc)cQD&g>j7|Fxw; z`HV;5FNg(Qz>Sinm)VY~k5-li1bW_OC|h)=)xqpWbGO6gaN3NQ?jBizIRSq*KU#Gl z*LFK@P0PYOuC0{67=5NFd~9&Leb^XUTxGn$GS4#UbQ*+i{#&aj@h@hwKRpVM?*cYt zs-(zX=+W|EO0!m%lib{gYy-1^w6rv6+Op#wT6A19K2qmB1u3`;}o$a&mHU zi4f|M8h3p`K|%4RW*bp&T-EpZG@Fei)w3S(GStw)zpLTo!29M=R zM>1!?RuPww`fkC+$i;;|HM?FUJTFoX4-YwP#)XrU!9L5>xFz=#OkcZ%Bar_7+IdFq zU8ahn?dcYEh#uwI5`?4#oUl8K%%8}`(eWHu4i#%hvgr5fyDx_|;3owK3%rl7d9_h2 z7dM!Qb(z+@)}u2t<;xfCg(1h3nKozr`_|;$U8&tY6+1Q%sgu`?1(4jg|MMGgls9*)gEWAT4?kPG1g$Vne_!tw)oqB7tz2< zgc;nQVQTN(?;m94HWQ)su8(!6e=kD~cs1yY;BRHi>`YYHko zH<8%J-@kxoJ1jJx%(x@wUYfP<=w(!fa*dm;rpK7J(fp5=4deMd=24$5KTh0&_FH(2EOZ_LY(OZmQk z-#MFLt@`riyGIdsBbNGb`XWM|KPMl{h-|MIw{HtCSw@cTsFl*0yT-xrV?YKysH+qS#@Q-5_{vwbD%_+4^ZuW0n^gOEIUN(lGnZtuI%erJRF`USXD)~>3`Ad6 z2}Lq08fkPEd=Rtl&+l*Vq3HMe4oW#$&*Ltc+LESem*8p@&X~j88|hHBoWYdsW$os^ z`x1xGW1~cQ;YPOEb4_VzU+#(jZAC%H#ieH4zSQ;QiEGbiSx40jB{}un!))Vt)7O18!RLazw4=Ng!;R?`TOdPB#J8n{17QS3CKaX9E##AGNB+wJ9{ zG+6ue-2MBHi>^XT(CMiu1BhE91)Lq2w7yDiE%xu&FdH=C(=K9Gkc=)YCZil6h5qC9{Ql%pqBmzkT z9pU6_<+OIQqkV6oQq#_)5MKhul$OZ6Oq(a}Swlg@oWgEy)n;)1&6`Yf=t*PNlO9;V zp)r)FTQQ?n!lYaN4mA8cP} zmhgJh*X@!F;sl%@gfoO6og8e*Z;Ji-J>x&*K2&^?As1(6Hd3BXZMxL&2uP00NSUp? zn_K%4(T}LgVi>t0i9>=`D>TBPmNICp%;B+(;Ir4Gb~xO1%v8z977@+Mkd7?jgy-Rq zA8I98%Frqwo{W~P`YiN1%Z2vj3|-bL-xSSiisLZ-I9R-#n5&y0U$X|^n8U+iJXM(< z-DCrl4F;_(m*&%C|3HlTBbqTCoB-Sq+EBiTXF)~JKS2giF~tl$9Q*1#&94(X$%4x_ z>s^q_YAU|JZM)D@W*D^0?W~7toFAzu{45ujR^^OmI#GxB)oLW-VMkMd813(giNw4b zy%Z=T&v88nG}+(uB<8XRby(7%f?obHvswj)RRDG?hW6y@+9E)M`w3a}oYveOc`z2! z@ML@%Uo*&)!|z+K^i*Tnu+f(p^DcBy8|JA`Sm06fRE2)6E~yjHTwJ^kQH^N(-W}t= z&Zt>uWkk@x?kwuZ!=STxubLU5Cyr5zxm~bIvV=&`SxF|Ro2>X_#+W+ii}1-In?x%x zVnki$yrP3~M1u-SqOBL*%*#%s#SGR<$D6p6j)$=$&gAVqm!~PpbY3`V<<0u88zrig zImiO&q=`^p8sH({t7g-Q4V!<+Phi*a3A%ej zIyzg{H=8EaR%EDr2T3HV4%FbwoeC;+J0b-@f>E>Vam+H~4OC>oi|I$wGbkI;XD#Q<+GD2?^NK`l zmkF6O9)CL{w``uTK`n6j+FC|N=5T6a0xx#3!{Ku%ugy3kymra{QXvb6%kFtzx5J8E zip$Q<&JEV)TrrUGQu&FQ4RcHKIb`R*8ZKpV<6G>EeSX0Ab9FeMY*wss9bhZ4+^MoK zduVOEZ;~QUuPUiM_Tu}KiB}2=_TWH}s7@}H+jQflYF0$wHTj;-Y)$EVw**iqVI?+2 zM}nJIjUc{duDz!%CwA}0UFtof(6LEgzKC%2lxkP~4}Em*kyeFcp7Q#nCvpedV#_#= z_udJ8`ny|ryk{THzrr9&la8EKDYYTPA@0$JweVxGM8vZzQz7xI)8Mq1bR8w!X9lQ2;tlseWnHQaYaf>Y zz7w^uS!at!$gUvCIB$CR74OwBNO7ackPTsf#X~|d>^ANZBcMz-A-`wLDFe9jy zL}GhD227_K_x68>b#OQ=VavvHf1lH6P6~CdI&s92Ik07ii1z_}T7@5>4R#0HWB|p@ zhQN3`(ZQ(2vZ5m4!e<*YXe5xE+}>DYrQ>Z(KA^oGDI|cV6`P5SuoX$OBkO(##o}_v z8vFL97m2-#EQe(D^ahmUa6*yTjnpqN5Dh#_Z>lt5axyhOIXN>01x2y}kriPPP-SGPTboQZS~95Tsw6c*B$`O5XOaV?I&w{RCvK%yqm?;;VBxyI<8nK^1(~ym zr?*6ku^oH|08d?qBu0r>@0s;54L`p~e0;pq5}S~2wTmoxSb4ijowCEt*#tuB0g3~u zm43Mw?U!r0;Y04vCg^iIZ4Sw;78-YSI2|lM-=7K+BDlIPJm!9q+@5fv4M?-)N}*{t zS2!c#9X}fCW9zt<;ze^#m z6iL_+vlv2kz~>@J6I*pd`k#r4_i3V1^fyuQIZaeNPZO0RMI=#Kfh7)xob5DG$@!b8 zc$_9G=Kw!MN#3E^c+I;{YGt0QAd%3~ovil!9gj#z{Qi!+d>pSpvKNlI`S#LttzwI} zPzP;gch^vnKJH|oGq#nb@)wv03LtAYdaInzXEoVyc-v`Z7tUiFuiR@da1eTwRU{R% zmC_sgD?_u1Bhg}je+&4xbigs%KIY$1jap-AQrAIw9c&L-+ixwXLh>;HG0!`1Z^-$Y z=7_w0gfm8dmW}z0b@|3z6NNj+X%esV%OFVLvvGo}T1y1eJp>98_RZF-(tj90;&yWP1?{OCakt zrgNr7zKDbEbW5ZJ!V+|)CNR1m6GO`V;a=KNUubM!WN?kU8v+%b!->A-elj>%u@kJ8 zEjb$Gsub1-kUq#~g@NEwjbaZVtG@pw=f!jcJX2=l6-n^+JbELPGlz@*Rb2i_cG* zRBV=))VU5UEZ)`LPK2;^F(XY+akNr(Yt)&-6x`dwufzc54my5^;jtX;6Af3F*>=|- z`rC<=q>RJ;}7mw3zIe zfUTo~dlgy~TQF`0lwSs$cdkdNM@d-glc0#2F*_E~3qAFOruTavJz5!Ij|?7=T3P2R z7z-w(+2>4Nuzg3#q;Y^<^`!njxNp1wlASu8dQNcauK7;#SFM=qn(~ik-W5BnhQw+& z^`NVR_Q&5&MDyBL=jxQjcSU-g{U?2~jpXUTvvlR^R`>^of*TqEIMCj5prB#yju%8w zadHWQImr@1KU-Q_PH}q8z#oQP(AOO?|H;W3;6J#EEC!TnPL3;n!VDPL6)031*cpi+ z7@%y_9t*p`=_VQ7`xnzc>AATzKHhiUn7_;FxDzC+%}r_9y|eKy1-T9@I$409na{R!BB{ck=`3(= z)}HSU@8IF#N#VIyqDxjKVW7O)(Sf6KwM(!4RAbx2rv0OxWd?O-1&BGi08Ft5a!)l& z?ZIlziA~paiJJ(lBI3D_4g(s&!8xyY(9s0}ECOk8=EZC078?i?hD;cq%`|{kiXQGGwW^ETM)tP=)#U;o0@az$v6LJFZ|EuCM)ly@h<+tX%)Mk8X;^Z z)#|i5tI-fB`12)X`E-K43d!g~gV4&v6SeiHSR5{r680~v6RGDP7=9Q{s8n05*-fak z8dl6u^5pm(N7z%XE07i#_3M}-Ez2&WPOYPFWxkw2gTH@a(L^Ed&7a@v%Bj8>#DCiB zJECs*iFNP`sj*c5lGLr>kPa$gQT}nX>nAQD`P$q`120ve@ihun{H$tS6lJ5k!Y3Fx zxLA<2rIjrug-63FKw`+;zOA3VrTk~MBRNx5+Z;j}n5O1SZMr!(&kMi-m-VRC&PN-9 zh5+2)Gkta&KigHvUXNZ5UY`rO6= zmD@_Tc#EECUphDYasFe^SUm=oJPUCNjNwb1Hz!MtPZ*A>^JHYtp=@xs5*d>OAF2s6 zd2?^};U`C6dbBpWx_873uF{pWn=u>G_9$s8JFHUvX;bZ(BciWeEl1rlw;kJW?ww7% zw;(vTu))nnp{&#@{h{|c8CThaFekV^l+?oqDrbr=dHBx;Zv2v(=dn7A(p1hAI>|Fu zm8xw(mOX%p;h*Ibtn5BeL+&0<<;{02eA}`uUO*cl=AN&n-5f7|OP;3awGABBqdH=K zH+n&X%=vus;ptiKWgw1J0OgE#Ug3ca_6(wU5)yL+LiWIRoNXrq_at|;zok6il^Otc z7mjscma;QdTS&+-C9gr^vo1t@JRLdKlc}oi>2+-3n1JMq8#5FDGGCVJdV`hK`{_Z> z!y$x@U{HO;e^D4k7QGU0D6EtaDjg9J}dR5kB!=*XzIEgLvr&sJT zZDu=my53E9^>folNH2B}-YAy~wL5(1!)q6x9@a$o-P(P5-%?<}C0(l|JM|HPpZT{* z>*?vZ0I9o>R{`OOTXnQ8Ye+;b$U4b7_1Y?lFh+;CfM|phpdfLq@c`7LRe&B@lP`%Mxsmzeo4g| z6n5}L4-yEtJ%tFB@OaD%hvYgLSq`Dz%Wrdcr2ZX*FzQv7iU$yuSyM*CogUH2$A?od1IA;&@D=!iSE||lB4aLv z_l`gQe|U!aoi6lZ$@VBl``jt+FA%$F1@Zaj?$qA(D+=Ey+!7P}R6Yso6!13t10@RY7ha+0r1UUlcjuUS6Er0cERssJmj@#>JzY-IK%j5<7L zSQ~!Hgtx4^&MyTuIT6%i;$n+M_l+_&GOT`1jVY>{Z-r6YFR*K}vE?%;q!j+yb?m8f zzB>OW9wRCW+(W!63AOd=60iHQSyI!`*UTJJp33X^_?d;XB3oNCM_T1;WUAQ;Iy|=f zb5X?)h`Dyfpf=)Lh5_%-kTsX>-yM)W!f|_Dre@*wC!}qdHXQC+$Aj_DN|pfRgP=#n!#q(`{8*II})K3Sf{^yno#_fP!CWLxuU^&w^9lG`!gwfAC?%62%=9jjc(>c@uRDJi^&wiXzeiUNau}ILR|- zbO?*C!*BGkscjvqseWubUH@;E&DKcwq7)dvTx8MW(-1dO=oi$?d{4Gz)HE=+qBY;( zhJRO5Uu~#m=+`KQB4iotieDgbq>%y{8bw<|&Ciu%^q-;VpdX{L(vsveG$LvdT^&0= za#Yi)P`5TFiScSjJ_g{TYOBK;)iZ&q0DGDTIW)9VjtmM4>SWWY`}E+gN}>qX)_k`F zNKZ~fF&nnL>ix{);{)sE{`~sWBwVh{VL7_`qqn0 zeijcRUk(*81o?<&2qo6D{CCuQ+NEol$;nbLj$BYW693JrWErGFN)qAi?VXg=4iXl{ z_Sovs6qx6-@cRb^2V}wEXYTsZJWNbHubM1QBOA|8*U5(D$;xRCCg0xtAZC61mD+Tu zc%&TSuj7651R%3L-z|kuFAtZgahj{4!5VM1u#DXSX#LIGu`X|lV~I#+G6g`OK#Sv$ zG)31+DFrwyudPv}7tUfbmt}btFkisH8UrF|3rLCflP4uTNca}BQ*AZ+zX*E^uqxMX zYk1jP5D*k;P(+aylr9yN2I&R?3F+=qNofRWk(3VUR*;hJZt3o3ee==xea|`H`Ty(V zy7t~~z_k{v`<^kzm~-%?rigt_w_BeocjH%sNYI-(@DLIz2HXNUI}?HEOc= zvCWfRPopugs#2k!kLI@XJleS``Aq@=0^N+~7Kt8+WT;nsfa33~UYkb$6$)qg-#xnf z_Zy}Tu_^cxKm-FL=U(M@>tE{q2JUeOQ9(ztu-#EY3!VQx;OzI7y_QK>oI|47(la1u z8T9(JJFW&qeF%^^irdevAx$!r7d0H%mo0QUmIHQJEVqhT_C|wk-@4FzxJK+ zlk2Hhm8xhZEdO3!j%!~h4?YBo;HAvAC^R?RF2NeC0Fo?@Uy>vf5v5d5-S?E zp_uT_w=t1~H7)H%&1Xe%@vJc&HUl1Eh-0)tZbGw6qs@ka9`jbeA>7c)miwp_5~WMu z<;r~ZgNMci6BSWcdL8v0-@@&@d*O_+88vIwCuo9)R}$1}$WsB6S#D#mprc<&gR=sM zAoky}#Sw`udyc%t?QfX)S(uDY!#E1`0ucINK34;)=uOYhU}sbn!FS%SyXVw!bY7x@ zSwSI{1u0Q-{n#60FMLIX&P zXaGdI7rJ_izz-NNmeAWhXPE>jo{O3qh-|m{1a5lRBs0JssUe+sjzJWao4q~z2TP7 zO&*I-EAi@g>or`eaEY~ zjh1>H5QqhVRhIirM0QR2VL(R6ua2J`KQ|pY^XEIu> zwF9w5d{!c*=k_ApQEckB+#B08eQ6q^Z7~WFR9F<<(gpmdqITeP=NmbhGsEh};~s!|4({GwU2?~7p2 zE5CG$El<1pQ6W?7{RhLYEXNb7Pbit3r!!BtHXdf$=bt}%JxB8E;|SB4g$Vd@sgzHW zooX^p$Wd5zwByKEt4YTkC;xUnoiO-1hUU(ZBs(1aqqQ@qpjlm|`a1B<<(NE%H;f-$ z34x~t0c=8J>lAW-4!H)iI_;Bx8!E^jSkAX;2e4|WHK?^kh5ueVaNfV_>I7hvPxkfe zuy6VupMET&9GTp?6QMXj0sfwj8h8qo0V>!cHrvysRWnwv6Q$gJjR2*K^1VVY$vPyk48vwKvD$wg}V__8Qdtk2}is^Y|UAyzdy;`E6L{GwEzF&Lcb}|v(4Z6 z5MwphWkZi=sb_j$$hfUaE=ot+8Y{bYgAMJj*` zSFq*ad&2nLT>o`%!6H}U{bj7~m7xMCAsGMM+rc6bV(=cX6m0F5 z40sva?COJQOx=#pWB(Fi$`l)a0b9GAwJ~mk^>G12rGRmZ{*{gD$L6r#`K1(r>rkA7 ze`7XSM5Nnq)()53cJNev0cfddS1m>f62wj3%@`%Jk4eJRQ#0kqFg+d)Yp9h-bNTlwD=# z{;1sALDCa*pbI3-K!1j0xzNcy$i~1I%@hg>1dgbz*yK1KbSPRjBUf-JK?iu>({rrb z2bba}e>J$2R2`f0f88F8F-AoRrGI~>QnM$AF$NKIUFn<|b$ zTe&(hw^qvD%E`;|@u9L87t&uDSvK=>^UJ}L9U0H=M<4tk=G~RUlFzyM#cKYU&7q}I zqKJXp=y43KJUt^0PQe(I;F}AFm9de%4Ryw!np;_K)A2I~T6Mc^8dgPSz`GPT(AW ztx5ZUkKnAPbXK@m2d89h0 z>=YM`X)e(#V@?o)yYmyO_7>w^Y0G);Z^h)#M~AOl)Yef9j&}5yWW+znRr`i5 z1*+jZli6S4jfa7sso- zjRy0YX*H|EU|{u5!H}`d$iK)TkQ_EsbpWZ<1cJ06-np-!#dx#Sd$_e_Y08oVOMw@& zOB@%<=2g=5+KS6<8N|VrU9?c*^oX@Dsqs4KNhJ}H{zq!J3}RMV&fWJGs<0>P4;#^y zShO^HNQCG!?39)V+zv*e&XR*{3fx!ST0Uu&tJ6B!s~}LbogK~(yk14fD3u!5OsI<$ zo;<;Y0r01B{OFRCQxmD-{J&gO8YqH)@)tYppW9vGlv}SJVUl+UJ`|7Y6)?kz{>nU~ zm?6Gc8@=Bg!@|vst2Q>QyF2$?YWEaJNG{jZp)ImVTAnt-Uvq)0tJvn(Vz%9!VSNV0 zPOTNz@0sm&v)k{M_?%7aBAj}QCE^Z59@O2XoLyYJ10GlEWsYJ6bI#!glogOJ)1}tg z3`bG}qYDXBm)XyxLuwCqA~mr+V`~y+-g<`qMg?0AeRD|D-~TjRbX+>uK9Ny!RH7Ku zU(`3VyLe>q%ZOHmD&BU5D-|a-r^g>hQz~NnoCWzPS}X*207M|S{We-Z$#zNGfyk3! zGG>`scfV=I9zg$48qTUXkI){Y`~d-|JU$`%F4)l3ZPiiaAHOjuo%>UPzeyUWUZbNy zLsE6VFq}eJ7Jar3+BW^(JS`K@6^P1b7WYHTAk!8d9uVk7?*)KpS?AribiRS*nNB4g z5CGi$5+dGm3{?U35_})h;Qo{Bi;A`T&F)!1&RMXG?3abgA{A|hB9@%BwSR-SQueMLHo729>OxAj2iaXHwTt--=rmC{nDelRQpNoWntI0b;$R za+}OI-N{livHB+mQ6QcU-dpESJUw>n(vO#jXr`*zzRnpsZ1a$`t*SRm`NHWue-Bu5 zgOnZw$#1rPb4GJm_{bjfNB4~r6Srsmuj{#1GU3|8RCAIDklEnE12#`@Z#qydy=XM2 z3F6(04MRyxXmhfNP8b0Rh6*p-?djxf!1N?~t(!ps`-J_>c2?k}}B*;@?Dk5U2WScbiVO06Um7IE)hSy?3~z?X3!RQn##p9@lu}lN}9^5E3MjtcojZ* zfyN+j;m}X?$1tX#di!H@^7%_=QZAZiB>Zn)quGbk9hU8OJNWl^2jb(K2L`z7*RkA_ zU!bX#7~;=@G+fj3`QrLCZ`{xA+Gdh?sWiwBm8)osuhX;$+9YFQ0r6BOlF5bUxgLTaFesib5G z$(feD0{H_4`4K(ioY}K(*8ZeXenqao5VhyY0fkDXea8P_;{?3f=$!V9UkElulnt%Lvq0 z#4NveZ&G{(Q4p>Ak%)tn(|bJs79{Sa6VG)k%aXi$xTwa4oUJEyOdcV#p%Pn{PE9rsU_ z=9$#`7AJ1en32~!7Qb@4VCu6kM|GLugNl-h)AqYIGv3XseS|b+F2qM%?iE%Oo18~n zs8>?RVAglmvC3~f3)fMhzMJGfH{0Z;hN}Ds$?hss-VIzBUwuR~9#4KXnA5^cfOLNu zz3syGJ=~oCDd~Pi{Duq5?aZmqo*Zk3E{rt1$6`2?NssLpp7o`xzsSAFF1EV~D6gE- zLiuCi%inR=%zS1(pJwvgtnX=oOtI-*sT(Esu9i%%_mhT64G#|G{F=+CCa!Nz^ATn_ z4KU8~cIViYl9d3JtnnYFtJ!w@mt)dh5H$R5-^JY~JJ&=SX~&p78120c=)L1&iYvy| zvNWRsY&y6$my<7`>|(toL)%Hnp2#$`7PUIJqgY-bgcti)X*|@MChdF!U`&^~Q~X-> z22ML3^YEzs(R!PUKs4?KEd%SDZmK`;@^{Zd;E54d_!E<+>7O8*>54wJBja_Hg9FFL z^+7o&Wkol@m zD$soMHe0!!Y<@-fSjy+`cEECcI*8qI#~RLN&lK|?Qs_xTH0wMa7THN6U}R-0??w(W z?PMTj0=1$fo#8Bn$QjN*=gPEwUsAZZ9FNEQxyGEjFN6I_yxXSE2_N#_IF4cjJ|l2`w-= z{DXRJ(`)(UC}Hh9B!5&CVC3)^h=#L3xD8Wj;Jne z8E1g^_072~FDgXU`~>irRT*Jn9~dk(w!T^MUq}=nM9c`IS?7&2AjVXcjDb%}NOl!| z0LJf7{Qmgy14-ckSTlTVer-(!#j~J7jv5f|80nzJfq2vOnuX@y^8t&r^z`B%sipdZ z?+74|iv*uwQ;-S}kv$Dv{{7@=V{b3C9*_&`r)v(BgF zjbqjY6-O$FO%|I%vJq~xPUrl?_IkevNcUA#LHjF*p@0f~cNau{)!ic#5vFM}p3`t} zCmWDHFhvZ%yWb3FY1g%&H%%)=Fv#rqEYET0bI$#MOje`bWjXHS%tS@pZ&#BY-#H8433dkuY05~FCu9SjgTvENwGnmsQHMXb4pN;wCvQ_(K zQa;odz1l3<+GPhDi5IYVQjo+#KH)`~)` z5x&19@JBla11>Joc6TxXUqQWA0ENsJOabQHEwUnI7Q%DSqMzb2c008Z0ikIG)ge?( z^?t|Qt7`5HS#VN?F&-bdkSv=gx4IQ%RRJd?r+>#57LB^-wpeEoXx7VN9EU1t>Gc zL83eUF(M+u>l%?Zgs3O?t#_BOip(ZdfyD&W(FtbvD%gq_GXAXvXfu&WMM*@lC|<(s zSAcS`$YO>~q#Gnfuw`&*a|GrGT1G0w6J4@b_aFQXUtd7ZgN%(dl4m{u_1C#`Sa>Gv zE=QCAqY44WAz_^bdU4Re?;*3DAMjNdV5I;4FFw$-BQufg6Qm~WW@^|XN7H_0u$%P> zJa0e9=1i*Do4EczcsA!dMCKQsf4CV2QkvgE6b(>Ypw?!GcSkVR*q%SXP?4dWiOaRQ z?2tK9YppJLCXV3$Y{jJ7qw;M-@{BY9wB1VWAX zOpyrzI9j?b3Zjr~*2b$Men$o#2^Swp$!r}z-YYKI9nPw{?8Hl5TFvM3j@4|u&bMxV z9sL}pG^Ng+drrDW00RP`_mf}l4}dw2Y@}B@+**{3vx0$oVy?mt(*ZP zNDiRZEI?%B{9aWK95ek|MxzJ!$T-a9`Z7yGns34m*(c9GT&y=M0e}Ix`6_4UUkGLT z0bf&8be!k&)d6Q1FMN%WS9TW5TKtWZ%lpxKX2A%j3PH#4vcnE924;n>odh3;chStG zXl&K^*MZU2H>?Aemh*0s?GEP@Yh(MOf)G739+(<;&d0muOisRnNP!P}W|3bZcfEgz zs6_yq31Z=7;4T(>r1<|1cB+BJ4!%6>#$r${ZvDfk`mRjWXu=?)!hCyNK;ZPvf+2cj z%p8&9SX-SOSirtzDUh(qwa0QIa-1d)1atbt5dYF(;VJNvN+6R!64cz@|7LwSZ9e~A zDKQx_MJN)t(;_Kkkn7nVmE+l5+X$D9q4zEN$gFc0UWkR()|#fnMgZ`TbQ!C35EqM( zlwBXMvq8JspSZG{j`lWx-y7XC500>R0VGh}SpVKOJux&&sF`oq3+oe7mf1 z+(m*`dV4*p@8@WEPTrb&j7gi9&^Tu68^v*#2?E*Jn=f!zq#K^{Cg^*pgP30S(cWX# z>3<>89T+EMGy6*-j^iX9inYasj(FkF{wzWSFU`?V%g*bx+1lNeoVUYyBKqHm%0C-G zo?BG8pwMgY&QlVf!xw~&$@tQFTI|Jw{#}oIU-?y085%2T)93395E}Ig(hugC%t$j4l)W&NP^+dJ&#OTc@#goVBg(JddnAk4N?AB>OV(Z@Qk1pxf^< z@Lm9zt(Jd){EG@JwVb8x%LC`-PrK%)4FX_Tkv^u^VmqU?{u{@M+AYj(G39Y!<#EsN znILEBOg&xetr)LGSx2yT*BFp}@K7l!ZdbSft&C!a$VuU`}BU>g43(13y3*pkO%6wLbm;a(lH!*B=c_Jeq*y_I{& zr?%?rHrfVRecU!xbgw;|rlPF-`PV6l9X7WKKbTWT_QRCHcEl+5KThPym?rZV+J612 zt#bN`OUOa$>Uv>vcJ{4)2jk>S5V!W)Xdl=<2|W2uNUyv__L(ohBR7}*XomoT6y^NLeKn1^h9-@1J8E*^9;su z<6qTU z)=OI@Z=0^0w;T58(Ac5`lNVrI!X9v0$GsIA+XseKP?4ct>FC_Gr1Hv!<+I>-asK>; z3sdum6g0#4uZuj9``PL7OvQS29q4JL%dOw!*CAF7=TWd9=>m+#Y;11ga^HNKER){u zsf%od(s}55V*l8n6LosLPr!W>tg@x0rP12^Yj*4p?Us6pb8^D3-`FMvFFMfyD2g7h ztQdEt%NiXVInIohi5ZNQr#GBFI94k$B@53{Ezn~ft#lyhNxhVy&aHWHSQR9bV!18_ z+Toj7+1Zox`+UhOQLPC*iHEaDX@zK?vH@`{Yv z$#30~k+aEmr%(*^aN(?ojlbY7NcrOv*35>6^3=z-X~TN9x5AHTs;i#yMr1wNoxRJigjFYkMTK#Vyoev0L`BZ7e-!B>;c zv0OzrAkWb57ddR3!Y1rXs4w9AN64&A{F%pB{s5gy^Z96(-yMPph;rXCsDAqXBSB3| z>KxqfMWRM!U;p4RR=o2ln%f~C$UHRiUOW^0mG|js!@KU{K(DMsVDXE z$2wLQltV2mUi?nFteCNN``FTUp)=>)9tDq|hGyZ9VlwJ{yLmg#coOzGStaH0CttD0 z?L#&$FD^#C+bX*{!zZxV&1Nvrj~PJHSL^9#H>Dw9#n|8+&}0wJ?K*XQw?jg7Tmn#$ z@UJq`5C@&{=M5hF)Off=jJ|TaJY)@Cus8ou`;QG$dABWJ=lAcpdBw%g-~cmQ%~@U} zJGzI0m7aT%kKgdY9KQ#Q#3qVG*rME(3mUXGr+4kQ?<+H{D`Mi*_<(uJ7pZtD4Eh^f zLHZg?(?tbN0};}DXJvTz=r1+ie@zkHZp0?BeAS8R^V|&0l7qSg)&!YQ!;^u-`0y@W zQ$3f~B2xU_C(AW=%TG*hv)qg=7%sfI!iyJ@_U`@rt^|QArKP1lWFJ&mqOQTcuk#T9 zNc2c=64)a`me>D)=QEBl6<)poa)~KJfY-h9@xDYY+sWD#JSexr?ZEcN>Z+V^mj_)g1>XMH?kI(5b+FR3g$Qx_}r-zFstV|yl z6}P)YYSM;NK6-S+lCv`>jtPe3(=988aGDHAn4~qBG^-mXf4uz%OFalsno9+G42`2@ zH|qa*;RwIwp(7(&6hU6(Vj!$Rks-)Co zf73lyga)=hsuZPpzdzQysWd-vC0{iDsvnQrmE&i3&8e)}GURN3QcyGMoHitJGRGfG zIFp_+;BgB`>h#CH^6+MOTVx}`r}^Ubq@08S5qa^&HCEcGh{>7A3Ug{MyS=9_QtUkT z*Zm?RFM2lHbO?@m=vQUrhsl()x@OoU)55X-%m`JZ&okg4UK`5Ul_y#8QQ;FXqE zLZ3cpZG6jZjI<}8xWTY7F7()*z@^iuv$L{djg(M8whsUbSSczBfJTVq=o& zI}SJiEZ0c&@3HV@g}h{=D|^G^_6ydt(bTD2Cc+%=c=4Jek}f1&3oIryA}@sGYt{Jq zZ0Sh87;4RSO<}|#=2;LBKQzURrLDXYq!K=*lH%MxpOs~KtR$0u5e|&Xgg(8un$F)m zTYC}=7ua2}0WMb*F>H3Dy132Aa=(&41oF8EgUnCtr)EgrW%`?U>5#nZN)Y4eDK}MR zJY?l?tDZE@p?TIa1!#~*LHcK_ZxUBdt?r2`VFXIVxTJCAJ{!2fB9xvn(@av43W@Te zvmGfBUP8^fpEg{Wwm%nUrrLUpDdf&{Y+vegT1b>(bj-}SLP6wU)ayQ&U_kW|@NhOZ zHcBDwgRHEqyf;1fJv=?91~lF9xna>U{H{=7JGaoCf>~@jMr->3sTEM}fc#7^_IzFF z%ZwOI_~(AOXh^s&@yB4Kf|~~g22(nd*@Q_Dg(SjAe4KT*x`vIRsXQ}tL z@klYLO!|^4hTDPGz41zs25^4c*q>0GTwcDw&8-^tK+E?Ab!|vkm_GpPH+MK~!1h$D zHS#TZN3wyC>OFW&7A=<;5B)QcE2Pzvs&0R*kZP-bXY>3MP(qo{wK~Ld;0Cgrg(5+! zXyo4Z#_qsGl45R9SBCrz*kMY790<@6{``-n6g_=3|8;69_2cOrSsxcg5Ju#(T; z_cH2FW@A*Z_MV|;y35IV$?wyOm*VLXC)2QkQc3z1uqpE}8Iigb+4Q|63hb4G8pcnj zge4?84$J88VSV~^E3cs7N5MQ(zG2%_&klK8i=P{JT$LWXUDv2_^*bPd?LoRLqIZs_ z9;r)S!u8D(yoz~^c;lR5pY@O7-I|0KHu?uPZ{EGF`!vA8CKEA#JFFPGQXEO}hbz6p~&Q67BOEQhfL`Jv*%6D1G zRL6BXQz6&2w71{N)tvb5Po#6n?LN=R`G&x_02uqcd+#28V^I7L&<;<+63JP8Y*LQP zI6=fvDqB@c#$zKJ)-lO&kT)528ERyOTn{N$`s2rG^&Qr0eq+9ZoEKWx;oq+zQLSHQ z4GszMjk0>jY@HY1;DXUSe~W3%FPVx+677IRz%>4R<^^kC!)+=_N*40JrdPdSc_rvs z|Hrtq6&Ln!%6J_j&y}9k;{0U^R_Fc`5(1W@285xZq0C18mInv+we9T+kYJ4ZGMGU) z=u5_pO?mI${vnOY+~!T<+-mKfdA`Rqt)wZ39U;Z*zmwh_tZ}eVJ#(8NsE0x_+DLf> z(#hGs)5*ek-F?ccks>34WEmRZGVj5k5Tk=FQ>~o6u`KdIqD&pL|d>s_!8^ zVF$YP?iTv}Y^M1A&wkv}LC=Ru(u-64{l%>hUSZ&A-e1&p^2(^xQNCyv=Yd{Bzq^5s z2|D_0`i@UAq1H*MM(=TT%qFJc0L8neZM(d+MzgS-%9Bvk`epozDLje|;4dQ*w3=zRJO|FGyYU zhLCyXuAHp{JNfQzlj*{sF6~O(Nl0w0OzB4f!NkZ-b(F%X{dEhA+>M@$+^$k-2IpZb zY{On6pF_WAdG=GUoKHe>8&(UUINg&xMa*v9lF!D(h$&%m=WB)@lV%5g1VoYe8FSU9WOE3Qx8z z{aSXs9{sh|N@hh`(mBzxNV+%PWq7!=jif%pgnT0cN3Swm-sp^!@I9n%sgD)serCmD z<8U42+*fl0d>OjRb$J7hZ-#crXC06evr+_f&Gc*cZH1UyKS*M}e8&PW!~$HHQk&$A zbmg4Ii_j*<$NdCzmSvGG@GPXbigBk)EsoD9E$Tq9RutX%Xk9INX}V9*_-NESn9w>d>G3)z z@Xb~I>%SlABU$r;iR;9&)1R!FwK{>Tj%K?~gxRiaqDEy|rp9P{U2sPd$_;54e=?q^ zR$qDf_-Hw|rW5L2Kt#SbH;b&8`#-a^WT~vI>`ars>*3+yLxv5ZE2KGCm}(Fr%E{?z zJ@#|D#l=PN{{9iktOJfakN*~)k3N0+bXi|tUp$5lYo;OK1~=)Ca_hx+u&M%5n}oLa zGYA~X>_HU#Uf$kUVBRed$lm-qT+jT#ec0eyU4ojnJ}VTQzFfwmqWR-Gu5U7ryNvdg zgp}ITvwi}x7UHI+QYM3{urO}O6ZGF)7MyAXGXOuRZAdNd)`gi;0i{7k4lc*L^v2ZC zSk`rQkwA0f6UcV~I!4rYh%!iL1Kp)(Zp|8wkdRrWeMnkfwrYvw_qwFZ#b*5O6s}6X zuq`}MtFAnfeSPc#d$hLwCM9k-Eo;3Gu1mi2;XY?ek#A#nHy$w4KVms=Y}6B|?^+y* zKcT5SuFx_br#$RaPEIl4w-9!S(C~2}ye1~*N9nUnrayoZoESS#mx+gg?k_oMlp$@r zFNc0AwlQ@jplhTidoUr5>bEogJ|}D8w!(M{!Sv^q-ot|E_PBC1#qi1qRK%~7=Ov97 zH1w}yug3dwk;_lQACVfjSqJlWh%gFOw2bp(>hzIx&)B6ko6C3ZY+}*VC!3o;c&{g6 zsI#&_zG3?EuD}SgfnvUXJMo+l4b#82og^ge8XHeYqdsa{5^;=O(SPYk+0M<(fqs{I|F^zoV zd+^4@_~QfOhJ%D0C8&t|{Oz1`@@gWovyG-^Ta2LDqV2Wy=vVhxlgc8#9+gwcdc)~> zzcWcl|752}EsS3Ie0utuNIsXV@7@dGQpCH%xbUl^9WzTq!st))*%N8Q`o$|;Sg0b_ zhzxmk^Qqc|*!;)TUmO0x90}i6#>>bUJhyNg+qa|E`sno;b7dlNJbf#N&fYvGw5m$rAt*)P7R6e^(&>8FUAdu=z)n- zPPlo{Zg`CWubbl;gNKUnCsNC0s9|uqrkK~tc_!R{``)zCM||@abw=1 zqr_kY+exKAr}ER((LnKBR~w4vT*gJ0iu5;&kq_V8g_#b|>T_fmRlE?%seW$pz7$OL zIJ~2)F-9mre&u0=Y(v-x-m+0GvihBc_g;2OU9mBDeqHCeb*aaCttr+z z^ks6cttsLa4C%j8L{${MOorw>1B*)U@r|fcjp#xEdQIxj$xr*sVPUI-Q;u}I;cC?U zLP3Yu4B%b~Zs?AkVyX5ZD!znu&zEd&FAul&VAC|_Gl7953mqmD)Ke|yi`_P)e9q5S z*_vNQu2)(K3eMFN&L>=O_kLci+ukNFIG5geiXM~Uef~_)b7x~p03?uiZ{L2h^75j? z@g4&Tpu88qh5fy=vl+k-*&UYNyqc(X(f0ncXr7WzI`JZGsr7`r_lz;Z?@xe<%D*cV zI0@uGtg!wH_55>aCF$tIRvsd?$&O*I$5nGn%Uam6SdaauT!ES+OIcMFAWkne@_?2I zH-Y*Oft(YWj%n4fsD;D%;C1^x3ERq(mQMlr{V?IQAG->O-Nr`R+l7TjLLQFm0Ei1Y zpXk!7bCHp-uid?xBI7^VgETv+bsgVtCAzsUQ^*2vQ7}loh}!5=ys)-6~+QqyMw?Yg^dyo9E4=?Iczyi8{Ybz;7B}#8s`Fjy*yaowEC>qY(%52!Vc`*|7Hk)%~q|Uy;8xm*a*$D|Omug!RAFe|o~#4Yn(T;5b*fu9vB>aonE#0nu3BeE<2>HTac|*uKV~bpn2WS&I3ODHclN`CQk+yoHFI( zAj1VK;ka#=7cVYnWgE>pGli6Dfuz>l*7TiWpUR!bJ4)>^`fzrIz2p<}IcVnSj0c+2 znW0=59O@Go$kCiS+$KP|2k{a60Ms7#U<%v!CzgmvvkyR}sS*`+{l(Zzeajw6LUk8c z1&B=c#Hc<^)m_LlRey+@T(8OfqWxT`v`oJLplooELk@~`M)lzzFy04c+Rxk?#v6ql zZ83H_-N{o1ql1`NaLy8Bc_57+oLKQIsj5y*xKUgkW+Dn&*GGTIqUY?-;`JX?n%&Rc zUn)RFMJcDfEMxy$dA=Lo_5MEIOd)T$BhcY~=;P-WUQk{2kze7=9!0Jp(3RKxfRxiw zqw`y3tonuE=xA)z0rwU*mu(cre8#yvyDg!n&o!tgI@kEKx!-YNc_olGmyU~yF zMWt_?R(e)EbQqqCUHw^K^KA7<;P%Iqi~!!P6Ef=qa}2eN-RIkEGv+71bx}`L94~tX zs$EswKSLh$eM6F*hM#GC>(7*y{m$YUs=~#GxG3|LgL?g}zW2s}? zo!)KoKnF~_bpNLjR%x`Rx+Zt{b(en!$wwG{rA@8<%nnieBEuLaPHw`+4*A=fTC-Q( zVG?O-RZJ>{WI;~*@flSODD#D;34>1in>>|X+Pl#Lw7C{+@{LTtPtJcp^UJLsuT@c# z1vth2TMK|Z4w}629O@S1c)-)TPe%R>xilZgPvgNFT&^rICFRm+hQiO@@1GBva%o&u z>;z|QD{SAqbF@)sHj;ekJ? z^UbY{(rqc0mKc|oSfBONEfHT25u}6xv5^{Yr~SlyyLIW6@v6%j8svZt)X&Esg9k%C z7-EGJM-X_GN4G%p_0Z)A5A4Hl8dAI_wob1qT<1~Na^C98d|f&~6jtC0Ue9nAEUOh+ z&#tCX=*zLEW@LT(Vw@#C68%0n->hG_S5wi7Y(T+_6|XqO7`k+@Qe_lqPv6=TDq6L= zx;Qi2s3vl{%QA45&C(h^_~{#BrA5i20jb#h^Qv^w+K0X?y$#8<9lgEYa<=qK({0uF zhe>Z7cc+F#FjGIuw*46^{%C-978h+zu;(o<)QtDZaY*l(v5!VEq$#m)jifWu&iEAP~mt0o&x$)JbI7EFR~jP?DZ0y_JTS43lB!Q zsfUY}eVdvxL3kuk=cV3jDuu5;XDGu?5tcENJlzKclf8q(tF7Oc76#T;v(&k9AUY!W z&&5feB*EA1*EYw#oK%SmHaz(o5FxU4Whyd-!M?}80WmKx?@4p$y?X5W1$r>8zIE#s zP)hFx6X4g28`1<@t}ik%F&U4Q%OicgPm_3+!}etmPd{BBuY$tg=)c3s$piotg3dbqb;oEF`Jag@F?(Xi)TtVRtKY4`Yi9!zX{8cn@EJLwr8BGq~Tf4tqMtu>v!PJ|({uZ08`X`u#fpt+$NL0F`OujMfwhO19V4TbEhIB4wnntx{lUCD|v@SmE^q(Z(OU(8Eqt1v#Cyd~lE5q>oV zUoTFAcRN5uSg1S~eL_dquFJ4llvYQ|Im%=6ElEDT!2@zZlZNQj@=+#*sk*v%Lx7>y zV46Heyoj*#`|T;ZhuTVt;miw`*z)z+Sq%TYVRF!kBOa9f`X@-nmZZPsJy%KMR%LC9 z-{9OscBa`bP*pCEYvXda+_{rb?9;leQvmp)_%cJcJ^O8ngQEI;Q2fu#%7!61vYO!S zn22(h0c*P-JLy%-<_ngw7!@CQk2_u+PHPeCv}8WtE>D}AL$FE=l^Wgc@=}}mn$h)< z0B=Y&kcgzmQzB~1G3T*t442;Gve~$dyl+#@Jp^gLjd9#f(jN?miru>+$nVTJ zX^0dxi;A(R{c!*#Vi#yH^Dq{O{~vJ<3MVR<=YV?R;zj=-q4y@H7JGz&8cvdm&qvAC zpzknmf+LP3@K2Wrc^$oe5d!_&S#^BfdAkioMWyBmcbLzG?+|^ubk^>hm#1#fo5she zd4{E!Ln>dvt7{#XDbnZsaw+iR2d|97_o!EW8Oq=_%ntYQZT!S>;!h#L;Tec^f?l3A z5xz}?!u8nhKJKveauNow{`u$SnYOq9umK2XZcF$g-FWC5xYfmPz?Q)oXdnLFgDj7f zFxjq+UX2n@b@)Sx^I~FQJ$H2E;yc}u%@^p3mZR-E*b+sMV4Zx|Q%AKbM|{w!-$2N6 z_+T+d`10RP)po|17Yb$vy6+S=AJS$ao1U62uzx)~M}+`S_8I%|#5l%xHt*LT=z; z>ub<_-K99X9%~W8#sXeDFJ5+X2@0yg2>2={eGSssTxn%tS-XIur>8ex=)9k$!iqWD z!u@LKTY$mP)+2z`4gV*6(_xn}NR~s9%@Ayh?Ke%W^g8e?ziJxAhm_XVt{sDdb!?j0Yj3)1GO{zKp zV^k}(SM2g{#=$=fN&4)zNFOwl}-dnV^1Q zh2Q=bILD6I#{Qhl<&z^hE~_zv1zO$)12oqq<*Nm~alYQZfB#$;PJ9xO2%~|H{+!!( zlao_c!Ob$P4Z#ds&u%(bj&D#-XRksm%xXwB*E>8l#P zqSUv_Za4OBK+-^bIJU%Cr)Kf3OiI><*8Gb+2Hk)&7w$wk8anH>h4)vwI(p+L;CV(` zFPNwfc*n(Iou_t9*iz&hSZ#@t zlP4=v%h_-NPM_+h7m@%hq-<&FD{L8vfiYxMFAFN%xt6W1t@+jOg5boSi9#<!xDi(6!5Cg8_KX!gq6=ECngx4 z-9~|I=Q45~&QAD+#l#3`o*`EjC>=wY{0p}!Z*y=EuC1-X{Ypo84J1?~xrD@wFT%oq zzT=fg*T}eTiiiLk{THoRf=Chi@uS7Y1%1d zZEYPT7&nLxrbYi0`~`RWXO6n}#(ex8VTpj+w--omk_Sfoj$##JyfR$0-R9uKdEC-d zP#j#>>rf-8jgZOU(9P$3sB5RBq-3x=^e-ma!^`yh@_-0z?q&*KPEAeKI^MegH~kJY zmP*Qff@*5#fe3{l0Mh}1{{F|MPVCn}!`MTs!GcZa#)DE8C004;udl7I$k$pWOj({i zRVQ{h!`}0JF)6%Y{F84ua5DR2aKhX@L(8-?l0;LYjkR^pZxb`mwDZ2vhHRUq%Z18v zx7JjhOyH33+$lbDB-Q$gek+vy%RE~~!(;_+eYQ$(OH>}yab}oXX~E_<~%pE!y;8Ho|3N>*zX*@`wK8TPnF-g&NX*?mnpR&FMUlNd!(d8#Oofa*&p3 z^S4M=Eti&-KFp+(JvLR-T(vYYdgQ(lEry)vR0&zwYVmy8CHPHO%3f%^r{~QmhtIw; zY+mUZ*ohy?nV-uSd7M}*sg!ituEem0+HvG$bN34me%eBWc0Nt^goctS{>3hgHZDAO zBI&B?&a&5#n+#2oFMQd2V~D)Yp;&0Fd`VlzHul-S%K^UJpVfti4Pnp`$#x75<05{j zlrjx3jdZ=7(Ra2v_G+*=gDwE&p=}Jk5BLj)SUl>D6^lx&<_{^md0f+)5-G+U0e^=w zVVmsDQv8GCV>1(zaOC~Z*kJTO8By4CaH+9+*0WcbV5)IXe*0-Umn5z0e^Y}T?HcLy zXU%GR|NZ?6J{Lq9mpMkzk#O6s34;2}-_U#o(3`w`T)sOu@e5%=K_&Dg6E6F4|LR(N zXce-2pwZC*X5%l-pm(2-kGwI6Lg;VTLXGEZij#y~M51U=(>$RK*FM@={QIqff-hd} zE)5;P24U%ZZ5{DAVkj>^3D7dwiE{@{{788=CZ*#y#! z1XA$7_r{6;-Yjz1@)ZSoARWLm6CQ@tE%w}j>c}o(=1vbDdYY9jhs@6}VvW+ig$gcJ zvUL@UZOZl24I71Udb>;TzliKh3@si1w*(-VP_EgD{#W(B z>B|d5L{X8P)Pw?8j^FU)=t=OJFYnzo9&yEEQ8s*g`oDz%m+FVIL`>AmopBnaG+tO* zV%%aF!pc4B3zzgyXS}T5k&sck9nYlOn=Kn@MEzUM&}!U6#?Cc{H9f$}ZK7eZCg%=~ zh?nQn3VGvMG6@7m!5G{3SFTBX`hIDUVvDRnLM`YkGt)&A_8MqAHfM(GM5&!xo0@*s z33V{Lbuj|uL|*50Kr{3%QAF0Q;VZ8SqHnm>M&Uzt=c~nRyxTbxOvTiB`bl#xe`82b zHBog0%~^9ZO`3YnbD79Z=vk_!If`W$Mp>v5b$O*vsh|mjuINt z{d5_`*KtuifB+^WG)_)U1wtNA5DLQSESMiCbv?yKq#@NX>d+e1!fY};Kpe6B%%JTO z%Jo2t=M&@DKT-54dEOE)#3P2qTvNnGzIdcuLKKMbLX?VG3^kj6@;Wf zkdd77$S~Y)Z#f$>kDr-s%=t{&(fPBwtV}f|L?n9;Yq)wwWr_?rzs!J&e*y$MK^j$jFESM*5X={V2m%5ojT_wb@UmFW zy`i}(?0l=4$#qlgcP{%?_UA!wHII%k1XO;_JUcb*=gEA8i=WPac5ne^0vEL8;FZQSD&q+#3g8em(Lbi%0C{h3Nnz+7c00G10pV&OGmT;C@n3mA|Tye5{i=29R?yT-5}lFt#l*ZpddBG z&^5%nPrdH@x&Kdmdgp`ugvoP`V;_63z4lskHrCm$pr5R>`t$L@fyI^9&=U7RIXhr? zUiolV;%S!Z@=tUG_udc|!{J_H!z-ULfCJ*@?{acVYPYGxi9BC*lndN7gKj&)e`iK) zRzQA>z`v0}L77LVuX$uTAL0StH{6D@7h*F{)=YNpNvzZBKD&4mXg{CWrYra|^-uVm+z(wZ+p&2__ zf)5OH(i&r}ouSDVQK8v`lJjMKvy~T;Rt^+XSIHFVNPW8}vjwLKby+pF{)CoUUPT2g zpZO+F=d-c;1M;)q608YMOYWZh;GXkXNGN%r38Pqax7_8o7l$eS{c{GxCOlCYXTKIq zvW}G>lKb8YI|Ee@LC7fg-kf8!N9j8O_v6M6HNS_Hx%WY%586JRq@QIHT%G|!y??r} zsrtwHCvzW+MJkH%?H``3Ivjm+*B>8E^k*@Betuk-wydXC!A^9|<7Q%y!RprXJJTA{ z#Qd1}?3Jnc*E#u64kT}Rrk{N_OJDJ7>Q^@zD3Ub5!1oE1S_uOz7!H`4#69PJcJ~OI zq+IaoGT-r-)Q|Pb(at0Lz4fA=Sa-MR-t<}7s&ZQR3to=aeS#LwJJVmqmIa4xSKpUu zA&AfG9`lR0NEHTU*JU|6Q%%HW6zK1`L=7lXTBeV_X4#H*x|Qkwn|aX8*y=yA2?I$= zd2I_Ei8Usr)|c6bYF7Uhfc}AIy9=|^{{|pyx~Eh9+jf-ONeV2*&^HZ#Q%Ld0MK;kh zZlLwz-jTqp*^Z=l379rnpE(!f8ZT>+dKd2Zb zlmcc5nk(l}&_s7v$atmw&GiCOOR%UTAp#Qplh=SxafAE(U5f#r-j3C{kVASAp~QY$ zE?N9LPy~I!1_1cC-HSjuB;&Dlh55s8BO|JRbUDL zGqV)@7&axb#zf%&tNTnyXvVJFc^Lx9^^Av!Nkp^CAq9r0c#O%rV1)~L0#`eLz0IUo zkHh1*dCSJs6!p=hHUrNNnp%i^M>rzyu!?PP|7qMsocHhwdUKE#~}4A zQBt8JgDC3JJw9BxBzJqRH954qCzE(U%N!UB>T}%mGt2>6@#2DUp6Ez^UMg*cHBCiDvR~`{PEZ1^^Nb?7I~I_^_Cy|LghM_%DjpJrn;u4JN;Y_{JK+*$-Q6{o+N{uR6uoFwtq-fj-8Q`}LKyN@l+`{;~ino?;Sq z*N&cPK+bK^pe}mcHzP}#MGO1@3;(;l8dC-1_QRJgsHe3(U7Ey;qzwO1mW0c~$kXcf zd_OnKznv(HzdgY1(HULg%~*5G2O53?>SL6TxJGU`TtvThmc7eFJk4XdABmTmO03Et zTst4JfyUb3tW);n9Pd)VUw+zqSl*EJrE7301kFNpw?`7VLQe$*u3ouv+xmej3|vE5 zewF)TU@pl2@i^_{Tg-J5{DsV}c@z~PLoFUKCij^X2R2K%$MF#s@7J$ie^2Up@&9c3 zTNb+rpH)<3ld0IoB6teqBWDJzEH{M`gvg&Tgw8EBL7NrGmf;0C2U^P` zTXe}3RIst*jr@+hK6t5-^Ei|E#T*$oTy}4!TOMZe2~-J)`4IgvE_Y6>xriX&Z>rj)@vZU4YvwDS z=;N;r(N9XPur*OGHIvlR+G9Czc$GR}L^{o($q2JwXQ0VA&;R-YgH}~eE6DTqHpj?9B8+Y$^ z=L*HUKyYv6|MWUl*Kwa{5kLQu0Pj(ya@)f(LkfID;>?%xxQ4{f^q|IEAh$?T*ZD2# zwtNcRlf(R}`l`k4I&k!4I;V3vk+J>V`G@WL4i}lI;-|hvi64gr&P=iJ$>R@x+>6@{ zBWLFCyD{r7uiQS}9K{sc%MbiITgLdc7oEXv(xYh%_3dS-Sq;~P;`_52K|W>p;Xo4D zZ&IfTEgQ>t-5f?S!`#o8MUyE;Kq_Y}*6xR(Xu^Pxwudnhn%lA7B3*`W@8bD6B1#M0 zrLw3vpF0b*F_qI7+=gGt44AkzyHqt~b9jBf()6~TuL(WkmgeK7>`999=R~kz0|OfS zZPx0`CE`>ce0+R{w8Ks-DJiu_a^OumW9j+!eqZ0+-TnM`0L$YmWRXZ(2-B0U07L)7 zX(@%WbsEOQ1z->qnrHg|PxCCWuTTE;*|Sri-Tn*;S9qtNKc^}uCO%tuNqW}_&L10F z`+>&kCZ_qZGDd3*-+NbAGMq)nt^U(vRan&b@3hi2&c^liJWVU(HNU>S8y;a8z2*; zGu`NR7b(A60kfPhY`;>fNbR?qQW3<^tF$DgtNE?h;n};`oxOZsMF!IBP$&Fza-6>L zvO^nn8)Cylgx+fH2`};lt~3OuF#c32NgvY2r`D}1o_%Y)PLsVD^d;=$j}Q85B0Gel zFh5KGL}Eu28Ri(Z`4nfkL%cg(QkJ@NJb0JK=mOeSo_e)RaO13Y-#puS?&>SRil?w` zqK@LrJCE>V5?c=m&i%uUVJD^18TqXVtVF=+?CVJa7p|)6wq)=91}DisOcBrm{vJ%r zk!NRd9%(K>mz32cuvt3K7ifr|I^`R=C1xIZ;uzjbWHiDRYzu1I{rJ#*TA#k|QErHg zB@o~8LbUD0lO}a(nq=f*kfJ)59zSbpUf^@L630xn4EyZk#&?|3f2b?Bp4&JU3-cOd z70^08!PPmZu%2ipMhd4F+z(9}BRInH<4dU;?V}~iq;f89)cv8;?A>AVW8rU~E@aat z{1+GCxz-S&X9)EUzc;Bxw)*UVq}K))6NZ_-y+ZuD1@%Uqy62{waop>0bd~2PZ%$bT zxs_g;y7Is5nSK3Q@HLFGI zk3h^RG@ZP;vm!`aUtly$C7<4h&%n@eg~VQbIWJxHc&2Uet>tE?3L^!@seyqae7>DJ zJlbDNW2a%GG^NSuuOY)1mJ8*pn@ir@Hb)&QHvdKBp{`Frt5b95$RFgJo-8hLk@_`f zgW>zOGA^8JgHeU%81QuP$HvDY#e07G^rm!q)^I_S_WmX%5Ir8OmwJdR{HqJOrfm(C zE>&<8DKuzhf3^)#C9(uhKcI2r<>#ph@-coA_Mc>&p=U{poBuz_IQuJ{xiu7+uTa~) z%_c#?VS{kcQLK%tz2y|Cy)TH+?6*P+Vh@^nmIK3aCcD&1aRXC~JBKCJ4`}l_ybWJ{icFngWRV`LimdgP5oJ9c)`vMxtZ~qe8Szq&r&J@jWw}>NnkTJL9)5y!$ zPoSE7wceX9n#v>z@mn4n5gs{R$$0CXF?j1KFXmTR$=lFYq=S<_r8qz+FJpbQp*dQ+ zBrwdi*X7QzETACnR>cl=;d#~a8^AL9wZMDQ#clFiB6Xy6nY~uwYi_cg6@G{R?#RLxi6(DDd}^^zw|qBLqO94rz^Gw zg$lNc{vQOdGsV=2mlM*f^Gzp@f6fkE>+F=swOzCKS)0pZdV^JAmf*Fw{1h5`j!H_? z)%mcx0h$BmR7w=gikS&luKLHZ1g}XVVY-&&!UFE-`-kOy&qr)xG9NbXpcR+HpX&3-B;K{&indX&p9D7_J)L=l96F*a1N#>3*`G7 z3$J|Y+9m2_;Y%OT$lG^Cn$nsP`{B@()~Jjy=d=nVCy|QPB%XCQ5*rR94`3J1V|?Y= zlql6cjl{)|OuzRd;wHSBqr5mn?`#GJQ)frvzL+=@rs$wE@iGGLtoP^6N&nngJ-rm8 zpyykEtL4gmVQVf!LvfJLo`30o#YlhULMQU5ylN?leutU=mvJLcR*}9ZHJCN(3oMpX zsJj$72>pI`~JLVjK-j3d4%Jq{c+j8=mJX z2j$)iuT`m7Ckt)Xl|IkCX~&Ij8qaUKzD}M{j;#&uwh|ZB=%g+5`CT*di9O^lVP4Z) zRGsO6MdIMFu+IpJRpqdQlzt#s6a=m-5Q#s@(=?{I&s4m@D} zBhnpj`#=)(oE$PdpA4lOlI0;i_W$fp^^8Ca&IKVjgrpC}<)2vo%BU}OVfW_;_Rl`N zeq|Q-Z{L35J)(|n@OlQ)hu2`7@jQi)cwM>G#+R^NJm?gA-w~jiURERC-#Na;Z9@j= z%~{k7XfMrAR9!bRw!pA}9gGx`nYbKBBaEH-`PGwHkazu`v-TOek~&kg&jh1uKV8Co zXuo;UrzvB3e>gus|1D;qHob#J(-P&WiSQK*5pUb>5Zc|47E=6%@pZ z`UVV^`8~m#Lqp1G-n?rsuQi@Csn7o!XM3fH*ig~8Hf(JXh)7fNLkc_&A)gmRou586 zI-jk%D>IWvHo|}XQdRo&=9=K{#jaP}1qlbS|~BA|>HSchgQ#q;{l&VxrSM z=i6s(4TZ+9ZJhE1CHci)J@YqWm9)3x45F7dnAmbtB>zO&GIqIB4kv9n2<;cfXuS85 zxesch-0iJ0?xOdFEcd=Lh0sl=6q5o2_L^>ERzvH{Ix{}UGi3Z8N_G_My z*zARs_X~0kx<7x+AT%V0?bz=i>A>0;qU1GeCDYMyVYE0FztFjD*2-+L)1_>(&uE>J!WTKxLfl z@9P38BY9*#R)^&`HDBo#yb6b9z zAhcz+w{BW$K9kxC;m&0wP5?LR(V9tTFLb)PE6eaCqdqt9RfAT80e>w25y)kvMa9ZD zb8kMs6{Q<=F*l)TUuL1e%ejER&p0}b5BWKE_BG7KvSyvPY+_cWYJAH^3nWO zO{ojjZxsrO1Fr>a;Jl&O%PEQTN%}0f#1vI|u}mYZwe++666$k%|H^p9onTSWbL0Sq zj>o_2vLK+>KrXca?FPd5`1pFHlKTAl*^eA8{!05T9AGbk`USGVwLu&XjKr_0mYC36 zKX3zb_n9i?Ft~5?5Ag`r3*fd@4?21E$b><=x_n;^5obQ5k+E2uT*|;(tE`V^1cF;^FN=Zr)gC5!x4%aWEI`ISBF@0w4w>-fnmAUS<7M z!y;}bh5vK&Bd3+InJ9OQvlk!ix+)j%ZJc7#@1z0Bkl+<4BaoZF9m$53&5&~Fv-+~g z^HHm3&f;k?%v-?7kaWqfho$8^byF`Lu3GJQ)UN+ekp^fcVld64tC>m!N3kWJVCFtc zhY0i?Ac!W&oinWjw&UiexKGndtxoWeWczmXLH|x@Tb^-~$i*YDnazvEV%bf`96wt} zpZq01BZiQ@b#$&lHC@Z@XHd)FP`xVGNMF@`o_qvUV5xT2}Z(HVVET+@FTvzT!?2oX*GWOv{=iXCE zqAI`YI(=TKwUQiSt)HdID$b8}pTmofO{@7dhe5iBBgNTx+}t-$OCCH`QO02#aqo2O zn#{dfB&F-e56qv&@A)STn)Einqv-f&ZlYc zw>SZnZ5A&R26kqT@0~f>r^Rty4g}#8Ov21Te^4FZA@Rc>1wa}4r#HsQ;OPWyiU;Io z!-evfNsVoPbKD`FLFqc$e%i%fnLqLET4Hv9N;>V@~`qbA7@?|ovaedpwgnX_{w z)6S{dt{9lOYnbCSFhK4S)$4OpBwe*AVEm9}uB3$TjY&?C=aR0EjBR<;7)NVHUR0G? zXc3*S-cF2fmGLmKX?bnGejVxfVtzv~R=CsKPNPgHpEhSU(&s1j&Rd?n@&^xg1S`SC z3189Q-`{>d&RxFJ)+`}GPweT__Xh{-T`op@4XH8-n`=$vi@;(WfGiI*=U*bSmZ;xx zV33(6;C@-P+OdqxX;TIfuXH_0!^|uz#k2MiEa19j%PHgpmz}8jYg~>@+Yer(vsG6g z^AH)cM+OH3+$AR;^?RmDie4m#lN6X%}Lh-IxGS z-keX^0b}bNa6bH2h;erx?Dx3d&1FF)G}G#q{<7_@3SBqYQsp`C&4hLAldxxIaj(42 zw%Z7qd18-$R>Wft-^Hp1BwD}zpq{#U_xRefkFWowhTmH82?t_C(V%F!6H)OZ0mioa z%3s1fhE@2425YSWr(C(zfzUE%veiv%gSJm1>C$}4S}qQcy+R#LHi!s^tI1!QMsDu# z-VO|wy2%|F_gW^luqLu2`PhZ9kygwpyk=z*!{_5<7yeXiJXw{kHABbkO2sfep%=uA z42i`?HTs?98*+tv1rM?`3nZ>O%`IAKe&vn?DA^U=9l!9lTt94AF;nPw^|KblE8jMZ;DNu=bSVwh4|$s=FbbV7F6xojl#DbI zeP+6%d)r3G`2tEh7dkIpd)|IklXsS1<_}0J#ZFiE6Y4wrS#8^lZxWHu=1eXWc&*(T zlzrumcLLSS8^cvI+tPKHc~+-=hPvjyekgaN`n72W;q}oi`P@bgp4juKW~UggkbJ6= zSOq=rdZHFN=adeH*`(4CW?JQG3Y2S#nHBGkgI&I&sO*~triXGOU>{smY_zB8a%3lC zGco*Bz`Z)2UaeZr{c_eHp9!@nJ9jc3Ttn|#x!=F5Wi)+fmUwfb_`K+e=Vl9Rq?#t! zm6{?KHuR3GBb7ce4?8jNR4t7K<~Ic!)@!Pn*J^BZ2tC{<>!goy)W8qI-hBl=6>c)% z2@11p$uOHiOiDR=?b@~9b8`nL$=>>wl$0oDD#wBRm*GIBiV#d7pF6&qT-%?GBrkN>c>)H*L)u#3ZDE+9=hG#vG{r0f;%cob z`dU6e3P6O2Slt)Bv@|)a zU7^aFlSCmb^`=31Qbofy#j)(gC9)qXy(yc$S}q!c4Pq{w!3BZ^5hE@?KUVLpxB|DH z?XaWBK)S}Wzv0Hx%Vrbuk=E$48uPpNn{t(??hm@N%(VquOw&2cbP0GG12OW0WcA4xmw+sOG*d=s&|55Gc2a zP1}fI=9N|Bn@o6eic2l+PD1ze#9)G7p(1=fRwq%dEY$*ub1p{`0e#sL8q4E0JXv&u z0mj*yVxc-Y)O>t0Y=%Ky*3MF)(R;BUKRyE?`V8B($Dri>1X7*O-0^`*Nsnvqxa`)0 zI_#sv-SgOMddjV?AFkGsIboMCL&B7E;ri^O@uR3EulFy9a+Uh4uyqEb#o@DEv59ap zbyqvNd;21ODJWO5d=}vV6$q)0l~sky{O)baamMr)+AwN2G7~re#nuU;}^m{=09R!iDn@zeG>xr6Gyx+kS(?U_E@6_0&sNxWw zX*Rnft~U2QymI((EP&S=ueZNt_sg30iP8`v@{_196+BkX9lf!FdhHEiVmj@VOe-`G z2Cq*ufE4Ronl80c=|(tT<8yESd+?7RPGeF1?s?Rg(R?gl=_b#ilL4nwPE^8{6t}fe z0<|1rszfAJKqAbK7@+&yz>~~J4y)g3VCp0*jDh}&Hsl~UHjsmCby9LNcm;uLz#DLf8OYOZVzpUOM-<*~i_j>f zC$gJ3Dgg~yNk-;of2s^=$~Nxh%RRFl7{lRwKZ0vL%2aY;Cr=&?mnbvwTP>LT@g2Oa zim^xM(P3fmQWpN$KHF+AyndB;N1b0GGi>e7`i~!1!1F#t)ApB@+b8y3&@n00X^5h; zDysv{vHpy~f^?OF52C?1oz}cBTZ3e`&^GBV$4vIehr~D+E|?D~Jp{Mp9T1n5%Tgt^ z#4b~^YS$L1w1O{u@=A3@78tB2kD8i(rIH|%H#g6pu+=RbFVB!qOU>5c1~H8(b822> z38LKE5|v_uhK+hyK9I}5o?m8fyW}u?XWZ%d(HS?PRI>yF18IJ7pNLES30HH}oSvvH z^pvEDWc)(Tr zFU~REyGMD6c=Ky(sKpAON}6oRo3+|s_I3y7(p6ksP39`;Yy%tvR5>#a~DC}bP)02|i7Cf>v?3^2j%~G>j2$bR*j1v%M zd(*QKkQhK|I#_9Uy+5VvIVI(Ip%UqfM_Q94qT=lnclGT$Mk@@mL(ah(_)^7r&2R_o zlAKjPeCm`dV5h*pW#rIF!TTRkNOCfsZ#Y8B%<8gh*~MAru(lTr6x?BLuw4r5(8?_; zWt@sHApRiv%-5wC7f(a<_^$8BGsnW`+d9&!=fhH?uWIPUVrQ{lMyWlVtI(bB-Cm$FARFefpmWF%lGeW{G#W0^bpOzBS15I-y z%b=6gY-|cP6Lu-tS~b9?vTkW=YMKW%6ckw-X#7JR1PBHQ{zrm7q9N!{aJ~KM`Zq)c zL`0-}f}G$tSHxMmvfIpjDO^9MyE{R6Yusj%81dZt7T231m0`U+0xub)>9nPgFl}4- z-nIr0#ZzY`a>M)b3Dj9!fBE+LD-0r{JZZZbSg0|K@641hj$vpKyO9L@R{5p0d9Bd2 z*D~ZK{TCi(fX9tzm7@gMtwX{br0JYjQO?sxvy9c$e5H`CEXwHXDLp7WDjHyNZ@llO z97U1!xC_3+LPWX(ZH7!2e=-AJk$HOOruFedwf>CY0{cpLve$9U!S<>h+ESCJyM4m%ueY+ ze~QU;#jN!G`2-*P<)Sh!D|99aTY3V=1W^`@1#`B8zRAmA`cZX(PdsUy7Dsloy}>*a z6BFsON$)jXoD#`g4o=uuYMqd7F}sRF;gluTpYp7+uZ1F-=b{C%n|nf;_ox`Z$zvK6 z$~*K%SKx`&Ti)+*{NC^Jz^*zYrI~5-P2X{yLC$){eZ%#=`iG{lsXW5Rg zWy#lbj5-w$EOkCa-Qu=F2f=Di`dbSKLoEP)7f1HtX3je7kwtnf*GM_dpTUyn_~S9Z zt_+hCRxt8(xY;Z9w-{*NnRLkk;&dvQ*1@L2Ef~%O1`Bw~piqxyhV^3=7J8D{>^EN{ z=Q>i^S*-Ml640Y{EidBha2Hpn6?D^Xwhobz@EBF=FlvcmkIE*M40?9&vl-Pt)$lyT zV(WzH`ur;VUM~wzTTV4QkQ_c$;^j8a;~UQ(P2uc$uJkH&22aRoQnWi;SQ&(}H!F}-+tE&ZH<~?iE@fdh_DmKk>;b&n^ ziHT2PxztQ6L80A-%^R&6>RTa^|NRPDQXS z640yUkE9_KHl?{v8*1itIXElY6=ObrOh77NNMiQryf`5-LOp)+P5l#=3GrS**)lXf zUTGpa5AWr4%q}sxRQqZCd)|2CertD7sAef5ixXvjW}#T76>$G7b7Q*prGkjOjC`6x z_Z}4)c!9|z=-wuOp>Y?UiO9o2qdEJ)953bT_zfno!FXVVz(1oV6%sa+P9*>j-Me$= ziLEE&yXw;nf|IH z0$B!N<@E1munBJAi*QVKtC?0&YH4X9Wg$%6^EpXU$tj2M!^L4@y@4tnb@Ni{FW9kE zNZk+On&42ZIoPp@d|9*qsz$ZgL6RVyFNwiVqR;f$OJtM~l9w`E_H@M$?+p5kQi~m{ zo6?MZ(NQ(4a{ZUcGr#^gpOl!GF26?xf$YbyMJQtCUYHLB6^&e;{wJqNXMHaNj$4Ub zK4)J(dKK=;Yc<^{VTEC16Noky_v-Uvr0@8_!M2De{j}D{Xp`usCHe9&C!yI>Q>v}Fqp`^lg&W}B{Is>Mfs2e zPxmFYq0XEgPykV9*rdWyBaEztY2wKb;dlL~@OZK|<`_jlVbUaBzw0WEW@R?~$<3|E z{~qt|3oW>6%}ps0@T-P~2K}^+*5DfaTeo7itstRwbTUkIGG=yrv%e`wBbOWwP5>E~ zZdQwq0@LRP4z6JW^UH`gv+WI+H%4hqGmHmDaxLcgz{f%m{4f+sR(SUhHnKV`$#K4X z|KNa*wow!fKx*xtqz?pyv$p>EVPUvP(!Wvz!+{5m{qcenPP@O1QhduS7gT-7c$Fn0 z*p}VLc!CGKJ2OQSj@wpRt*;hdbMO zFa`Yih4*QjU#f3?XctL`)l9*X3?rY?IgWENGvhL;{d~R0Ymw|Mm*0zO0v;Y5J-%!X z+YU5p#bvF*hW@{lK|Q}ESJA)3Mxnx3&zRzQrr7D<#ZlSEE&Fk(pL=6$1Fc;Uo^_Tr zm8`K%%}jLD`dtlWW{f*y{%3y{f|T4Mpu0-`cLlwD11K(BxUjyl!NMikSC! z!Tf0sbhXwGt&l)57bZ@{yZ1X#we)Qq>_RGXa&pAZ*VU1Mnpy;kHN+7~&l10cDY656 zhQVG4h00w7%E10k&2d%}nS(eU&#G&;8EoVqt)Yw*UXwJ4l8Fbk#I?Axv+C)wa2<>c27i` zzQI&$_1pY3Dtdhs0)j(;RNZs;Jc7K4G_TMoIeUv<(=oyp}8{ z%B7*CR8Zsdlht_|K%+nt{0pn)TqOmFr$Ui|it+e%0gcjSg<)n&mZeDmA*wdf=CIgD zvI-=yH+qU4?82>nwWH}cd&s-|-N}v8BqD7VV>%h%pckbw#KXTD6_LXga}$HdZ*

7O30(8+0p&*#_8KNBwU*W<6wOXIYvn#Yyhw_eae4);0Cb! z_@R!@wrBuIae*Ir37ZxAX-CACXZ5~81q>$P)sAs1hw z2WvQ%F`Am1qLO@I2uqUce5i^ZTDxF{?vtcfV^B^X!AslYEG5Gb9RYc?@aG3r5&2O7CYdge^>W%-kCDU#nt$ZhHvGw*xo+jJO zs)|Bw@e*=@GZZ`IMvFUGU$&myLT_b4KJz7E%MV-u(!r4nobP2wcx=FZ!3;pg? zlfihoO<$sj(*XPXUggm9%^I8<$AMb{C%$bk4dgRc9O~-Tm`n2Bm}{(B%9+3TEz6gb zJNd&oy!1aQPRmuV2Ke;P_V&!C8A|}hpa;s$eW{qfo2He2?LmLX@j30oS?>kbN{rsV za315hA^@sVwz|x-XCJ#|#v1>l{bCJ~O#=Mp!2G)K`1@tJV5qkPEt8#TTCom@m0?j$ z!MoN_+yU59ow5uD7&ErZIms2=A8BuYlY-jdN0+;^oVxl`)o_0(23%46$o)Z5Fs=&g zTvX8!&J|0(#t1EJRyAV>=lX>8ot=JO(mE^Z?Ff?z@jNxY`R&J6Mw8Nu9ET6!a}i`z z%N?g++6MAYIE)m!Jc%QS{gK#0{^t9bKE9>Hi;*3HR;$545NjBiZp`q)D9p z?Xkw)#@|{IrZq@nh3e~*gtLlyANREn<-VH;VhE!naF8lg* z>PqSK<%ERa&G7i3hzJ^fe*T06d>jSn*8FY>WV2h>TL!l2AJ^$-ieMTA#96PE3R9&d zVX16?72ZzObOx3s&slPZGhae?!#_X>hom{-pPr{iP0Ux*>b)l^lFg~U+MiokN+ z`SodhJ-d^Gvrcm~(c3szIdOYgEt&qk!=e`Tk8cA_ZMuB!kQ-bYSb{Vql!Rb~_-B!mtoaH zp}e)2qXy8o0K*JKT{jIGXjZH~9|IpxlpARc-U&C{<5@G@L5M~%?~Vgc5V^(jm;MR9 zgKNhyi>PuC`)8-RK%**QDbvQXEo@s{weD6P z$cp>Ay2j3hk`Bw%s89ssV~f2;uN0Bz&%fM*h|~V_o?BC{b{&{W+Hb9lt6_IAp*cA@ z-Tv^nFXxc1()SPN79mW6<1#x^UN)ypO-~oSY$F5}!nXMbCXi+XM?@GP4q2$kPnT{c z=jJ}-#V!^6DLhfquHvyhN?p~fzEqXPvTW(02s%k$z`1y?M~yWj&4qub80mz78$=f8 z27Bt0LmHg(t75b)76bQ5xVfHUw_|@)JJq0pz3&*q0wM+BcVLF>6itTDggh&Nlr#_U&hPRRHlKand`*4V?(LhQfX z+*&f@31b94{9cypnLiJE&*11lgyi8*LGV&nC^A&)^(3&+V;|Kj<-Bu82Ji?PO&;mF zPK_Vl#9rVc>0h`^KpO&n@lIPgF5@WxKli!e=A}dLOf={`_<5zs7bI^nDZKbvZm%@IlSU43R+1AK+z-G{>A)zZ*eP1f$PPNE6LqqSBvAJWQ9v1xA34 zLTvhqH;rHS&SLB*92jMJ?RheF8Zx^?;(p>@+=TJ$!@(pcAo^z%8S?i+MnOQRsL6kj zD_nEzEEE@ahka+bbd455iUPOwLBXSSIFr$XriGA!MkMgS*W0RlYpnER)2h#Md8y6|+JLAmO$ZeIv?@lb@^Rho#2ul`uq5iZqY7~(PBw$z}{egakM z#>s?wForX-+YDjVD2t=71oOb@XipJ13F2}o)me=`i`390l-(ZW&z~Jo6&x*IR-A6! zVtv!2p3+)SeD&S{C2SA7Z+ZbGQm)<9sIaQN66F_Rl!1p<;iPNUa@SCoZ}HelyW^1$ z1W<}WUamWsqXqtwduG=k=7jyt1TK-V{zw!F+-$sQ`qzHc4g_GHf+?wzo?hao%cN*q z1OTQT$Wlv5OuPqeM_nkFX%W;!p0F1_cg!20?T%F16;xwaxc?YYBKLoM;u;i?w4r|1 z%Gm(07ux=MTg7v93JI|iwHXW1XO26ilPwoU`x5)#?h(?*+gZfOylt7fT*8^oQl;I+ zlURWvPpLU7&*qnO{+bpTJMcAbLytXj6QfVosfcA6T)<#5QXD1|+`QN`G$PA~)33jE zUt`?qtHP_yykA~4IJ)s^VJj_T>vhQ>!A)#vx*~N3Kr4$-=yGMIKnl(5SoLPyw#Ybo zYedqe^2?WNCWi2~UGJ}MQ&Tfl!9_)Vb$#%+rvvL`r930Jwr(-qe5N>EI?kKSF*o7z ztz8H00YNWCgXS-xdd%VYBqZ^3F^egl1Z|=LLcX_B3F*t{ktVS9at-X`F4>)bdNzoU zHJgES$nrleo7TaS)Cm`tPr#J9Cn6RiE?486U^OiiplPe^I(~pG#kNty$Rc(lNVA1} zqcXwne13oH^d9zxocfq{=>GLHTF~WrwY!hSC}lvCe5B_12Jim-?L&-+%NGV zeYciPq1GehczNPQlNVz(7&a(Ub8)GHveEn_PV=ey3ko@!8rbb+IZ$Qy@s&%ZQ!RWB zw4r{W1|aYbg3_RDPXmwxp4x)Xo)M8!D)DUf$#L0lrpCvIJ%>YPxVOwgAKJBsP>;eY z&VUvkbONh;s-3WZ(%vuM!G~L+#6$x&pd`8D>bq{U_2~D`0G4}OX!M*(5J0(pe|nFA zh3lhOz%?nFBlMAa8utU%r;tmkPt3kS0Pp5*vA#-`OT5Dc2~eKmU-=Kb8s6Sp*FAF7 z83m?8-kiY6-^a0S(DsAm4baC2xirLo4cw6{{oij7Q0CPW{wa`n4QiQ1@HzRz^2-4V zy8$#W?LJw}p}xLtj$QmgNl7Ms(0?654i2wrEQ)f{n}zG_SbfbiT7otD*zxhfh;ibx zu2zIzf|MB$+LWkJj!Ngia!69UsLLPuBkUl!+IOqWNj9xJ35B3Xdf z4P69}Ta`{ZD*bA%U1hnyBxvPT+WRM`gV%Z-H@B?O58jIL&ayMD#^KmMC;WfE82|eJ z^MG<|(z^iBzieY4%+@%^YC7=@Km=D|rtn&C-`g#Rn4p3pXKGls4m*eGtFv|ZfWnF8 z4SM^YdW0{VUD17YEZ)$sh0FQ?5Plw6p!IT1a4Ux9)R$G66pR|4{?Y*HIzB0B*2*|; zBi_=-kEcasp{0CLQg#>63&2F5&2_~N(&kkD(?SLfPOenovcWcR`_rG{E3{r#g^&n* zI5K7~&(Z&AA>T&qs|>-$3INpn)*xzKqzgUPI9ibo8eJPpwk)qI6V|3%-G0uITGxPeYJgn0EqK-iM%q#I;`V>XZ9keu4)5d?=-^e9hNQ~7Ahowa&ZYv2j z1rQ}5m2SJ$zr0|<8FRa)>poM?so(7jy>pqGivWSYt1CM(pDWgb(uU-~^3&2VT4;TfJrQGSlc87<>wD{vVi{vI<wUMQQBN zv43iF(Szm9=!d5i$d_!uMwB{nIMMemR`OLU?E}`kb+G_v$^bNzev|K!z;GJNK&sTS zNQ4ys2W1;2xfm3nxcLK8eHh^sw%?D!!+!owSx~TA0ZV$t_R(H3MerIk&xZe z)zt-gqg zxLZv+Nl{b1G7T{Y(qbCuO1;`U0Io*(( z^bmaGd((aeZ!GxXoZ)h^SIQ>EJ#%V<>v3lgqZXAE;q%T6B&hx$H`m&C@I`*xupQx^ z`BIbH_~1)LXH-?9y%j{Vx@j;#h2+ppRCC*AY37mf&}PC>e^OEZGExws`&-eQ;a>Z% zw-kAQysWG~GRj9E9oIwxk5GPDl^}nK3p6()wB& zwDHBw%+_F|!t&4wW}KAGsa#KfDiDeAGG4sU;9dRJ3r`_Cas)@gt1f3L2xdxU=JZfa zDFuL@GzM|J}aU3zgW3R*Tir%E~v9S7*^@=#)Or+6&&a;D?V0?+Y#hbJmzIsRMxKH}>}WgWzN! zpdAof_b$7oHhhb;F5KCwI4aBTw!?DEm~dX663%$k)s6ofltA9WZN}1kvQ%0ORp9Rs zD^?AaX%-ek2>&~sT!u?oS`pDZIl~6`ai|7}+)`|sgCDfp6R+j7VyKt5SVYP{5pMeOEe7-8BCE!7Cc zmD+|3gbj1;nu#)RQ&~58MS}0b3brXtJShhGoDqJ<6e8?71cq}h6Lyok3w{PemZ?b4 z+{5KpcwV-`5bM=LU|-rNRkE)Z06k3Zx;4|it$N~qT=IB+Nv$vUBqGZc56^bwsmAi^ z3LiU>1qR9)g{*CF%PJ@JkZ>eNk}Zv?o7NOGXjcI=E^f6b-&a4VWU!jM7s00c63NL_!Q==-*6Fg(KsDS$8fAujhxGM)z)sqSw$zBbwFBx%VO3Wc=X=BQ|!=Q z2)GCM(1$>L6HGNW8=RTb!Q<->;{jEc39cM~^#+G9YMFqQJ?e`eXcCH2iUvsoLIPC~ zEr8pya&l0+!NLJY_T>m6ACe?!7G^-oiGV}js1aJL1ExxViOGT0Zv8ocwzX(whmF}C z*!M}`d)*8N+GE$6d(bRygE2vF*lW0+9>cU=l&bKMzEU$@iRki&0;Gub_1g60;Kw?hyNHzqVML&M)7I+TJ&C4P^16lqCJ&Dzj(}|zMbIyj&60+$h95-G@^CFJg zbqLps7%dem7yxhEYO0x!OE|{>JyQEQlP!K{NnReooX5ZH01$Hdac0VA1uqngEwb!?^BeX|> z4TNBYMF#!w3X?CN^Zjds8<|-j%@GP)&}`4r>F6*ZRkuB+Y5_jILo}Dw0Gg?%>k#^S zpx6{E_?SV{TOpLe=uH(RT;e}mfK!_ommL|SC8>Pnc=$XuM>Wp6bLK`bEoO;2+Z8zF z7}&QxJO0`8K;Wm%wGe7w{MT#$?>{LxEB@^HilT{El7rm`*wXBrH9N6co7UlsE)s{u z&=ED8-U003QqlFjo4`o!4ZcW|ee_{2Rb(2C9@a0HjNu7QYDW_=wgRxXk)lKm3CF>q z6Q#&M{T@~maSl@M&1@arf%&00oBrG<1RdcecLYe4vK!xEI!iVOB0p?RiDAS7swejY zj}A8Sorl3U1n!+ug?mo~>E{bwN`F1d(OYylN?h(pDaq*l|55hVaZ#sh|G2xZx{JZl z6_r-$mXZbwkd~HK>28pA4N#DflP&+GS_ ze?S?AiO+rC*Y&PTCvAxTINq-%#_H>6D%IKtG5NcdZ?yb~oFVHiEi6_DVMY9y?J||^ z-d`$r8VD+$CP~m*OMAK3XNvHjn4jq4?g#bk(3so79z}rbc>5)4G_$eag$>uoNd<)D zy6b1?26d<<_f=oLk_*oW)w0*DXjutMHtONr6^$6f9#o$9Qj+je!M*q0Uhr#)W`~!2 zwYoflF&-yBbDeQavD{;v1TQ3sw(9=Igr|&`TWw~aId^q`lHTmM)~=w$L6Vch#-d|1$`$YFQde;o7+suh zkJi{+nN1Ys>0EcSw*66rJ$_sXO^Pv;O$c}w8X8(#w{!MUMu};M6+~F#Au^B7d-W#p zKy8OQs#a;RAP>Ae%isdqhcUMi+d~9+=RM2=-h^@?|L&Wspvs~!Lk}$ny?{+&y%(x_ zgfme}5+9ml1!c$*Z~;)wPtfaE%_g}aU=>2mI8EP59QGTne7L^N!z>39tnz419Tcoz zC>gLm+hei0i}!%}(*)JT{I}+gY7c%L;7JF*HZ|ZgW6W}VcB2uDoPuK}W?ES4pwb8v zFVwrgT@k~ml<>XQtW&?h8uyLj(p)lA5F@PkJD}(wbl-Nj{_tFcN_h1*x!$AXF5NI~ zAfeqR*jcBf9NW^e<*YT(F6Nw|2bqkaz6U!wR6@R&o3{OSmm>RoUcW|=R{|beTD|oC z4-#DU3XslR07VA7+DDeqzSM5s*y*L;AGnYmVRcb7+oeaO8PN8tSBa7)EtH$b6PiJT z3cWVJ>*AnHIKo^YmYbOM<-uYVs7}kP2V;AjMaoM=oKKn0%@KI^GS9e-*H1>_=nrFXaO$+y*8=Qd2=7v zmoU7&bCv|%nO_6BCH}P_@xgQjR8@g$>RkB2RMo7K&^%DDjrH;BgMxd026J8$r6waA znmn+eu9a&WUtOJTT)DSbQg395TTUmge4W6;OkB!(`u$xGY@y~Ag$uH2TXfo!t1$cM zHc%+ds%PU!Tg=VPOsto`tPp}_B}pLd-RnQ-JZ?3d;lH)KtGVrJ^~hNdddenIgSNM^ zWd|E;Yb82=W3O_SLnl&!zUpJiH|j(mz?f3ay6UL&J!J;blOokxcE#@FV1~&b;Ic*l zuJ8=rbUK_@N4-n6L`A1`@gr$iiJNXKB3aQ~R`Z>11T>9Nc(sIYKsjkP%yTO?wd=Z) z+JgsM0~lXICQE-EPQx|SVNWci`BB>{Ap~WgwZGd6+2s}N1 zrRTVIt^3DvV}jnL&$&w?8*mgvoOrn0=$7Yt(nTMyrkHgl*}6pP8}r2i%ku**rxkU% zd-4Kjcbaq%=r?STo-{!VZKlbk|2-99Y~ie%@6Sbpj3W|T!pxzY{FU@aF!MxMtO=t!%o#sj2(k@)4AtLIv?B~1BH0QH^O zpwPzcyaAJg)o#?Jhes?w%?PXqDB8~;WieW(pbR^FB6=~(-6dgjhx0-%ELP&dE>^2U zw6DdXGm6767Y@MTcMpp%aDKn>J4+{URL2&EZaluMkQZ+Mi8Ar$BV@Gbi(TzUktMXw zqCMls*y7CI`8jy%e9GqPtVMU~40({^P;O~o9)XBH5adSeS9s+h7`CTUJ7#`NnxP;X zy~WQC7p(gg>Hp{sOG>rrNt3kn6N*=Y4NS8v&y?`*Mz6>s`!}a{bvZAT*&1la4+K{X zh3M(nw0%!f#0_MZx4P}+wU=Nh~WR-UAAD` z8g=l_7b9G)#h1*YH@pAw6}p_i@-cV95{ot*N&`K{*_vt38z7b{ZpjmmMP&@2tgP3( zlEpY{CR>{F)brDtoV1HRS%c=N6j7P$5Hqv7^z&m{w!&g!bl?c-A960vqtS|6_|WO< zc%4}*ihbCSK}zhi9qROr<3I+K0c3>*oxwk^@W;HF*>9!%>cO)kV0Hc(bRSQi7TkGt z&J%_MmHkCJdVfg|l~fxlF~x#r9+*oi^iaYA&k4Z696mlOwpjhrNJdVM>r;_BL|OFt zAg$DAXWA`_drSl719TtKBzk@s@9w<1ktpubjxYZ=H;J1l41_Zke00YMlPo2tU1Zn? zT!~IQWGdcKRXsu2nbo7Xc(Lf!@3T3Mlb&R!HrDF70c1?KyNAy&2@x4V<=d!hV`7#-vf zA+*SZZw(CbYDSJ8ZrntQ1(&XPeb5g<*?0&VF^VLeC}DA?rzjU; zm~s8<(f;Gt8-@;U68E&nm1fx8i`FcI@>dG}+U)ZaSu2sv{@b_AhEkIr^l$}98>N3; z7n>Jdrd#;<0k1UaARBA@+bg+s=rbOqBp-0b@Pi+M}+!*5fXJw{^U$b#n({-1bi>&&98tVFg1^--LetaY8=p<@Y?Oz0W$y ztlZo;N6P5|7;eb3jKI59h^S|y#DDt9cYSTEx12HzlUB)6|zRTvE zyfo~ifzWrz|6ez@^ZsPo1F44@D{0{ZbvM?{vzUShDA~ z)n^4wJu^U4DEsWVaHo{#=&tAHn*l3$4p}Pm`I^?{^Iy6eEL>f1 zbp$~Ln65O@G9CdwOM#0;mE`YJp*YDm?T98jd6Hi=P|u*j*TC0z!0+Xil^6d0R*g@O z!HCvkWVFB`)PDIQ0t}0}Ep`Kpeh8n7kAk=vMmv#vczA$O4+jj>UzVBkP72YSs^G(e zSS%aD6>}i7!LPwo<~Uh{FeHJ2!#Og+_(4uFr>Prd7Cq8{6u`>kx2gFz@55;B+XTd76yx4+!HnXVm{0?iQ``+Blx-sKx>DFoyYE3s5i~bL)KRL3>`HzFu%0b1Q6o zT=Vo0A~O6hw@XbXWaugkRxolAJI$8%18XeVu zA6H6M__d3tw^pQ!wbI0~W<)-!8uKu*d>w`rTj8g&1uf>p1+DpSH8wWeF3;Lf+b5hpzujD-s#|G+X4hOmfhrfZ^IhspQPigGDk#7=YT+-{m?_bx zTxpugw_G?|y;Ob#Oq4S&UVP43$$Z;Gdg$ZU-<^Z{O9?Me;g+Jiw&wy80|ckbB=a=U zPd~l(*55zTkO_Y?8{f%Upl0dn@h-E_K%sSY|N4^Ay;yk(#EBs4v`KC|B&-TDkoI>H z09Y?|k&BV?RfJXszA|JBxG4(BsdX=bVyddh|BO*eMtmC{i+(m>I~Y44Qy5f$YxY+X z!!o4R0kxw(+oi)C1Y#EJ;3JH3uU@?(JAE1pT(5xlcZc-JO6>FBelG>t9Id4H;2i7S zY&czrK!>@^bzg~sERRD{#^cl994KwBfUkn*BOFSybkJC1kh{FUEiEmbD;;!UxW>IG zPQdb!OM*VX@uyQ<_ut*<>FGJtKD^KX2QM^i>Aw3!ONgJBX<@rY*0(Qtq*V{cWN=ON zmm`-r@2K&nLv78rmhr7<+1;Zd%xTPZc{>Vr`1ThXh0fYZd=y^nM4TzRZa<6m@L;;w_HxyYOum$Xq zc9xBuk+=bw0*PcDacBqJ$U(nT1zRPV{5(a6k+Ay zVYz%XXQ3Odj3rzlmZsUA_tVo`lo1hinKSMzM(>?mz9h}0OLC?9&HAKh&V4mr-eOM= zo6u9!f#3aG8}95T$ROInBbQ@%Nk>ys*s~gDUDx9iNcvj@R4Rw|nKoR7MfukDR$<=9 zkH+w#J5|q^hejoM_s+O^**cc;kQD;mv%ZmU={l0=UfvCm5vWjbnF=xdHn3;vy?Fjh z?!%_tvWHENfO)$SF5tUovot{px^90kV3)XhK8ofd^tJ!__T`D=LO0+)0L-B56dIPp z_!roQ@;lUBmPGH6OSn{cZu^3l`eJ|S{B7U}Vlom?l+9F1I+;d*m(E?6Sij3sx4&gV z!S*d3d@Z(P)w+ck6Q=Dr-iE^z)ciw$SBKiVVm1}e`|ZV_Ad`6qPg^3mWBmg zmxi%(tfZFl#1?7I6+arkt|!0!%>Cu(!pfC6F*{ScD4p_G7oukj-O2>|1o$gHbGb9b zKqAMJ=;#@gKykiBrD-&q&R4S3H0F=3*Bhkv8YyD`xcSGk@1Bx$8;^y(V>TowM8Yxd zGtN$l&5z#X=jZqEIs`QbUCh5<{D_LrF!$6_aZlrXXq+H> zD>^d3tX}9wlRuB@Bejw=Wx!KYnC<5kTM59@Pl&Wch99zlf|qANOxx)@2{&-gKGy>;YbT?eM83 z0~zzK<&G$ilO4pV^3vhkvLQBo<8=Y&&y@(OC&w6i-Uek&KO_%QPUxL~uw13-L?>gNnT$)>i!fi8BXT zlq-a;HJa@YqSO8Uu9KQITGHvpqvf@=+zAPvht~)$9?|QqnAUJc=fbXEZ-k#ee;&+; zqB-6>;6`h61zg+7)fJd@JJqy1zoB%{GJ54UJ?qmQf7)1jA)_6@R{7M_)E{o9d!TW= zb?eq4hZret{yzdVhtOe2Nr4}gTJ&74f^mMdpl><w|bvY?^1M=9h}TKMtxN^1bW3WTc&m=gDi~kJxNU?wQ}0 zsM{}lux5s}6u7@#aVZNmvL)6ZJ(ds@?VU?h@^9}Ok-D9RHOD^P)>t{XLK!lzE^-4`#9}JivTgQ@<>L3K`F(Ev+PvY@C)loht;Prvuk1Y+ zl@e8!)chHFCSOjIu#+lv`L_d9@Qt1J_&=1>ndFS@_HPEqldC7nJS($ki=mv!8FL5| zuiMwmB?jRst{Gh9HZ0pu8}d-HGfaoSVk(kcCn%>VKk!BSLXJ~#{rcvX_T)?xW7|xX zFwwJ4h~aM1_(2?@Q3iQkr{ii1NXt9Y_Vs1ADDvx4u9JuKwoP;?gn!shzLzaKuJ$62 zCx>v9p=`gVY2Mka%&mM-%umnSTw%EE4Yt@%%tIM@1b%a#yD)S8OwW@m8zmUG{EG2f zM+}thgG-Z(i;NTO5a+ANIZ$sx^PDs0bS@ybH;MI-G8U%b}W*-c$I|F%^B_1kOu z%6QtLe4$8##H(^{3+Kl;QODoZ#mQODbQ+|tPwVB~dZ9%*Lv~UuCz{Kv6@x0o<5tpa zM{e<(BujYZvTJSjJ^#(w$;G$)ll>Sm5hhA=l7|w(6Cjc(PRWLZm&AN%3eAy=u$vde z!@bcSwFT>DWs>Iv<7{|i6~h@-y22@a4@T_$*cnaouiKr9Og8$xs)|BAn5y1$0i}KZ zAFCOdDrObBTu5+W6%i5Xaz47lRD`z}pReuT{^dzc%TA60Tvc70mn8YF$!>g4Y^kEVEi6QFlc(PKksWI^RaMH{ab8Q+ zt=2Q%B4iGsF!MbQ3`=>2b)L{tsW37!wqmi6gY+&HjKYVWUauv=L$wnkyYona`&sp* zz0fK$v_+_|dq<#k!$bb%i>~{sM+5uZ!{V)9pVWf?fT;DqO!0#TATUA*#^LbYh)I*m2Z>QtLtlu5H zzMso<(%Ke=fiaVFRvnh6@IwJ}#!nuS-b4iCsDA70jhO3Zff-ERP1oj6ZhXVjNfm-^(% z*)u+8Bt?l&;P{o6UD1Z|g_{Sv3XF1~?5aykdj!X)qz>v$;OkKax#5y&W4PMc*yfSL z8jh1Ot?`_kOVW3qTg~^*<0t$|x%FRax@;ahO(QG=O6vYszl*sNH$4JoYa)s(-34vF zJ9vK8G@r#&4qxC-$cW;&-Cymh?z*rQc4g;ZH-NwWDYAQDwrBxkyI-XBG2q{~pC?w?JldHB=OX-!&; zSYiw@>4?KN>&j#%T-%DF$LLQ9it7IHo+#|^`tp*^BZ(?V4H*Vz#MB_;UT+y{u(gh>S!~J2FMfv zauh)iEp}r#VDUPQd|#V{L^Dm0MW6T~Id=%9T(cstsvgaL=hGrE6PyG~*|6OB1NogC z%Lpp`Xz{DjF#|!j(gt5qV!*d=2iq@}gk&Tc+}*WZAHRDOCzPLg^n_nW3FeK6Y{+Bz z=0J)%)CP^nH_NK3svL!E=4#*)<>c6OwzfH^QXTSMSfcXVZ#Y}JSvSKX+yNm2q7U+9tFK4s@m?hO zq@t037AD5yyILC{Yi$y0RyNl;KAG{TByHrl*3c+`Rbf)&RzCH~Q+}wyuFg|31>(&( zQYJXLb{(FJi|gAj2(P^MGepn&G6ym7j?`=A3pQ;?q&oLDaJqaQC7!aC4V7tcvl&RG z_tSo)<`lx`D6y}iT*Q)(WshJRvHY{c`ci)9&R(jvp~p=-yXO8(1uxA)eM88R9tw#U zb}VGqu*m$@Ol<+_H$(z1B0K|TtU(OU1axeiwu7rtx1~LuAANZKb$!)AJ%)Q}dKPzQ zcZ*i-{@_g@Q*I@SmT9hiTlU-@!_g$ID1Z7k?z%aLn}4Al5#iU>?e=|@$H~d5_h4U& zjrUC)``;GRzkYjpx_0~x%JX(eYg^@_6*FLzip$G(_<^M>h+zjtj11R#HGq_)v%@Et2TBXlF%j-P7pzb=dct_)81W{K>EfCWrGAc*k6f|M1y8gA^!jQ~AF8GDC^( z#7*8fQP9qcsn_+l5cg?+j_PowV>*$rV2&_L_iivUGvm=+l6{bp3XjC|H#x{M zFStH%KtmzMq9`uCfgjS;W!{q)?6saUsoL)0El0)jOF12$V|<1DEC;R1X67zw4Xq>( z%(NyfFOzl?<}n^n>dRNn*BQV?)HetZmO&b_xxP>GYE-5qj*4e9o~BuSIqvBdRofA* zk*G_d3FX1a>;sJW^=zmuyUpW2l)CnrK$eE?#IkTfW=Dmo$fp6`zJb;^a6ZWea(I^$n2e)!jWhYIx-RwZ4KrxvP5YGN4WoE+KW z#&KYCWMY!m%I{Bomh85yZ|`nMPF`Wt#g-2Th;w>pID^L<{_%0s&CR}0i;Wq3Fikw` z^BV_eCX|=C&a-KnsZT5R_TpNtg8Sk6CTw)EEOz259YN)HLjfjBv(xt;g{;aZlW6C& z?x23Z26Lc`yRRuQZzOht%haIWTjx+#O~9MFM2Z_1-=F&S4IDQnsZiB{>bp8iF*eO- zcZ(9~<&IQ+?(0(@t8vEx=ziGZlZDxil+QyK{et+vYQ5VaDn_)Vu!AVW?+YGgV?6Qd z1|f;H#}{kS$8$m`145RS29G`3$T9R$pg19hF;_EaSk&cvM!BE+{k63Vn1tWcWKQR| zjg1M`krv6Xvjas^Q!kbG?1Z2ChM6GR#@R&K%LC36&5uKJH3wcA$nO}un=c>uHY386 z3z8eehExMr>*{+ux0aoB9-|R(31=Ia= z)6gZ6lWXr&I7^?5@qRN|D^!!X)2mT%Pxtl-*S^p5M>j#*I0rg(+rHCf*6A*6y2aw6~s`%M_(|ZA_9$TrR-qvz1#ch@UUpj+GnTK0egI+*d2IY%H;FLXAA<*uGtBgH`VR?|t;)8C$js z3-23WZr1CoY)x-^BIhBzYETTUm2VvKz94DWy1*4zP}Fc>INdG?mjW6PSMgYkPRy>{ z-FK}+LpsetUXTy?6mb5y)Hbt>Mp2IN;D_8!*ceL1yP=jgz{*ylD&fD_@()z0B! z<4`0?(1-X<(I-n$Cj~?zWj?!hkS`ayzG0mQ(Q204%lpXP3JrpAH7lLY-}hEbI34n} zjeKP5yrfb=l@7yR+aT&J{S2MB)&Jd&L6AV6kV}ngV9BE*vv3K7GVGF)wP+5!yuYFH z)R<-E%g$JyQoI=%IZ1V-`~`e=h7rsdb*Lt%zJ8rqbzElTf#xdcRaFL*^QwuWf_!_m z;9K;ZiIpWRqInhq=zaU4V4GW?&JTPo&JocTIh_DCTki>aPVHMynRvhz_~gUKxV)(h zVVFO6X)jE0X!Vr1)52PXiBes<5k^Pb^>~;u$Pgf+Y~Xhl@rST-BErHhYvV>5dYIQ+ zR=+l3GZijEF03q=1o#gxeJ34=QG&(5y$_KEKY)~vPwqpPn=0-z7mGc#$K z3jPaAuSi-T5ItW{dM>HES9EH>BZ?!}3>9Ggz4ZoH!gip%7e!YI%4R5hsMC^1#k*E| zMa4@FdMAybat4iR7sr~(5~qcP@J+P|Zdg02yXAzXkd5`tZe$l`bO_4ppW^`F>6ra@ z`z&9e?fUn~{jI(){9!e2_MBadHF12P-u5U2P37u6 z&UC||CKy-L(q*wQBL^Xm^8Rir_N2|0<#$G#)6w)Ui?}{dYemTUFlQsjyd5wCWKj!{ zMdB|m{csx6=>A@qonW96U;k^L^8xMSB4qo6+l?ePy#a-IA)Y=624@xbMnf0}l(%%A zIc0GIxkY)JBe~W%Nmvu%;}w&_=A{g$`K2r1I z!-q3gm^cp+(ayxFOc`|<%d!A`|9p;Gru^KPyHQ8KIW|uOV1Pux@-;K%1=Y}IV^IkS z1u*)RzoQCduLb7GOIa}Ou_JvGd1s&0nw99Hw7~H6LJo$0X_lDCLI2n|$Lf&8_4 z@QdL(qA&fCsX9MKV2z%`un@6=*<|`cDUx{l$%|)lT`YHv%0H8Lc4DIv_G(fok4*XV zg$Gj7UX?w^)xdFpPZgK&hYR{q`ZMQIt!{l4#a?QAZfywt+`M&z1xW3x<)PUu%i)ar zJcInS9#yhnD4$%Ht{YxiXlX-7%KDe@>bJL|?wl}wdRXdzdH1xAWDK8)Rq*HS$qS`E zS$f4QzX+$jwOd>mwI$nHF_pbUsG)vemloLbXK#SKx&L~5a_dXy0xiXs=j|8!drv+* zt9Ewh$6NpGFgm6az}@3?e@$D3ik3kSftGLbbm4OQAP!)V%)`UeMCw;}uH^Qg0{3jA z0xxid=Wj&kcI#d2M0H_BoP(giU}9;eq-BVB>Y^edTNnhiqr~3e$nZY|3^V1cXN{|0 zkII)0_;6R$nO&I~J#}<+;nAZtbGgS>e$sbvlzR`q0tYYr$yW+N`$Qqj6DlrycA?_|)!WZ!BX*9Eumcs+wM+uhf0OT@<1 zQwWCNlO{+QwvOPR>XJ*kDs7eHwsZ*o3q_w{T~$u5N+GNFaoO6FCan9xC43`k@SUCa zAKy!q9)6oL83-xL?m(Y6qJf42Y@yfK6n7Q3`DC|UZI=4wM{@5h(%sSd zdEY4iKZptBAx63E>SW`B6u5LaaiWNy)m~=<#Gk@+Z}00g;)Pa^ z%Bc<38$tCZqO!skGlF}8Mn@zqjNJ^9+T+u9o)JR#{Pg4lBP?r}x8u2343dxVY@`P} zEK!9I;|#$In)1`pysIo4dqnqOTta3oQ~$mD?21pjPFS*_RyP ze|(5Wz08UUOfUw-4bzT+T-%WuN+OK}(Um9;%o1?Ug{`eFKE%XzVNoNu$;gOmAf~B^V!86QGE0dXb7pC4$^i-#=1_hs~N0mR#aLVMX zek^%6`GiWhk8}c)+-y3xDe=)W*i4`;`i!QLq1mu=%3f$9elyem(fGWl+lnPS@#8Xi z>=D(QCLMt-nxICY_s8GQqVy>XU=Yjf_5qL5#znK^`}*xX<@8LAaJ~lRuIxw6og?F&O`e^}zU!)og1+ z`a|IP+$#}n+J_D-Z-~26(=3?F2JF6dq^1gX50A(FIOEW|jJIzU<|mbyjUf5`)^taO z#tSkO!fu3T0Ofe>m)G~!z7deK7gvE|gh>U4WaLt5%u3bs2fOr9jJE$qG{inUh)2Iw ztOx7$AKu=&a0(9$S2-6!`X+5v4Nm>paRcyhK>P6S-7~;414c*jdA|GH!{uB8KAn)e z1Mw9j?Je0a%(@KYmUM>^QYoOOqdWB{$Y?4H&!G2(=}7HS(M9&nHZ9V!M)!tF3=w=1 zh(pzUt!)Udn-O*S3=r$)O#E#mq{U~gxuPPf1$vDx$q^-PZfQabeKxHq_r1Z$?X~vR zO#jlBr{w+@@{GromR)@A8;!nR_;Ff|(2SAZEUtXjhyGeV#x8uEM?^#i=pDY-&L%uE z;bxtuB7ybML=AKM__8hd#OZDO+7wrSGd#KNf|r*Vz0YQ@|LOS4a`1Ltg%1ISyS!nn zTW=rHt={b8Y#C{{x1VSmH>7lGiBNIVOqX#La9i93g)WZb()T2&nkZ>cwhF=SYQoC= zcI$q<_a_4nKyUur?MXbvCe__z67)|-qxw8kw(zdQ`4yH${+TGR3?Q$qm;kwOIzPMtt*Eyextp໽S9NC25twcHx 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

>bxQPKg80N5G3vLX+d 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 literal 686928 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%0A9b_vZnMhoK8aqc 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

8Hdb{fb0ThBUj*YFe?=!nAP7j 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

-d+TcQ2FUFm4Py_&{veAL1yQ${J$ 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