import datetime import phonenumbers import sqlalchemy as sa from fansifter_common.adapters.db.types import PydanticType from fansifter_common.utils import timezone from sqlalchemy.orm import Mapped, foreign, mapped_column, relationship from ows_text_campaigns.adapters.db import Model from ows_text_campaigns.adapters.scanner import ContentRecommendations from ows_text_campaigns.assets.models import Asset class ArtistPhoneNumber(Model, kw_only=True): __tablename__ = "text_campaign_artist_phone_number" global_participant_id: Mapped[str] = mapped_column(primary_key=True) phone_number: Mapped[str] = mapped_column(sa.Text, primary_key=True) country_code: Mapped[str] = mapped_column(sa.Text, init=False) vcard_asset_id: Mapped[str | None] = mapped_column(default=None) vcard_asset: Mapped[Asset | None] = relationship( primaryjoin=foreign(vcard_asset_id) == Asset.id, # ty:ignore[invalid-argument-type] init=False, viewonly=False, uselist=False, ) def __post_init__(self) -> None: self.country_code = self._parse_country_code() def _parse_country_code(self) -> str: try: numobj = phonenumbers.parse(self.phone_number) except phonenumbers.NumberParseException: return "XX" country_code = phonenumbers.region_code_for_number(numobj) return country_code or "XX" @property def vcard_url(self) -> str | None: if self.vcard_asset: return self.vcard_asset.url return None class ArtistSettings(Model, kw_only=True): __tablename__ = "text_campaign_artist_settings" global_participant_id: Mapped[str] = mapped_column(primary_key=True) artist_name: Mapped[str] = mapped_column(default="") welcome_message: Mapped[str] = mapped_column(default="") opt_in_message: Mapped[str] = mapped_column(default="") asset_id: Mapped[str | None] = mapped_column( sa.ForeignKey("text_campaign_asset.id"), default=None ) twilio_account_sid: Mapped[str | None] = mapped_column(default=None) twilio_messaging_service_sid: Mapped[str | None] = mapped_column(default=None) updated_at: Mapped[datetime.datetime | None] = mapped_column( default=None, onupdate=timezone.now ) updated_by: Mapped[str | None] = mapped_column(default=None) asset: Mapped[Asset | None] = relationship(init=False) phone_numbers: Mapped[list[ArtistPhoneNumber]] = relationship( order_by=lambda: sa.asc(ArtistPhoneNumber.phone_number), primaryjoin=( global_participant_id == foreign(ArtistPhoneNumber.global_participant_id) ), # ty:ignore[invalid-argument-type] cascade="all, delete-orphan", default_factory=list, ) opt_in_message_scan_result: Mapped[ContentRecommendations | None] = mapped_column( PydanticType(ContentRecommendations | None), default=None ) welcome_message_scan_result: Mapped[ContentRecommendations | None] = mapped_column( PydanticType(ContentRecommendations | None), default=None ) @property def image_url(self) -> str | None: if self.asset: return self.asset.url return None def get_send_phone_number(self, recipient_country_code: str | None = None) -> str: # Find the phone number that matches the specified country code for phone_number in self.phone_numbers: if phone_number.country_code == recipient_country_code: return phone_number.phone_number return self.phone_numbers[0].phone_number