from typing import List, Optional from models import Playlist from services.permission_role_service import PermissionsByRoleService, RoleSchema from services.project_history.schemas import ( HistoryItemResponseModel, ProjectHistoryResponseSchema, HistoryBudgetValue, HistoryTerritory, HistoryDateRange, HistoryTextValue, HistoryUser, HistoryTeamMemberSchema, ProjectNotesResponseSchema, HistoryPlaylist, HistoryArtist, HistoryObjective, RecentHistoryItemResponseSchema, ProjectHistorySchema, HistoryListTextValue, HistoryCampaignChanged ) from projects.schemas import ProjectHistoryRequestSchema from models.user_project import UserProjectRoles from projects.services.project_target_items_maper import ProjectTargetItemsMapper from utils.json_encoder import DataclassDictionaryEncoder as Encoder from models.projects_history import HistoryActionType, ProjectHistoryItem from utils.date_utils import DateRange from services.project_history.project_history_repository import ( ProjectHistoryItemModel, ProjectHistoryRepository, ProjectHistoryFetchResult, ) from models.user import User from models.projects import Project from models.artists import Artist from models.campaign import Campaign from models.media_plans import MediaPlan from models.media_plan_phases import MediaPlanPhase class HistoryService: repository = ProjectHistoryRepository() roles_service = PermissionsByRoleService() projects_targets_mapper = ProjectTargetItemsMapper() def get_project_history(self, project_id: int, params: ProjectHistoryRequestSchema) -> ProjectHistoryResponseSchema: history_items = self.repository.get_project_history( project_id, params.offset, params.limit, params.query, params.actionType ) total = self.repository.get_project_history_items_count(project_id, params.query, params.actionType) models = list(map(self.__map_history_item, history_items)) return ProjectHistoryResponseSchema(models, total, params.limit, params.offset) def get_recent_history(self, user_id: int) -> List[RecentHistoryItemResponseSchema]: history_items = self.repository.get_recent_activity(user_id) return list(map(self.__map_recent_history_item, history_items)) def get_project_notes_history(self, project_id: int) -> ProjectNotesResponseSchema: history_items = self.repository.get_project_notes_changes(project_id) models = list(map(self.__map_history_item, history_items)) return ProjectNotesResponseSchema(models) def __map_history_item(self, fetch_result: ProjectHistoryFetchResult) -> HistoryItemResponseModel: user = HistoryUser(fetch_result.item.user) if fetch_result.item.user is not None else None if user and fetch_result.role is not None: user.role = RoleSchema(UserProjectRoles(fetch_result.role)) return HistoryItemResponseModel(fetch_result.item, user) def __map_recent_history_item(self, history_item: ProjectHistoryItem) -> RecentHistoryItemResponseSchema: user = HistoryUser(history_item.user) project = history_item.project target_items = self.projects_targets_mapper.map_project_target_items_from_model(project.not_deleted_items()) event = HistoryItemResponseModel(history_item, user) return RecentHistoryItemResponseSchema(event=event, project=ProjectHistorySchema(project, target_items)) def log_campaign_created(self, campaign: Campaign, user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.ADD_CAMPAIGN) item.project_id = campaign.project_id item.user_id = user_id item.media_plan_id = getattr(campaign.phase, 'media_plan_id', None) item.campaign_id = campaign.id item.new_value = Encoder.convert_to_dict(HistoryBudgetValue(campaign.planned_budget)) item.keywords = campaign.planned_budget self.repository.store_project_history_item(item) def log_campaign_deleted(self, campaign: Campaign, user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.REMOVE_CAMPAIGN) item.project_id = campaign.project_id item.user_id = user_id item.media_plan_id = getattr(campaign.phase, 'media_plan_id', None) item.campaign_id = campaign.id item.old_value = Encoder.convert_to_dict(HistoryBudgetValue(campaign.planned_budget)) item.keywords = campaign.budget_spend self.repository.store_project_history_item(item) def log_campaign_schedule_updated( self, campaign: Campaign, old_schedule: DateRange, new_schedule: DateRange, user_id: Optional[int] ): item = ProjectHistoryItemModel(HistoryActionType.UPDATE_CAMPAIGN_TIMELINE) item.project_id = campaign.project_id item.user_id = user_id item.campaign_id = campaign.id item.old_value = Encoder.convert_to_dict(HistoryDateRange(old_schedule)) item.new_value = Encoder.convert_to_dict(HistoryDateRange(new_schedule)) self.repository.store_project_history_item(item) def log_campaign_budget_updated( self, campaign: Campaign, old_budget: float, new_budget: float, user_id: Optional[int] ): item = ProjectHistoryItemModel(HistoryActionType.UPDATE_CAMPAIGN_BUDGET) item.project_id = campaign.project_id item.user_id = user_id item.campaign_id = campaign.id item.old_value = Encoder.convert_to_dict(HistoryBudgetValue(old_budget)) item.new_value = Encoder.convert_to_dict(HistoryBudgetValue(new_budget)) item.keywords = "{old_value} {new_value}".format(old_value=old_budget, new_value=new_budget) self.repository.store_project_history_item(item) def log_campaign_countries_updated( self, campaign: Campaign, project_id: int, old_countries: List[HistoryTerritory], new_countries: List[HistoryTerritory], user_id: Optional[int], ): item = ProjectHistoryItemModel(HistoryActionType.CHANGE_TERRITORIES) item.project_id = project_id item.user_id = user_id item.campaign_id = campaign.id item.old_value = Encoder.convert_to_dict(old_countries) item.new_value = Encoder.convert_to_dict(new_countries) item.keywords = self.__unique_keywords([i.name for i in old_countries], [i.name for i in new_countries]) self.repository.store_project_history_item(item) def log_project_budget_updated( self, project: Project, old_budget: float, new_budget: float, user_id: Optional[int] ): item = ProjectHistoryItemModel(HistoryActionType.UPDATE_PROJECT_BUDGET) item.project_id = project.id item.user_id = user_id item.old_value = Encoder.convert_to_dict(HistoryBudgetValue(old_budget)) item.new_value = Encoder.convert_to_dict(HistoryBudgetValue(new_budget)) item.keywords = "{old_value} {new_value}".format(old_value=old_budget, new_value=new_budget) self.repository.store_project_history_item(item) def log_project_created(self, project: Project, user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.ADD_PROJECT) item.project_id = project.id item.user_id = user_id item.new_value = Encoder.convert_to_dict(HistoryTextValue(project.prs_title or project.ccp_title)) item.keywords = project.prs_title or project.ccp_title self.repository.store_project_history_item(item) def log_project_team_members_updated( self, project_id: int, old_users: List[HistoryTeamMemberSchema], new_users: List[HistoryTeamMemberSchema], user_id: Optional[int], ): item = ProjectHistoryItemModel(HistoryActionType.CHANGE_PROJECT_TEAM) item.project_id = project_id item.user_id = user_id item.old_value = Encoder.convert_to_dict(old_users) item.new_value = Encoder.convert_to_dict(new_users) item.keywords = self.__unique_keywords([i.name for i in old_users], [i.name for i in new_users]) self.repository.store_project_history_item(item) def log_campaign_platform_updated( self, campaign: Campaign, old_platforms: Optional[List[str]], new_platforms: Optional[List[str]], user_id: Optional[int], ): item = ProjectHistoryItemModel(HistoryActionType.CHANGE_CAMPAIGN_PLATFORM) item.project_id = campaign.project_id item.user_id = user_id item.campaign_id = campaign.id keywords = [] if old_platforms: item.old_value = Encoder.convert_to_dict(HistoryListTextValue(sorted(old_platforms))) [keywords.append(p) for p in old_platforms] if new_platforms: item.new_value = Encoder.convert_to_dict(HistoryListTextValue(sorted(new_platforms))) [keywords.append(p) for p in new_platforms] item.keywords = " ".join(list(map(lambda x: x, keywords))) self.repository.store_project_history_item(item) def log_project_notes_updated( self, project: Project, old_notes: Optional[str], new_notes: Optional[str], user_id: Optional[int] ): item = ProjectHistoryItemModel(HistoryActionType.UPDATE_PROJECT_NOTES) item.project_id = project.id item.user_id = user_id item.old_value = Encoder.convert_to_dict(HistoryTextValue(old_notes)) item.new_value = Encoder.convert_to_dict(HistoryTextValue(new_notes)) self.repository.store_project_history_item(item) def log_project_name_updated(self, project_id: int, old_name: str, new_name: str, user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.UPDATE_PROJECT_NAME) item.project_id = project_id item.user_id = user_id item.old_value = Encoder.convert_to_dict(HistoryTextValue(old_name)) item.new_value = Encoder.convert_to_dict(HistoryTextValue(new_name)) item.keywords = new_name self.repository.store_project_history_item(item) def log_project_target_type_updated(self, project_id: int, old_type: str, new_type: str, user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.UPDATE_PROJECT_TARGET_TYPE) item.project_id = project_id item.user_id = user_id item.old_value = Encoder.convert_to_dict(HistoryTextValue(old_type)) item.new_value = Encoder.convert_to_dict(HistoryTextValue(new_type)) self.repository.store_project_history_item(item) def log_added_primary_artists(self, project_id: int, new_artists: List[Artist], user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.ADD_PRIMARY_ARTISTS_TO_PROJECT) item.project_id = project_id item.user_id = user_id item.new_value = Encoder.convert_to_dict(list(map(lambda x: HistoryArtist(x), new_artists))) item.keywords = " ".join(list(map(lambda x: x.name, new_artists))) self.repository.store_project_history_item(item) def log_removed_primary_artists(self, project_id: int, removed_artists: List[Artist], user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.REMOVE_PRIMARY_ARTISTS_FROM_PROJECT) item.project_id = project_id item.user_id = user_id item.old_value = Encoder.convert_to_dict(list(map(lambda x: HistoryArtist(x), removed_artists))) item.keywords = " ".join(list(map(lambda x: x.name, removed_artists))) self.repository.store_project_history_item(item) def log_added_featured_artists(self, project_id: int, new_artists: List[Artist], user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.ADD_FEATURED_ARTISTS_TO_PROJECT) item.project_id = project_id item.user_id = user_id item.new_value = Encoder.convert_to_dict(list(map(lambda x: HistoryArtist(x), new_artists))) item.keywords = " ".join(list(map(lambda x: x.name, new_artists))) self.repository.store_project_history_item(item) def log_removed_featured_artists(self, project_id: int, removed_artists: List[Artist], user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.REMOVE_FEATURED_ARTISTS_FROM_PROJECT) item.project_id = project_id item.user_id = user_id item.old_value = Encoder.convert_to_dict(list(map(lambda x: HistoryArtist(x), removed_artists))) item.keywords = " ".join(list(map(lambda x: x.name, removed_artists))) self.repository.store_project_history_item(item) def log_added_playlists(self, project_id: int, new_playlists: List[Playlist], user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.ADD_PLAYLISTS_TO_PROJECT) item.project_id = project_id item.user_id = user_id item.new_value = Encoder.convert_to_dict(list(map(lambda x: HistoryPlaylist(x), new_playlists))) item.keywords = " ".join(list(map(lambda x: x.name, new_playlists))) self.repository.store_project_history_item(item) def log_removed_playlists(self, project_id: int, removed_playlists: List[Playlist], user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.REMOVE_PLAYLISTS_FROM_PROJECT) item.project_id = project_id item.user_id = user_id item.old_value = Encoder.convert_to_dict(list(map(lambda x: HistoryPlaylist(x), removed_playlists))) item.keywords = " ".join(list(map(lambda x: x.name, removed_playlists))) self.repository.store_project_history_item(item) def log_campaign_objective_updated( self, campaign: Campaign, old_value: HistoryObjective, new_value: HistoryObjective, user_id: Optional[int] ): item = ProjectHistoryItemModel(HistoryActionType.UPDATE_CAMPAIGN_OBJECTIVE) item.project_id = campaign.project_id item.user_id = user_id item.campaign_id = campaign.id item.old_value = Encoder.convert_to_dict(old_value) item.new_value = Encoder.convert_to_dict(new_value) item.keywords = new_value.name self.repository.store_project_history_item(item) def log_campaign_notes_updated( self, campaign: Campaign, old_notes: Optional[str], new_notes: Optional[str], user_id: Optional[int] ): item = ProjectHistoryItemModel(HistoryActionType.UPDATE_CAMPAIGN_NOTES) item.project_id = campaign.project_id item.user_id = user_id item.campaign_id = campaign.id item.old_value = Encoder.convert_to_dict(HistoryTextValue(old_notes)) item.new_value = Encoder.convert_to_dict(HistoryTextValue(new_notes)) self.repository.store_project_history_item(item) def log_campaign_name_updated(self, campaign: Campaign, old_name: str, new_name: str, user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.UPDATE_CAMPAIGN_NAME) item.project_id = campaign.project_id item.user_id = user_id item.campaign_id = campaign.id item.old_value = Encoder.convert_to_dict(HistoryTextValue(old_name)) item.new_value = Encoder.convert_to_dict(HistoryTextValue(new_name)) item.keywords = new_name self.repository.store_project_history_item(item) def log_claimed_project(self, project: Project, user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.CLAIM_PROJECT_OWNERSHIP) item.project_id = project.id item.user_id = user_id item.keywords = project.name self.repository.store_project_history_item(item) def log_campaign_approved(self, campaign: Campaign, user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.APPROVE_DSP_CAMPAIGN) item.project_id = campaign.project_id item.user_id = user_id item.campaign_id = campaign.id item.new_value = Encoder.convert_to_dict(HistoryBudgetValue(campaign.planned_budget)) item.keywords = campaign.planned_budget self.repository.store_project_history_item(item) def log_campaign_linkfire_changes(self, campaign: Campaign, old_value, new_value, user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.CHANGE_CAMPAIGN_LINKFIRE_LINKS) item.project_id = campaign.project_id item.user_id = user_id item.campaign_id = campaign.id item.old_value = Encoder.convert_to_dict(old_value) item.new_value = Encoder.convert_to_dict(new_value) self.repository.store_project_history_item(item) def log_project_linkfire_changes(self, project: Project, old_value, new_value, user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.CHANGE_PROJECT_LINKFIRE_LINKS) item.project_id = project.id item.user_id = user_id item.old_value = Encoder.convert_to_dict(old_value) item.new_value = Encoder.convert_to_dict(new_value) self.repository.store_project_history_item(item) def log_campaign_rejected(self, campaign: Campaign, project: Project, user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.REJECT_DSP_CAMPAIGN) item.project_id = project.id item.user_id = user_id item.campaign_id = campaign.id item.old_value = Encoder.convert_to_dict(HistoryBudgetValue(campaign.planned_budget)) item.keywords = campaign.planned_budget self.repository.store_project_history_item(item) def __unique_keywords(self, old_values: List[str], new_values: List[str]) -> str: uniq_names = set(old_values).symmetric_difference(set(new_values)) return " ".join(uniq_names) def log_media_plan_created(self, media_plan, user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.ADD_MEDIA_PLAN) item.project_id = media_plan.project_id item.user_id = user_id item.media_plan_id = media_plan.id item.new_value = Encoder.convert_to_dict(HistoryTextValue(media_plan.name)) item.keywords = media_plan.name self.repository.store_project_history_item(item) def log_media_plan_deleted(self, media_plan, user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.REMOVE_MEDIA_PLAN) item.project_id = media_plan.project_id item.user_id = user_id item.media_plan_id = media_plan.id item.old_value = Encoder.convert_to_dict(HistoryTextValue(media_plan.name)) item.keywords = media_plan.name self.repository.store_project_history_item(item) def log_media_plan_name_updated(self, media_plan, old_value, new_value, user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.UPDATE_MEDIA_PLAN_NAME) item.project_id = media_plan.project_id item.user_id = user_id item.media_plan_id = media_plan.id item.old_value = Encoder.convert_to_dict(HistoryTextValue(old_value)) item.new_value = Encoder.convert_to_dict(HistoryTextValue(new_value)) item.keywords = new_value self.repository.store_project_history_item(item) def log_media_plan_order_updated(self, media_plan, old_value, new_value, user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.UPDATE_MEDIA_PLAN_ORDER) item.project_id = media_plan.project_id item.user_id = user_id item.media_plan_id = media_plan.id item.old_value = Encoder.convert_to_dict(HistoryTextValue(old_value)) item.new_value = Encoder.convert_to_dict(HistoryTextValue(new_value)) item.keywords = new_value self.repository.store_project_history_item(item) def log_media_plan_phase_created( self, media_plan: MediaPlan, media_plan_phase: MediaPlanPhase, user_id: Optional[int] ): item = ProjectHistoryItemModel(HistoryActionType.ADD_PHASE) item.project_id = media_plan.project_id item.user_id = user_id item.media_plan_id = media_plan.id item.new_value = Encoder.convert_to_dict(media_plan_phase) item.keywords = media_plan_phase.name self.repository.store_project_history_item(item) def log_media_plan_phase_deleted( self, media_plan: MediaPlan, media_plan_phase, user_id: Optional[int] ): item = ProjectHistoryItemModel(HistoryActionType.REMOVE_PHASE) item.project_id = media_plan.project_id item.user_id = user_id item.media_plan_id = media_plan.id item.old_value = Encoder.convert_to_dict(media_plan_phase) item.keywords = media_plan_phase.name self.repository.store_project_history_item(item) def log_media_plan_phase_name_updated( self, media_plan: MediaPlan, old_value: str, new_value: str, user_id: Optional[int] ): item = ProjectHistoryItemModel(HistoryActionType.UPDATE_PHASE_NAME) item.project_id = media_plan.project_id item.user_id = user_id item.media_plan_id = media_plan.id item.old_value = Encoder.convert_to_dict(HistoryTextValue(old_value)) item.new_value = Encoder.convert_to_dict(HistoryTextValue(new_value)) item.keywords = new_value self.repository.store_project_history_item(item) def log_campaign_updated( self, campaign: Campaign, old_value: str, new_value: str, user_id: Optional[int] ): item = ProjectHistoryItemModel(HistoryActionType.CHANGE_CAMPAIGN) item.project_id = campaign.project_id item.user_id = user_id item.campaign_id = campaign.id item.media_plan_id = getattr(campaign.phase, "media_plan_id", None) item.old_value = Encoder.convert_to_dict(old_value) item.new_value = Encoder.convert_to_dict(new_value) self.repository.store_project_history_item(item) def log_approval_request_created(self, project_id, approval_request, user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.APPROVAL_REQUEST) item.user_id = user_id item.project_id = project_id item.media_plan_id = approval_request.media_plan_id item.new_value = Encoder.convert_to_dict(approval_request) self.repository.store_project_history_item(item) def log_submit_approval(self, project_id, media_plan_id, submit_approval_mask, user_id: Optional[int]): item = ProjectHistoryItemModel(HistoryActionType.SUBMIT_APPROVAL_REQUEST) item.user_id = user_id item.project_id = project_id item.media_plan_id = media_plan_id item.new_value = Encoder.convert_to_dict(submit_approval_mask) self.repository.store_project_history_item(item)