# mypy: disable-error-code=operator from decimal import Decimal import pytest from dirty_equals import IsApprox from fansifter_common.auth.account import Account from dmp.ad_reporting.enums import ( AdReportingBudgetType, AdReportingObjective, AdReportingPlatform, ) from dmp.ad_reporting.handlers import ( GetAdReportingCampaignsHandler, GetAdReportingCampaignsRequest, ) from dmp.ad_reporting.models import ( AdReportingCampaignBenchmarkByAccountArtistCountryDbt, AdReportingCampaignBenchmarkByAccountCountryDbt, AdReportingCampaignCountryDbt, AdReportingCampaignDbt, AdReportingReport, ) from dmp.meta.models import MetaAdAccount, MetaUserAdAccount from dmp.tiktok.models import TikTokAdAccount, TikTokUserAdAccount from tests.unit.faker import FakerTyped from tests.unit.types import CreateModel, CreateReportingModel class TestGetAdReportingCampaignsHandler: @pytest.mark.db def test_get_campaigns( self, handler: GetAdReportingCampaignsHandler, create_model: CreateModel, create_reporting_model: CreateReportingModel, identity_id: str, account: Account, ) -> None: ad_account = create_model(MetaAdAccount) user_ad_account = create_model( MetaUserAdAccount, ad_account_id=ad_account.id, identity_id=identity_id, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, ) campaign = create_reporting_model( AdReportingCampaignDbt, account_id=ad_account.external_id, platform=AdReportingPlatform.META, objective=AdReportingObjective.OUTCOME_AWARENESS, ) response = handler.handle( GetAdReportingCampaignsRequest( identity_id=identity_id, search=None, global_participant_id=None, platform=None, objective=None, order_by=["name.asc"], limit=10, offset=0, ) ) assert response.summary.total == 1 assert response.items[0].id == campaign.id assert response.items[0].accounts == {user_ad_account.account} @pytest.mark.db def test_get_campaigns_with_country_filter_empty( self, handler: GetAdReportingCampaignsHandler, create_model: CreateModel, create_reporting_model: CreateReportingModel, identity_id: str, account: Account, ) -> None: ad_account = create_model(MetaAdAccount) user_ad_account = create_model( MetaUserAdAccount, ad_account_id=ad_account.id, identity_id=identity_id, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, ) campaign = create_reporting_model( AdReportingCampaignDbt, account_id=ad_account.external_id, platform=AdReportingPlatform.META, objective=AdReportingObjective.OUTCOME_AWARENESS, ) create_reporting_model( AdReportingCampaignCountryDbt, campaign_id=campaign.id, account_id=ad_account.external_id, platform=AdReportingPlatform.META, objective=AdReportingObjective.OUTCOME_AWARENESS, country_code="GB", ) response = handler.handle( GetAdReportingCampaignsRequest( identity_id=identity_id, search=None, global_participant_id=None, platform=None, objective=None, order_by=["name.asc"], limit=10, offset=0, countries=["US"], ) ) assert response.summary.total == 1 assert response.summary.clicks == 0 assert response.summary.spend_usd == 0 assert response.summary.reach is None assert response.summary.actions is None assert response.summary.rate is None assert len(response.items) == 1 assert response.items[0].accounts == {user_ad_account.account} assert response.items[0].clicks == 0 assert response.items[0].spend_usd == 0 assert response.items[0].reach is None assert response.items[0].frequency is None assert response.items[0].artist_benchmark is None assert response.items[0].benchmark is None assert response.items[0].vs_artist_benchmark is None @pytest.mark.db def test_get_campaigns_with_country_filter_recalculate( self, handler: GetAdReportingCampaignsHandler, create_model: CreateModel, create_reporting_model: CreateReportingModel, identity_id: str, account: Account, fake: FakerTyped, ) -> None: ad_account = create_model(MetaAdAccount) user_ad_account = create_model( MetaUserAdAccount, ad_account_id=ad_account.id, identity_id=identity_id, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, ) campaign = create_reporting_model( AdReportingCampaignDbt, account_id=ad_account.external_id, platform=AdReportingPlatform.META, objective=AdReportingObjective.OUTCOME_AWARENESS, ) create_reporting_model( AdReportingCampaignCountryDbt, campaign_id=campaign.id, external_id=campaign.external_id, account_id=ad_account.external_id, platform=AdReportingPlatform.META, objective=AdReportingObjective.OUTCOME_AWARENESS, country_code="GB", ) campaign_country_1 = create_reporting_model( AdReportingCampaignCountryDbt, campaign_id=campaign.id, external_id=campaign.external_id, account_id=ad_account.external_id, platform=AdReportingPlatform.META, objective=AdReportingObjective.OUTCOME_AWARENESS, country_code="US", estimated_ad_recallers=fake.integer(), ) campaign_country_2 = create_reporting_model( AdReportingCampaignCountryDbt, campaign_id=campaign.id, external_id=campaign.external_id, account_id=ad_account.external_id, platform=AdReportingPlatform.META, objective=AdReportingObjective.OUTCOME_AWARENESS, country_code="EE", estimated_ad_recallers=fake.integer(), ) response = handler.handle( GetAdReportingCampaignsRequest( identity_id=identity_id, search=None, global_participant_id=None, platform=None, objective=None, order_by=["name.asc"], limit=10, offset=0, countries=["US", "EE"], ) ) assert response.summary.total == 1 assert ( response.summary.clicks == campaign_country_1.clicks + campaign_country_2.clicks ) assert campaign_country_1.reach and campaign_country_2.reach assert campaign_country_1.estimated_ad_recallers is not None assert campaign_country_2.estimated_ad_recallers is not None assert ( response.summary.reach == campaign_country_1.reach + campaign_country_2.reach ) assert ( response.summary.spend_usd == campaign_country_1.spend_usd + campaign_country_2.spend_usd ) assert response.summary.rate == IsApprox( Decimal( ( campaign_country_1.estimated_ad_recallers + campaign_country_2.estimated_ad_recallers ) / (campaign_country_1.reach + campaign_country_2.reach) ), delta=Decimal("0.001"), ) assert len(response.items) == 1 assert response.items[0].accounts == {user_ad_account.account} assert ( response.items[0].clicks == campaign_country_1.clicks + campaign_country_2.clicks ) assert ( response.items[0].reach == campaign_country_1.reach + campaign_country_2.reach ) assert ( response.items[0].spend_usd == campaign_country_1.spend_usd + campaign_country_2.spend_usd ) assert response.items[0].rate == IsApprox( Decimal( ( campaign_country_1.estimated_ad_recallers + campaign_country_2.estimated_ad_recallers ) / (campaign_country_1.reach + campaign_country_2.reach) ), delta=Decimal("0.001"), ) assert response.items[0].artist_benchmark is None assert response.items[0].benchmark is None assert response.items[0].vs_artist_benchmark is None @pytest.mark.db def test_get_campaigns_with_country_filter_showing_benchmarks( self, handler: GetAdReportingCampaignsHandler, create_model: CreateModel, create_reporting_model: CreateReportingModel, identity_id: str, account: Account, fake: FakerTyped, ) -> None: ad_account = create_model(MetaAdAccount) global_participant_id = fake.uuid4_string() country_code = fake.country() platform = AdReportingPlatform.META objective = AdReportingObjective.OUTCOME_AWARENESS user_ad_account = create_model( MetaUserAdAccount, ad_account_id=ad_account.id, identity_id=identity_id, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, ) campaign = create_reporting_model( AdReportingCampaignDbt, account_id=ad_account.external_id, platform=platform, objective=objective, global_participant_id=global_participant_id, ) create_reporting_model( AdReportingCampaignCountryDbt, campaign_id=campaign.id, account_id=ad_account.external_id, platform=platform, objective=objective, country_code=country_code, global_participant_id=global_participant_id, ) benchmark = create_reporting_model( AdReportingCampaignBenchmarkByAccountCountryDbt, vendor_id=user_ad_account.vendor_id, subaccount_id=user_ad_account.subaccount_id, country_code=country_code, platform=platform, objective=objective, ) artist_benchmark = create_reporting_model( AdReportingCampaignBenchmarkByAccountArtistCountryDbt, vendor_id=user_ad_account.vendor_id, subaccount_id=user_ad_account.subaccount_id, global_participant_id=global_participant_id, country_code=country_code, platform=platform, objective=objective, ) response = handler.handle( GetAdReportingCampaignsRequest( identity_id=identity_id, search=None, global_participant_id=None, platform=None, objective=None, order_by=["name.asc"], limit=10, offset=0, countries=[country_code], ) ) assert len(response.items) == 1 assert response.items[0].artist_benchmark == artist_benchmark.rate assert response.items[0].benchmark == benchmark.rate @pytest.mark.db def test_get_campaigns_with_country_filter_not_showing_benchmarks( self, handler: GetAdReportingCampaignsHandler, create_model: CreateModel, create_reporting_model: CreateReportingModel, identity_id: str, fake: FakerTyped, account: Account, ) -> None: ad_account = create_model(MetaAdAccount) global_participant_id = fake.uuid4_string() country_1 = fake.uuid4_string() country_2 = fake.uuid4_string() platform = AdReportingPlatform.META objective = AdReportingObjective.OUTCOME_AWARENESS user_ad_account = create_model( MetaUserAdAccount, ad_account_id=ad_account.id, identity_id=identity_id, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, ) campaign = create_reporting_model( AdReportingCampaignDbt, account_id=ad_account.external_id, platform=platform, objective=objective, global_participant_id=global_participant_id, ) create_reporting_model( AdReportingCampaignCountryDbt, campaign_id=campaign.id, account_id=ad_account.external_id, platform=platform, objective=objective, country_code=country_1, global_participant_id=global_participant_id, ) create_reporting_model( AdReportingCampaignBenchmarkByAccountCountryDbt, vendor_id=user_ad_account.vendor_id, subaccount_id=user_ad_account.subaccount_id, country_code=country_1, platform=platform, objective=objective, ) create_reporting_model( AdReportingCampaignBenchmarkByAccountArtistCountryDbt, vendor_id=user_ad_account.vendor_id, subaccount_id=user_ad_account.subaccount_id, global_participant_id=global_participant_id, country_code=country_1, platform=platform, objective=objective, ) response = handler.handle( GetAdReportingCampaignsRequest( identity_id=identity_id, search=None, global_participant_id=None, platform=None, objective=None, order_by=["name.asc"], limit=10, offset=0, countries=[country_1, country_2], ) ) assert len(response.items) == 1 assert response.items[0].artist_benchmark is None assert response.items[0].benchmark is None @pytest.mark.db def test_get_campaigns_meta_and_tiktok( self, handler: GetAdReportingCampaignsHandler, create_model: CreateModel, create_reporting_model: CreateReportingModel, identity_id: str, account: Account, ) -> None: meta_ad_account = create_model(MetaAdAccount) create_model( MetaUserAdAccount, ad_account_id=meta_ad_account.id, identity_id=identity_id, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, ) tiktok_ad_account = create_model(TikTokAdAccount) create_model( TikTokUserAdAccount, ad_account_id=tiktok_ad_account.id, identity_id=identity_id, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, ) create_reporting_model( AdReportingCampaignDbt, account_id=meta_ad_account.external_id, platform=AdReportingPlatform.META, ) create_reporting_model( AdReportingCampaignDbt, account_id=tiktok_ad_account.external_id, platform=AdReportingPlatform.TIKTOK, ) response = handler.handle( GetAdReportingCampaignsRequest( identity_id=identity_id, search=None, global_participant_id=None, platform=None, objective=None, order_by=["name.asc"], limit=10, offset=0, ) ) assert response.summary.total == 2 @pytest.mark.db def test_get_campaigns_no_allowed_accounts( self, handler: GetAdReportingCampaignsHandler, create_reporting_model: CreateReportingModel, identity_id: str, ) -> None: create_reporting_model( AdReportingCampaignDbt, platform=AdReportingPlatform.META ) create_reporting_model( AdReportingCampaignDbt, platform=AdReportingPlatform.TIKTOK, ) response = handler.handle( GetAdReportingCampaignsRequest( identity_id=identity_id, search=None, global_participant_id=None, platform=None, objective=None, order_by=["name.asc"], limit=10, offset=0, ) ) assert response.summary.total == 0 @pytest.mark.db def test_get_campaigns_empty( self, handler: GetAdReportingCampaignsHandler, identity_id: str ) -> None: response = handler.handle( GetAdReportingCampaignsRequest( identity_id=identity_id, search=None, global_participant_id=None, platform=None, objective=None, order_by=["name.asc"], limit=10, offset=0, ) ) assert response.summary.total == 0 @pytest.mark.db def test_get_campaign_included_in_report_with_report_id_param( self, handler: GetAdReportingCampaignsHandler, create_model: CreateModel, create_reporting_model: CreateReportingModel, fake: FakerTyped, identity_id: str, account: Account, ) -> None: meta_ad_account = create_model(MetaAdAccount) create_model( MetaUserAdAccount, ad_account_id=meta_ad_account.id, identity_id=identity_id, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, ) campaign = create_reporting_model( AdReportingCampaignDbt, account_id=meta_ad_account.external_id, name="Campaign 1", ) report = create_reporting_model( AdReportingReport, name="Report 1", identity_id=fake.uuid4_string(), campaign_ids={campaign.id}, ) result = handler.handle( GetAdReportingCampaignsRequest( identity_id=identity_id, report_id=report.id, search=None, global_participant_id=None, platform=None, objective=None, order_by=["name.asc"], limit=10, offset=0, ) ) assert result.items[0].id == campaign.id assert result.items[0].is_included_in_report @pytest.mark.db def test_get_campaign_not_included_in_report_with_report_id_provided( self, handler: GetAdReportingCampaignsHandler, create_model: CreateModel, create_reporting_model: CreateReportingModel, fake: FakerTyped, identity_id: str, account: Account, ) -> None: meta_ad_account = create_model(MetaAdAccount) create_model( MetaUserAdAccount, ad_account_id=meta_ad_account.id, identity_id=identity_id, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, ) campaign = create_reporting_model( AdReportingCampaignDbt, account_id=meta_ad_account.external_id, name="Campaign 1", ) create_reporting_model( AdReportingCampaignDbt, account_id=meta_ad_account.external_id, name="Campaign 2", ) report = create_reporting_model( AdReportingReport, name="Report 1", identity_id=fake.uuid4_string(), campaign_ids={campaign.id}, ) result = handler.handle( GetAdReportingCampaignsRequest( identity_id=identity_id, report_id=report.id, search=None, global_participant_id=None, platform=None, objective=None, order_by=["name.asc"], limit=10, offset=0, ) ) assert not result.items[1].is_included_in_report @pytest.mark.db def test_get_campaigns_budget_and_spend_different_currencies( self, handler: GetAdReportingCampaignsHandler, create_model: CreateModel, create_reporting_model: CreateReportingModel, identity_id: str, account: Account, ) -> None: ad_account = create_model(MetaAdAccount) create_model( MetaUserAdAccount, ad_account_id=ad_account.id, identity_id=identity_id, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, ) campaign_1 = create_reporting_model( AdReportingCampaignDbt, account_id=ad_account.external_id, platform=AdReportingPlatform.META, objective=AdReportingObjective.OUTCOME_AWARENESS, name="aaa", currency="USD", ) campaign_2 = create_reporting_model( AdReportingCampaignDbt, account_id=ad_account.external_id, platform=AdReportingPlatform.META, objective=AdReportingObjective.OUTCOME_AWARENESS, name="bbb", currency="GBP", ) response = handler.handle( GetAdReportingCampaignsRequest( identity_id=identity_id, search=None, global_participant_id=None, platform=None, objective=None, order_by=["name.asc"], limit=10, offset=0, ) ) assert response.summary.total == 2 assert response.summary.budget is None assert campaign_1.daily_budget_usd is not None assert campaign_2.daily_budget_usd is not None assert ( response.summary.budget_usd == campaign_1.daily_budget_usd + campaign_2.daily_budget_usd ) assert response.summary.currency is None assert response.summary.spend_usd == campaign_1.spend_usd + campaign_2.spend_usd assert response.summary.spend is None assert response.summary.budget_type == AdReportingBudgetType.DAILY_BUDGET assert response.items[0].name == campaign_1.name assert response.items[0].budget == campaign_1.daily_budget assert response.items[0].budget_usd == campaign_1.daily_budget_usd assert response.items[0].currency == campaign_1.currency assert response.items[0].budget_type == AdReportingBudgetType.DAILY_BUDGET assert response.items[0].spend_usd == campaign_1.spend_usd assert response.items[0].spend == campaign_1.spend assert response.items[1].name == campaign_2.name assert response.items[1].budget == campaign_2.daily_budget assert response.items[1].budget_usd == campaign_2.daily_budget_usd assert response.items[1].currency == campaign_2.currency assert response.items[1].budget_type == AdReportingBudgetType.DAILY_BUDGET assert response.items[1].spend_usd == campaign_2.spend_usd assert response.items[1].spend == campaign_2.spend