from dataclasses import dataclass from datetime import datetime from anydi import singleton from fansifter_common.auth.requests import AuthRequest from fansifter_common.auth.services import AuthService from fansifter_common.auth.types import Permission from fansifter_common.utils import timezone from email_campaigns.campaigns.enums import EmailCampaignStatus from email_campaigns.campaigns.exceptions import EmailCampaignNotFoundError from email_campaigns.campaigns.repositories.campaign import EmailCampaignRepository from email_campaigns.campaigns.services.send_time_estimator import ( SendTimeEstimatorService, ) @dataclass(kw_only=True) class EstimateSendTimeRequest(AuthRequest): campaign_id: str send_at: datetime | None = None @dataclass(kw_only=True) class EstimateSendTimeResponse: estimated_send_duration: int emails_to_send: int emails_count: int @singleton class EstimateSendTimeHandler: permission = Permission("email_campaign", "view") def __init__( self, auth_service: AuthService, campaign_repository: EmailCampaignRepository, estimator: SendTimeEstimatorService, ) -> None: self.auth_service = auth_service self.campaign_repository = campaign_repository self.estimator = estimator def handle(self, request: EstimateSendTimeRequest) -> EstimateSendTimeResponse: campaign = self.campaign_repository.get(request.campaign_id) if not campaign: raise EmailCampaignNotFoundError() self.auth_service.authorize_account( request.identity_id, account=campaign.account, permission=self.permission, ) if campaign.status not in ( EmailCampaignStatus.DRAFT, EmailCampaignStatus.SCHEDULED, EmailCampaignStatus.IN_PROGRESS, ): return EstimateSendTimeResponse( estimated_send_duration=0, emails_to_send=0, emails_count=0 ) if not campaign.audience_id or not campaign.email_domain_id: return EstimateSendTimeResponse( estimated_send_duration=0, emails_to_send=0, emails_count=0 ) if campaign.status == EmailCampaignStatus.IN_PROGRESS: send_at = timezone.now() elif campaign.status == EmailCampaignStatus.SCHEDULED: send_at = campaign.send_at or timezone.now() else: send_at = request.send_at or timezone.now() if campaign.status == EmailCampaignStatus.DRAFT: campaign.scheduled_at = timezone.now() estimated_duration, emails_to_send = self.estimator.estimate_send_duration( campaign=campaign, send_at=send_at, ) return EstimateSendTimeResponse( estimated_send_duration=estimated_duration, emails_to_send=emails_to_send, emails_count=emails_to_send, )