import uuid from datetime import date from typing import List from campaigns.exceptions import ( CampaignNotFound, NotEditableField, CampaignPlatformNotFound, CampaignPlatformsGroupMismatch, PlacementNameNotUnique, InvalidPlacement, InvalidActionDSPCampaign, InvalidActionManualCampaign, EndDateGreaterThanStartDate, CampaignAlreadyAssigned, CampaignAlreadyUnassigned, CampaignAssignedToAnotherProject ) from models.campaign import Campaign, CampaignPlacements, CampaignPlatforms, CampaignSourceType from projects.exceptions import PhaseStartDateError from projects.repositories.projects_repository import ProjectsRepository from services.permission_role_service import PermissionsByRoleService from campaigns.repositories.campaigns_repository import CampaignsRepository from utils.json_encoder import DataclassDictionaryDecoder as Decoder from campaigns.schemas import FieldValidator, CampaignMetadata, CampaignCreateSchema, CreatePlacementSchema class CampaignsValidator: project_repository = ProjectsRepository() campaigns_repository = CampaignsRepository() permission_role_service = PermissionsByRoleService() def validate_end_date_greater_start_date(self, start_date: date, end_date: date): if start_date > end_date: raise EndDateGreaterThanStartDate def validate_campaign_start_date(self, project_id: int, start_date: date): project = self.project_repository.get_project_by_id(project_id) if project.initial_start_date > start_date: raise PhaseStartDateError("Campaign start_date should be greater than Project start_date") def validate_campaign_end_date(self, project_id: int, end_date: date): project = self.project_repository.get_project_by_id(project_id) if project.end_date < end_date: raise PhaseStartDateError("Campaign end_date should be lower than Project end_date") def validate_campaign_edit(self, campaign: Campaign, params: CampaignCreateSchema) -> List[str]: permissions = Decoder.convert_from_dict(CampaignMetadata, campaign.metainfo) current_genders = set(gender.id for gender in campaign.genders) new_genders = set(params.genders or []) current_ages = f"[{campaign.age_range.lower}-{campaign.age_range.upper - 1}" new_ages = f"[{params.audienceLowerAge}-{params.audienceHigherAge}" current_countries = set(t.territory.id for t in campaign.territories) new_countries = set(params.territories or []) current_links = set(t.link for t in campaign.links) new_links = set(params.destinationLinks or []) # might need to add valiadtion for new fields here validations = [ FieldValidator(permissions.fields.name, campaign.name, params.name, "name"), FieldValidator(permissions.fields.objective, campaign.objective_id, params.objective, "objective"), FieldValidator(permissions.fields.provider, campaign.provider_id, params.provider, "provider"), FieldValidator(permissions.fields.startDate, campaign.start_date, params.startDate, "startDate"), FieldValidator(permissions.fields.endDate, campaign.end_date, params.endDate, "endDate"), FieldValidator(permissions.fields.spend, float(campaign.budget_spend), params.budgetSpend, "spend"), FieldValidator(permissions.fields.budget, float(campaign.planned_budget), params.plannedBudget, "budget"), FieldValidator(permissions.fields.genders, current_genders, new_genders, "genders"), FieldValidator(permissions.fields.audienceAge, current_ages, new_ages, "audienceAge"), FieldValidator(permissions.fields.countries, current_countries, new_countries, "countries"), FieldValidator(permissions.fields.destinations, current_links, new_links, "destinations"), FieldValidator(permissions.fields.notes, campaign.notes, params.notes, "notes"), ] return list(map(lambda x: x.error_message(), filter(lambda x: not x.is_valid(), validations))) def validate_campaign_schedule_update(self, campaign_id: int, start_date: date, end_date: date) -> List[str]: campaign = self.campaigns_repository.get_campaign_by_id(campaign_id) permissions = Decoder.convert_from_dict(CampaignMetadata, campaign.metainfo) validations = [ FieldValidator(permissions.fields.startDate, campaign.start_date, start_date, "startDate"), FieldValidator(permissions.fields.endDate, campaign.end_date, end_date, "endDate"), ] return list(map(lambda x: x.error_message(), filter(lambda x: not x.is_valid(), validations))) def validate_campaign( self, campaign_id: int, project_id: int, campaign_start_date: date, campaign_end_date: date, project_start_date: date, project_end_date: date, ): campaign = self.campaigns_repository.get_campaign_by_id(campaign_id, project_id) if not campaign: raise CampaignNotFound() errors = self.validate_campaign_schedule_update(campaign_id, campaign_start_date, campaign_end_date) if errors: raise NotEditableField(detail="\n".join(errors)) if project_start_date > campaign_start_date: raise PhaseStartDateError( f"Campaign {campaign_id} start_date ({campaign_start_date}) " f"should be greater than " f"Project start_date ({project_start_date})" ) if project_end_date < campaign_end_date: raise PhaseStartDateError( f"Campaign {campaign_id} end_date ({campaign_end_date}) " f"should be less than " f"Project end_date ({project_end_date})" ) def validate_dsp_campaign(self, campaign): if campaign.source not in CampaignSourceType.all_external_sources(): raise InvalidActionDSPCampaign() def validate_manual_campaign(self, campaign): if campaign.source in CampaignSourceType.all_external_sources(): raise InvalidActionManualCampaign() def validate_manual_campaigns(self, campaigns: List): for campaign in campaigns: self.validate_manual_campaign(campaign) def validate_dsp_campaigns(self, campaigns: List): for campaign in campaigns: self.validate_dsp_campaign(campaign) def validate_campaigns_unassigned(self, campaigns: List): for campaign in campaigns: if campaign.project_id is not None: raise CampaignAlreadyAssigned() def validate_campaigns_already_unassigned(self, unassign_uuids: List[uuid.UUID], project_id: int): is_campaign_rejected = self.campaigns_repository.check_campaigns_rejected(unassign_uuids, project_id) if is_campaign_rejected: raise CampaignAlreadyUnassigned() def validate_campaigns_already_assigned_to_another_project(self, unassign_uuids: List[uuid.UUID], project_id: int): already_assigned = self.campaigns_repository.check_campaigns_already_assigned_to_another_project( unassign_uuids, project_id ) if already_assigned: raise CampaignAssignedToAnotherProject() def validate_campaign_platforms(self, campaign_platforms: List[CampaignPlatforms]): if not campaign_platforms: raise CampaignPlatformNotFound() if len(campaign_platforms) > 1: previous_platform_group = campaign_platforms[0].platform_group for platform in campaign_platforms: if platform.platform_group != previous_platform_group or platform.platform_group is None: raise CampaignPlatformsGroupMismatch() previous_platform_group = platform.platform_group def validate_create_placement(self, new_placement: CreatePlacementSchema, label_id): placement = self.campaigns_repository.get_campaign_placement_by_name_and_label_id(new_placement.name, label_id) if not placement: raise PlacementNameNotUnique() def validate_campaign_placements(self, label_id: int, campaign_placements: List[CampaignPlacements]): for placement in campaign_placements: if placement.label_id is not None and placement.label_id != label_id: raise InvalidPlacement()