import decimal from datetime import datetime import sqlalchemy as sa from fansifter_common.adapters.db.types import ChoiceType from fansifter_common.auth.account import Account from fansifter_common.utils import timezone from fansifter_common.utils.uuid import uuid_string from sqlalchemy import ARRAY from sqlalchemy.ext.associationproxy import AssociationProxy, association_proxy from sqlalchemy.orm import Mapped, mapped_column, relationship from dmp.ad_reporting.enums import ( AdReportingActionType, AdReportingAdEffectiveStatus, AdReportingAdSetEffectiveStatus, AdReportingCampaignEffectiveStatus, AdReportingObjective, AdReportingPlatform, AdReportingRateType, AdReportingStatus, ) from dmp.adapters.db import ReportingModel class AdReportingCampaignArtist(ReportingModel, kw_only=True): __tablename__ = "ad_reporting_campaign_artist" campaign_id: Mapped[str] = mapped_column(primary_key=True, init=False) global_participant_id: Mapped[str] = mapped_column(primary_key=True) class AdReportingCampaignDbt(ReportingModel, kw_only=True): __tablename__ = "ad_reporting_campaign_dbt" id: Mapped[str] = mapped_column(primary_key=True) source_schema: Mapped[str] = mapped_column(primary_key=True) external_id: Mapped[str] platform: Mapped[AdReportingPlatform] = mapped_column( ChoiceType(AdReportingPlatform) ) name: Mapped[str] account_id: Mapped[str] global_participant_id: Mapped[str | None] start_at: Mapped[datetime] end_at: Mapped[datetime] status: Mapped[AdReportingStatus] = mapped_column(ChoiceType(AdReportingStatus)) objective: Mapped[AdReportingObjective] = mapped_column( ChoiceType(AdReportingObjective) ) targeting_countries: Mapped[list[str]] = mapped_column( ARRAY(sa.Text), default_factory=list ) reporting_url: Mapped[str] effective_status: Mapped[AdReportingCampaignEffectiveStatus] = mapped_column( ChoiceType(AdReportingCampaignEffectiveStatus) ) daily_budget: Mapped[float | None] lifetime_budget: Mapped[float | None] ad_set_daily_budget: Mapped[float | None] ad_set_lifetime_budget: Mapped[float | None] daily_budget_usd: Mapped[float | None] lifetime_budget_usd: Mapped[float | None] ad_set_daily_budget_usd: Mapped[float | None] ad_set_lifetime_budget_usd: Mapped[float | None] clicks: Mapped[int] impressions: Mapped[int] reach: Mapped[int | None] estimated_ad_recallers: Mapped[int | None] views_p25: Mapped[int] views: Mapped[int] purchases: Mapped[int | None] follows: Mapped[int | None] currency: Mapped[str] spend: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 2)) spend_usd: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 2)) likes: Mapped[int | None] complete_payment: Mapped[int | None] checkout: Mapped[int | None] actions: Mapped[int] action_type: Mapped[AdReportingActionType] rate: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) rate_type: Mapped[AdReportingRateType] cost_per_action: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) cost_per_action_usd: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) frequency: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) synced_at: Mapped[datetime] # Relationships artist_associations: Mapped[list[AdReportingCampaignArtist]] = relationship( primaryjoin="foreign(AdReportingCampaignArtist.campaign_id) == AdReportingCampaignDbt.id", # noqa default_factory=list, cascade="all, delete-orphan", passive_deletes=True, lazy="raise", ) _global_participant_ids: AssociationProxy[list[str]] = association_proxy( "artist_associations", "global_participant_id", default_factory=list, creator=lambda global_participant_id: AdReportingCampaignArtist( global_participant_id=global_participant_id ), ) @property def first_global_participant_id(self) -> str | None: try: return next(iter(self.global_participant_ids)) except StopIteration: pass return None @property def global_participant_ids(self) -> set[str]: if self._global_participant_ids: return set(self._global_participant_ids) return {self.global_participant_id} if self.global_participant_id else set() @global_participant_ids.setter def global_participant_ids(self, global_participant_ids: set[str]) -> None: self._global_participant_ids = list(global_participant_ids) class AdReportingCampaignCountryDbt(ReportingModel, kw_only=True): __tablename__ = "ad_reporting_campaign_country_dbt" campaign_id: Mapped[str] = mapped_column(primary_key=True) source_schema: Mapped[str] = mapped_column(primary_key=True) external_id: Mapped[str] country_code: Mapped[str] = mapped_column(primary_key=True) platform: Mapped[AdReportingPlatform] = mapped_column( ChoiceType(AdReportingPlatform) ) name: Mapped[str] account_id: Mapped[str] global_participant_id: Mapped[str | None] start_at: Mapped[datetime] end_at: Mapped[datetime] status: Mapped[AdReportingStatus] = mapped_column(ChoiceType(AdReportingStatus)) objective: Mapped[AdReportingObjective] = mapped_column( ChoiceType(AdReportingObjective) ) targeting_countries: Mapped[list[str]] = mapped_column( ARRAY(sa.Text), default_factory=list ) reporting_url: Mapped[str] effective_status: Mapped[AdReportingCampaignEffectiveStatus] = mapped_column( ChoiceType(AdReportingCampaignEffectiveStatus) ) daily_budget: Mapped[float | None] lifetime_budget: Mapped[float | None] ad_set_daily_budget: Mapped[float | None] ad_set_lifetime_budget: Mapped[float | None] daily_budget_usd: Mapped[float | None] lifetime_budget_usd: Mapped[float | None] ad_set_daily_budget_usd: Mapped[float | None] ad_set_lifetime_budget_usd: Mapped[float | None] clicks: Mapped[int] impressions: Mapped[int] reach: Mapped[int | None] estimated_ad_recallers: Mapped[int | None] views_p25: Mapped[int] views: Mapped[int] views_thruplay: Mapped[int | None] views_6s: Mapped[int | None] purchases: Mapped[int | None] follows: Mapped[int | None] currency: Mapped[str] spend: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 2)) spend_usd: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 2)) likes: Mapped[int | None] complete_payment: Mapped[int | None] checkout: Mapped[int | None] actions: Mapped[int] action_type: Mapped[AdReportingActionType] rate: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) rate_type: Mapped[AdReportingRateType] cost_per_action: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) cost_per_action_usd: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) frequency: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) synced_at: Mapped[datetime] class AdReportingAdSetDbt(ReportingModel, kw_only=True): __tablename__ = "ad_reporting_ad_set_dbt" id: Mapped[str] = mapped_column(primary_key=True, unique=True) source_schema: Mapped[str] = mapped_column(primary_key=True) external_id: Mapped[str] platform: Mapped[AdReportingPlatform] = mapped_column( ChoiceType(AdReportingPlatform) ) name: Mapped[str] account_id: Mapped[str] campaign_id: Mapped[str] campaign_name: Mapped[str] campaign_objective: Mapped[AdReportingObjective] = mapped_column( ChoiceType(AdReportingObjective) ) campaign_global_participant_id: Mapped[str | None] targeting_countries: Mapped[list[str]] = mapped_column( ARRAY(sa.Text), default_factory=list ) effective_status: Mapped[AdReportingAdSetEffectiveStatus] = mapped_column( ChoiceType(AdReportingAdSetEffectiveStatus) ) daily_budget: Mapped[float | None] lifetime_budget: Mapped[float | None] campaign_daily_budget: Mapped[float | None] campaign_lifetime_budget: Mapped[float | None] daily_budget_usd: Mapped[float | None] lifetime_budget_usd: Mapped[float | None] campaign_daily_budget_usd: Mapped[float | None] campaign_lifetime_budget_usd: Mapped[float | None] reporting_url: Mapped[str] start_at: Mapped[datetime] end_at: Mapped[datetime] status: Mapped[AdReportingStatus] = mapped_column(ChoiceType(AdReportingStatus)) clicks: Mapped[int] impressions: Mapped[int] reach: Mapped[int | None] estimated_ad_recallers: Mapped[int | None] views_p25: Mapped[int] views: Mapped[int] purchases: Mapped[int | None] follows: Mapped[int | None] currency: Mapped[str] spend: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 2)) spend_usd: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 2)) likes: Mapped[int | None] complete_payment: Mapped[int | None] checkout: Mapped[int | None] actions: Mapped[int] action_type: Mapped[AdReportingActionType] rate: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) rate_type: Mapped[AdReportingRateType] cost_per_action: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) cost_per_action_usd: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) frequency: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) synced_at: Mapped[datetime] class AdReportingAdSetCountryDbt(ReportingModel, kw_only=True): __tablename__ = "ad_reporting_ad_set_country_dbt" id: Mapped[str] = mapped_column(primary_key=True, unique=True) source_schema: Mapped[str] = mapped_column(primary_key=True) external_id: Mapped[str] platform: Mapped[AdReportingPlatform] = mapped_column( ChoiceType(AdReportingPlatform) ) country_code: Mapped[str] name: Mapped[str] account_id: Mapped[str] campaign_id: Mapped[str] campaign_name: Mapped[str] campaign_objective: Mapped[AdReportingObjective] = mapped_column( ChoiceType(AdReportingObjective) ) campaign_global_participant_id: Mapped[str | None] targeting_countries: Mapped[list[str]] = mapped_column( ARRAY(sa.Text), default_factory=list ) effective_status: Mapped[AdReportingAdSetEffectiveStatus] = mapped_column( ChoiceType(AdReportingAdSetEffectiveStatus) ) daily_budget: Mapped[float | None] lifetime_budget: Mapped[float | None] campaign_daily_budget: Mapped[float | None] campaign_lifetime_budget: Mapped[float | None] daily_budget_usd: Mapped[float | None] lifetime_budget_usd: Mapped[float | None] campaign_daily_budget_usd: Mapped[float | None] campaign_lifetime_budget_usd: Mapped[float | None] reporting_url: Mapped[str] start_at: Mapped[datetime] end_at: Mapped[datetime] status: Mapped[AdReportingStatus] = mapped_column(ChoiceType(AdReportingStatus)) clicks: Mapped[int] impressions: Mapped[int] reach: Mapped[int | None] estimated_ad_recallers: Mapped[int | None] views_p25: Mapped[int] views_thruplay: Mapped[int | None] views_6s: Mapped[int] views: Mapped[int] purchases: Mapped[int | None] follows: Mapped[int | None] currency: Mapped[str] spend: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 2)) spend_usd: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 2)) likes: Mapped[int | None] complete_payment: Mapped[int | None] checkout: Mapped[int | None] actions: Mapped[int] action_type: Mapped[AdReportingActionType] rate: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) rate_type: Mapped[AdReportingRateType] cost_per_action: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) cost_per_action_usd: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) frequency: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) synced_at: Mapped[datetime] class AdReportingAdDbt(ReportingModel, kw_only=True): __tablename__ = "ad_reporting_ad_dbt" id: Mapped[str] = mapped_column(primary_key=True, unique=True) source_schema: Mapped[str] = mapped_column(primary_key=True) external_id: Mapped[str] platform: Mapped[AdReportingPlatform] = mapped_column( ChoiceType(AdReportingPlatform) ) name: Mapped[str] account_id: Mapped[str] campaign_id: Mapped[str] campaign_name: Mapped[str] campaign_objective: Mapped[AdReportingObjective] = mapped_column( ChoiceType(AdReportingObjective) ) campaign_global_participant_id: Mapped[str | None] targeting_countries: Mapped[list[str]] = mapped_column( ARRAY(sa.Text), default_factory=list ) reporting_url: Mapped[str] effective_status: Mapped[AdReportingAdEffectiveStatus] = mapped_column( ChoiceType(AdReportingAdEffectiveStatus) ) ad_set_id: Mapped[str] ad_set_name: Mapped[str] ad_set_start_at: Mapped[datetime] ad_set_end_at: Mapped[datetime] status: Mapped[AdReportingStatus] = mapped_column(ChoiceType(AdReportingStatus)) clicks: Mapped[int] impressions: Mapped[int] reach: Mapped[int | None] estimated_ad_recallers: Mapped[int | None] views_p25: Mapped[int] views: Mapped[int] views_thruplay: Mapped[int | None] views_6s: Mapped[int | None] purchases: Mapped[int | None] follows: Mapped[int | None] currency: Mapped[str] spend: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 2)) spend_usd: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 2)) likes: Mapped[int | None] complete_payment: Mapped[int | None] checkout: Mapped[int | None] actions: Mapped[int] action_type: Mapped[AdReportingActionType] rate: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) rate_type: Mapped[AdReportingRateType] cost_per_action: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) cost_per_action_usd: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) frequency: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) synced_at: Mapped[datetime] creative_id: Mapped[str | None] creative_image_url: Mapped[str | None] creative_video_url: Mapped[str | None] creative_post_url: Mapped[str | None] creative_original_thumbnail_url: Mapped[str | None] ad_set_daily_budget: Mapped[float | None] ad_set_lifetime_budget: Mapped[float | None] campaign_daily_budget: Mapped[float | None] campaign_lifetime_budget: Mapped[float | None] ad_set_daily_budget_usd: Mapped[float | None] ad_set_lifetime_budget_usd: Mapped[float | None] campaign_daily_budget_usd: Mapped[float | None] campaign_lifetime_budget_usd: Mapped[float | None] class AdReportingAdCountryDbt(ReportingModel, kw_only=True): __tablename__ = "ad_reporting_ad_country_dbt" id: Mapped[str] = mapped_column(primary_key=True, unique=True) source_schema: Mapped[str] = mapped_column(primary_key=True) external_id: Mapped[str] platform: Mapped[AdReportingPlatform] = mapped_column( ChoiceType(AdReportingPlatform) ) country_code: Mapped[str] name: Mapped[str] account_id: Mapped[str] campaign_id: Mapped[str] campaign_name: Mapped[str] campaign_objective: Mapped[AdReportingObjective] = mapped_column( ChoiceType(AdReportingObjective) ) campaign_global_participant_id: Mapped[str | None] targeting_countries: Mapped[list[str]] = mapped_column( ARRAY(sa.Text), default_factory=list ) reporting_url: Mapped[str] effective_status: Mapped[AdReportingAdEffectiveStatus] = mapped_column( ChoiceType(AdReportingAdEffectiveStatus) ) ad_set_id: Mapped[str] ad_set_name: Mapped[str] ad_set_start_at: Mapped[datetime] ad_set_end_at: Mapped[datetime] status: Mapped[AdReportingStatus] = mapped_column(ChoiceType(AdReportingStatus)) clicks: Mapped[int] impressions: Mapped[int] reach: Mapped[int | None] estimated_ad_recallers: Mapped[int | None] views_p25: Mapped[int] views: Mapped[int] views_thruplay: Mapped[int | None] views_6s: Mapped[int | None] purchases: Mapped[int | None] follows: Mapped[int | None] currency: Mapped[str] spend: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 2)) spend_usd: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 2)) likes: Mapped[int | None] complete_payment: Mapped[int | None] checkout: Mapped[int | None] actions: Mapped[int] action_type: Mapped[AdReportingActionType] rate: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) rate_type: Mapped[AdReportingRateType] cost_per_action: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) cost_per_action_usd: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) frequency: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) synced_at: Mapped[datetime] creative_id: Mapped[str | None] creative_image_url: Mapped[str | None] creative_video_url: Mapped[str | None] creative_post_url: Mapped[str | None] creative_original_thumbnail_url: Mapped[str | None] ad_set_daily_budget: Mapped[float | None] ad_set_lifetime_budget: Mapped[float | None] campaign_daily_budget: Mapped[float | None] campaign_lifetime_budget: Mapped[float | None] ad_set_daily_budget_usd: Mapped[float | None] ad_set_lifetime_budget_usd: Mapped[float | None] campaign_daily_budget_usd: Mapped[float | None] campaign_lifetime_budget_usd: Mapped[float | None] class AdReportingReportCampaign(ReportingModel, kw_only=True): __tablename__ = "ad_reporting_report_campaign" report_id: Mapped[str] = mapped_column( sa.ForeignKey("ad_reporting_report.id"), primary_key=True, init=False, ) campaign_id: Mapped[str] = mapped_column(primary_key=True) class AdReportingReport(ReportingModel, kw_only=True): __tablename__ = "ad_reporting_report" __table_args__ = (sa.UniqueConstraint("identity_id", "name"),) id: Mapped[str] = mapped_column(primary_key=True, default_factory=uuid_string) identity_id: Mapped[str] name: Mapped[str] created_at: Mapped[datetime] = mapped_column(default_factory=timezone.now) updated_at: Mapped[datetime] = mapped_column( default_factory=timezone.now, onupdate=timezone.now ) campaign_associations: Mapped[list[AdReportingReportCampaign]] = relationship( default_factory=list, cascade="all, delete-orphan", passive_deletes=True, lazy="raise", ) campaign_ids: AssociationProxy[list[str]] = association_proxy( "campaign_associations", "campaign_id", default_factory=list, creator=lambda campaign_id: AdReportingReportCampaign(campaign_id=campaign_id), ) @property def created_by(self) -> str: return self.identity_id class AdReportingCampaignBenchmarkByAccountDbt(ReportingModel, kw_only=True): __tablename__ = "ad_reporting_campaign_benchmark_by_account_dbt" vendor_id: Mapped[int] = mapped_column(primary_key=True) subaccount_id: Mapped[int] = mapped_column(primary_key=True) platform: Mapped[AdReportingPlatform] = mapped_column( ChoiceType(AdReportingPlatform), primary_key=True, ) objective: Mapped[AdReportingObjective] = mapped_column( ChoiceType(AdReportingObjective), primary_key=True, ) rate: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) @property def account(self) -> Account: return Account( vendor_id=self.vendor_id, subaccount_id=self.subaccount_id, ) class AdReportingCampaignBenchmarkByAccountCountryDbt(ReportingModel, kw_only=True): __tablename__ = "ad_reporting_campaign_benchmark_by_account_country_dbt" vendor_id: Mapped[int] = mapped_column(primary_key=True) subaccount_id: Mapped[int] = mapped_column(primary_key=True) country_code: Mapped[str] = mapped_column(primary_key=True) platform: Mapped[AdReportingPlatform] = mapped_column( ChoiceType(AdReportingPlatform), primary_key=True, ) objective: Mapped[AdReportingObjective] = mapped_column( ChoiceType(AdReportingObjective), primary_key=True, ) rate: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) class AdReportingCampaignBenchmarkByAccountArtistDbt(ReportingModel, kw_only=True): __tablename__ = "ad_reporting_campaign_benchmark_by_account_artist_dbt" vendor_id: Mapped[int] = mapped_column(primary_key=True) subaccount_id: Mapped[int] = mapped_column(primary_key=True) global_participant_id: Mapped[str] = mapped_column(primary_key=True) platform: Mapped[AdReportingPlatform] = mapped_column( ChoiceType(AdReportingPlatform), primary_key=True, ) objective: Mapped[AdReportingObjective] = mapped_column( ChoiceType(AdReportingObjective), primary_key=True, ) rate: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) class AdReportingCampaignBenchmarkByAccountArtistCountryDbt( ReportingModel, kw_only=True ): __tablename__ = "ad_reporting_campaign_benchmark_by_account_artist_country_dbt" vendor_id: Mapped[int] = mapped_column(primary_key=True) subaccount_id: Mapped[int] = mapped_column(primary_key=True) global_participant_id: Mapped[str] = mapped_column(primary_key=True) country_code: Mapped[str] = mapped_column(primary_key=True) platform: Mapped[AdReportingPlatform] = mapped_column( ChoiceType(AdReportingPlatform), primary_key=True, ) objective: Mapped[AdReportingObjective] = mapped_column( ChoiceType(AdReportingObjective), primary_key=True, ) rate: Mapped[decimal.Decimal] = mapped_column(sa.Numeric(10, 3)) class AdReportingCreativeThumbnail(ReportingModel, kw_only=True): __tablename__ = "ad_reporting_creative_thumbnail" id: Mapped[str] = mapped_column(primary_key=True, default_factory=uuid_string) creative_id: Mapped[str] platform: Mapped[str] source_url: Mapped[str] img_s3_obj_key: Mapped[str | None] img_url: Mapped[str | None] timg64x64_s3_obj_key: Mapped[str | None] timg64x64_url: Mapped[str | None] synced_at: Mapped[datetime] modified_at: Mapped[datetime]