diff --git a/api/src/db/models/foreign/attachment.py b/api/src/db/models/foreign/attachment.py index f7cd98a4b..ced7b1e1d 100644 --- a/api/src/db/models/foreign/attachment.py +++ b/api/src/db/models/foreign/attachment.py @@ -5,10 +5,21 @@ # match by oracle_fdw, but we are matching them for maintainability. # +from sqlalchemy.orm import Mapped, foreign, relationship + from src.db.models.legacy_mixin import synopsis_mixin from . import foreignbase +from .opportunity import Topportunity -class tsynopsisattachment(foreignbase.ForeignBase, synopsis_mixin.TsynopsisAttachmentMixin): +class TsynopsisAttachment(foreignbase.ForeignBase, synopsis_mixin.TsynopsisAttachmentMixin): __tablename__ = "tsynopsisattachment" + + opportunity: Mapped[Topportunity | None] = relationship( + Topportunity, + primaryjoin=lambda: TsynopsisAttachment.opportunity_id + == foreign(Topportunity.opportunity_id), + uselist=False, + overlaps="opportunity", + ) diff --git a/api/src/db/models/staging/attachment.py b/api/src/db/models/staging/attachment.py index 12e4512a5..f6349cdb3 100644 --- a/api/src/db/models/staging/attachment.py +++ b/api/src/db/models/staging/attachment.py @@ -1,6 +1,18 @@ +from sqlalchemy.orm import Mapped, foreign, relationship + from src.db.models.legacy_mixin import synopsis_mixin from src.db.models.staging.staging_base import StagingBase, StagingParamMixin +from .opportunity import Topportunity + class TsynopsisAttachment(StagingBase, synopsis_mixin.TsynopsisAttachmentMixin, StagingParamMixin): __tablename__ = "tsynopsisattachment" + + opportunity: Mapped[Topportunity | None] = relationship( + Topportunity, + primaryjoin=lambda: TsynopsisAttachment.opportunity_id + == foreign(Topportunity.opportunity_id), + uselist=False, + overlaps="opportunity", + ) diff --git a/api/tests/src/data_migration/transformation/subtask/test_transform_opportunity_attachment.py b/api/tests/src/data_migration/transformation/subtask/test_transform_opportunity_attachment.py new file mode 100644 index 000000000..0c9895e89 --- /dev/null +++ b/api/tests/src/data_migration/transformation/subtask/test_transform_opportunity_attachment.py @@ -0,0 +1,41 @@ +from src.db.models.foreign.attachment import TsynopsisAttachment as TsynopsisAttachmentF +from src.db.models.staging.attachment import TsynopsisAttachment as TsynopsisAttachmentS +from tests.src.db.models.factories import ( + ForeignTsynopsisAttachmentFactory, + StagingTsynopsisAttachmentFactory, +) + + +def test_uploading_attachment_staging(db_session, enable_factory_create, tmp_path): + att = StagingTsynopsisAttachmentFactory.create(file_lob=b"Testing attachment") + db_session.commit() + db_session.expire_all() + + db_att = ( + db_session.query(TsynopsisAttachmentS) + .filter(TsynopsisAttachmentS.opportunity_id == att.opportunity_id) + .one_or_none() + ) + temp_file = tmp_path / "out_file.txt" + temp_file.write_bytes(db_att.file_lob) + file_content = temp_file.read_bytes() + + assert file_content == db_att.file_lob + + +def test_uploading_attachment_foreign(db_session, enable_factory_create, tmp_path): + att = ForeignTsynopsisAttachmentFactory.create(file_lob=b"Testing attachment") + db_session.commit() + db_session.expire_all() + + db_att = ( + db_session.query(TsynopsisAttachmentF) + .filter(TsynopsisAttachmentF.opportunity_id == att.opportunity_id) + .one_or_none() + ) + + temp_file = tmp_path / "out_file.txt" + temp_file.write_bytes(db_att.file_lob) + file_content = temp_file.read_bytes() + + assert file_content == db_att.file_lob diff --git a/api/tests/src/db/models/factories.py b/api/tests/src/db/models/factories.py index 677f32911..09218fc6c 100644 --- a/api/tests/src/db/models/factories.py +++ b/api/tests/src/db/models/factories.py @@ -997,6 +997,32 @@ class Meta: publisher_profile_id = sometimes_none(factory.Faker("random_int", min=1, max=99_999)) +class TsynopsisAttachmentFactory(BaseFactory): + class Meta: + abstract = True + + syn_att_id: factory.Sequence(lambda n: n) + opportunity_id: factory.Sequence(lambda n: n) + att_revision_number = factory.Faker("random_int", min=1000, max=10000000) + att_type: factory.Faker("att_type") + mime_type = factory.Faker("mime_type") + link_url = factory.Faker("relevant_url") + file_name = factory.Faker("file_name") + file_desc = factory.Faker("sentence") + file_lob = b"Test attachment" + file_lob_size = factory.LazyAttribute(lambda x: len(x.file_lob)) + create_date = factory.Faker("date_time_between", start_date="-1y", end_date="now") + created_date = factory.LazyAttribute( + lambda o: fake.date_time_between(start_date=o.create_date, end_date="now") + ) + last_upd_date = factory.LazyAttribute( + lambda o: fake.date_time_between(start_date=o.created_date, end_date="now") + ) + creator_id = factory.Faker("first_name") + last_upd_id = factory.Faker("first_name") + syn_att_folder_id = factory.Faker("random_int", min=1000, max=10000000) + + class TforecastFactory(BaseFactory): class Meta: abstract = True @@ -1363,6 +1389,14 @@ class Meta: creator_id = factory.Faker("first_name") +class StagingTsynopsisAttachmentFactory(TsynopsisAttachmentFactory, AbstractStagingFactory): + class Meta: + model = staging.attachment.TsynopsisAttachment + + opportunity = factory.SubFactory(StagingTopportunityFactory) + opportunity_id = factory.LazyAttribute(lambda o: o.opportunity.opportunity_id) + + #################################### # Transfer Table Factories #################################### @@ -1593,6 +1627,14 @@ class Meta: revision_number = factory.LazyAttribute(lambda s: s.synopsis.revision_number) +class ForeignTsynopsisAttachmentFactory(TsynopsisAttachmentFactory): + class Meta: + model = foreign.attachment.TsynopsisAttachment + + opportunity = factory.SubFactory(ForeignTopportunityFactory) + opportunity_id = factory.LazyAttribute(lambda o: o.opportunity.opportunity_id) + + ## # Pseudo-factories ## diff --git a/documentation/api/database/erds/staging-schema.png b/documentation/api/database/erds/staging-schema.png index 1bfae06eb..f4771f887 100644 Binary files a/documentation/api/database/erds/staging-schema.png and b/documentation/api/database/erds/staging-schema.png differ