from datetime import datetime, timezone from typing import TypeVar, List from flask import g from artists_moments.repository import ArtistsMomentsRepository from artists_moments.schemas import ( ArtistMomentResponseSchema, CreateArtistMomentRequestSchema, ArtistMomentCategoryResponseSchema, MomentMetadata, MomentEditableFields, ArtistMomentsGropedTypesResponseSchema, ) from models import Territory from models.artists import Artist from utils.exceptions import BadRequest, NotFound, Forbidden from services.artists_repository import ArtistsRepository from models.artist_moment import ArtistMoment from services.territory.territories_repository import TerritoriesRepository from utils.json_encoder import DataclassDictionaryEncoder as Encoder from utils.json_encoder import DataclassDictionaryDecoder as Decoder T = TypeVar("T") class ProjectArtistsMomentsService: moments_repository = ArtistsMomentsRepository() artists_repository = ArtistsRepository() territories_repository = TerritoriesRepository() def get_artists_moments_for_project(self, project_id: int): moments = self.moments_repository.get_artists_moments_for_project(project_id) return [ArtistMomentResponseSchema(moment) for moment in moments] def get_artists_moments_by_id(self, project_id: int, moment_id: int) -> ArtistMomentResponseSchema: moment = self.moments_repository.get_artists_moment_by_id(project_id, moment_id) if not moment: raise NotFound() return ArtistMomentResponseSchema(moment) def create_artist_moment_for_project( self, project_id: int, model: CreateArtistMomentRequestSchema ) -> ArtistMomentResponseSchema: self.__validate_moment_type(model) self.__validate_artist(project_id, model.artistId) artist = self.__get_artist(model) moment = ArtistMoment() moment.project_id = project_id moment.create_user_id = g.user_id self.__fill_moment_data(model, moment, artist) moment.metainfo = Encoder.convert_to_dict(MomentMetadata(fields=MomentEditableFields())) self.moments_repository.create_moment(moment) return ArtistMomentResponseSchema(moment) def update_artist_moment( self, project_id: int, moment_id: int, model: CreateArtistMomentRequestSchema ) -> ArtistMomentResponseSchema: self.__validate_moment_type(model) self.__validate_artist(project_id, model.artistId) moment = self.moments_repository.get_artists_moment_by_id(project_id, moment_id) if not moment: raise NotFound(f"Moment {moment_id} not found in project {project_id}") self.__validate_moment_edit(moment, model) artist = self.__get_artist(model) self.__fill_moment_data(model, moment, artist) moment.updated_at = datetime.now(timezone.utc) self.moments_repository.update_moment(moment) return ArtistMomentResponseSchema(moment) def delete_artist_moment(self, moment_id: int): self.moments_repository.delete_moment(moment_id) def get_moments_types(self): return [ArtistMomentsGropedTypesResponseSchema(model) for model in self.moments_repository.get_moments_types()] def get_moments_categories(self): return [ArtistMomentCategoryResponseSchema(model) for model in self.moments_repository.get_moments_categories()] def __get_artist(self, model): artist = self.artists_repository.get_artist_by_external_id(model.artistId) if not artist: raise NotFound(f"Artist {model.artistId} not found") return artist def __fill_moment_data(self, model: CreateArtistMomentRequestSchema, moment: ArtistMoment, artist: Artist): moment.artist_id = artist.id moment.name = model.name moment.type_id = model.typeId moment.category_id = model.categoryId moment.date = model.date moment.link = model.link moment.notes = model.notes moment.edit_user_id = g.user_id moment.territories = self.__get_territories(model.territoriesIds) def __check_permissions_or_raise(self, condition: bool, value: T) -> T: if not condition and value is not None: raise Forbidden() return value def __validate_moment_type(self, model: CreateArtistMomentRequestSchema): custom_type_selected = model.typeId is None and model.categoryId is not None regular_type_selected = model.typeId is not None and model.categoryId is None if not (regular_type_selected or custom_type_selected): raise BadRequest("Only type or category should be provided") def __validate_artist(self, project_id: int, artist_id: str): if not self.moments_repository.is_primary_artist_for_project(project_id, artist_id): raise Forbidden("You can't assign non-primary artist to this moment") def __get_territories(self, territories_ids: List[int]) -> List[Territory]: territories = self.territories_repository.get_territories_by_ids(territories_ids) if not territories: raise BadRequest(f"{territories_ids} are not valid territories") return territories def __validate_moment_edit(self, moment: ArtistMoment, params: CreateArtistMomentRequestSchema): permissions = Decoder.convert_from_dict(MomentMetadata, moment.metainfo) is_valid = all( [ permissions.fields.name or moment.name == params.name, permissions.fields.date or moment.date == params.date, permissions.fields.artist or moment.artist.external_id == params.artistId, permissions.fields.link or moment.link == params.link, permissions.fields.territories or set(t.id for t in moment.territories) == set(params.territoriesIds), permissions.fields.notes or moment.notes == params.notes, permissions.fields.type or moment.type_id == params.typeId, permissions.fields.category or moment.category_id == params.categoryId, ] ) if not is_valid: raise Forbidden()