from db import db from typing import List from datetime import date from dataclasses import dataclass from utils.list_utils import first_or_none from workers.base_worker import BaseWorker from utils.snowflake.gras_data.moments_importer import ArtistMomentsImporter from utils.snowflake.gras_data.constants import MomentTypes from utils.snowflake.gras_data.models import GRASArtistMomentModel from models.artist_moment import ArtistMoment from models.territories import Territory from artists_moments.repository import ArtistsMomentsRepository from artists_moments.schemas import MomentMetadata, MomentEditableFields from utils.json_encoder import DataclassDictionaryEncoder as Encoder from utils.reporting.worker_logger import WorkerLogger @dataclass class ArtistBindingModel: decibel_id: int external_id: str def __hash__(self): return hash(f"{self.external_id}{str(self.decibel_id)}") def __eq__(self, other): return self.decibel_id == other.decibel_id and self.external_id == other.external_id def assign_editable_fields(): return Encoder.convert_to_dict( MomentMetadata(fields=MomentEditableFields(date=False, artist=False, type=False, category=False)) ) class ArtistMomentWorker(BaseWorker): artist_moment_repository = ArtistsMomentsRepository() logger: WorkerLogger = WorkerLogger() __single_moment_types__ = { MomentTypes.DEATH.value: 1, MomentTypes.SINGLE_RELEASE.value: 19, MomentTypes.ALBUM_RELEASE.value: 20, MomentTypes.VIDEO_RELEASE.value: 21, MomentTypes.CULTURE_REACTIVE_SYNC.value: 15, } __anniversary_moment_types__ = {MomentTypes.DEATH.value: 17, MomentTypes.BIRTH.value: 18} __anniversary_release__ = 16 __max_anniversary_years__ = 70 __anniversary_years_step__ = 5 def execute(self, artists: List[ArtistBindingModel]): global_territory = db.session.query(Territory).filter(Territory.id == 0).one() gras_ids = [self.__parse_gras_id(artist.external_id) for artist in artists] moments = ArtistMomentsImporter().fetch(gras_ids) for m in moments: artist_record = first_or_none(artists, lambda x: x.external_id == f"GRAS_{m.artist_external_id}") for moment in self.__map_moments(raw_moment=m): persisted_moment = ( db.session.query(ArtistMoment) .filter(ArtistMoment.external_key == moment.external_key) .one_or_none() ) if persisted_moment: continue moment.artist_id = artist_record.decibel_id moment.territories = [global_territory] db.session.add(moment) db.session.flush() db.session.commit() db.session.close() def __parse_gras_id(self, external_id: str) -> str: return external_id.replace("GRAS_", "") def __map_moments(self, raw_moment: GRASArtistMomentModel) -> List[ArtistMoment]: moments = [] name = self.__generate_moment_name(raw_moment) if raw_moment.moment_type != MomentTypes.BIRTH.value: type_id = self.__single_moment_types__.get(raw_moment.moment_type) moments.append(self.__assign_moment_model(raw_moment.artist_external_id, name, type_id, raw_moment.date)) for m_date in self.__generate_anniversery_timeline(raw_moment.date, self.__anniversary_years_step__): moments.append( self.__assign_moment_model( artist_external_id=raw_moment.artist_external_id, name=name, type_id=self.__anniversary_moment_types__.get(raw_moment.moment_type, self.__anniversary_release__), m_date=m_date, ) ) return moments def __assign_moment_model(self, artist_external_id: int, name: str, type_id: int, m_date: date) -> ArtistMoment: external_key = f"{artist_external_id}-{name}-{type_id}-{m_date.strftime('%Y-%m-%d')}" return ArtistMoment( name=name, type_id=type_id, date=m_date, external_key=external_key, metainfo=assign_editable_fields() ) def __generate_anniversery_timeline(self, s_date: date, years_step: int) -> List[date]: current_year = date.today().year years_past = min(current_year - s_date.year, self.__max_anniversary_years__) dates_arr = [] for i in range(int(years_past / years_step)): anniversary_year = s_date.year + (i + 1) * years_step try: dates_arr.append(self.__replace_date(s_date, anniversary_year)) except ValueError: self.logger.error(f"Year {anniversary_year} can't be replaced for date: ", f"{s_date}") continue return list(filter(lambda v: v is not None, dates_arr)) def __replace_date(self, s_date: date, year: int) -> date: try: return s_date.replace(year=year) except ValueError: return s_date.replace(year=year, day=s_date.day - 1) def __generate_moment_name(self, raw_moment: GRASArtistMomentModel) -> str: if raw_moment.moment_type == MomentTypes.DEATH.value: return f"{raw_moment.title} Death" elif raw_moment.moment_type == MomentTypes.BIRTH.value: return f"{raw_moment.title} Birth" return raw_moment.title