"""ensure-artists Lambda handler.""" from typing import Any from src import config # noqa: F401 -- initializes Sentry from src.client import ( DestinationArtistUpdate, TransferProduct, bulk_ensure_artists, get_transfer_job, get_transfer_job_products, set_destination_artists, update_transfer_job, ) def _job_id_from_event(event: dict[str, Any]) -> int: raw_job_id = event["job_id"] if isinstance(raw_job_id, bool): raise ValueError("event['job_id'] must be an integer.") try: return int(raw_job_id) except (TypeError, ValueError) as exc: raise ValueError("event['job_id'] must be an integer.") from exc def _unresolved_products(products: list[TransferProduct]) -> list[TransferProduct]: return [ product for product in products if product["destination_artist_id"] is None or (product["source_video_artist_id"] is not None and product["destination_video_artist_id"] is None) ] def _source_artist_ids(products: list[TransferProduct], job_id: int) -> list[int]: source_artist_ids: set[int] = set() missing_source_artist_release_ids: list[int] = [] for product in products: source_artist_id = product["source_artist_id"] if source_artist_id is None: missing_source_artist_release_ids.append(product["release_id"]) continue source_artist_ids.add(source_artist_id) source_video_artist_id = product["source_video_artist_id"] if source_video_artist_id is not None: source_artist_ids.add(source_video_artist_id) if missing_source_artist_release_ids: raise RuntimeError( f"Cannot ensure destination artists for job {job_id}; missing source_artist_id for releases " f"{missing_source_artist_release_ids}" ) return sorted(source_artist_ids) def _destination_artist_updates( products: list[TransferProduct], mapping: dict[str, int], job_id: int ) -> list[DestinationArtistUpdate]: updates: list[DestinationArtistUpdate] = [] for product in products: source_artist_id = product["source_artist_id"] if source_artist_id is None: raise RuntimeError( f"Cannot set destination artist for release {product['release_id']} on job {job_id}; " "source_artist_id is missing." ) destination_artist_id = mapping.get(str(source_artist_id)) if type(destination_artist_id) is not int: raise RuntimeError( f"bulk_ensure_artists returned no destination artist mapping for source artist {source_artist_id} " f"on job {job_id}" ) source_video_artist_id = product["source_video_artist_id"] destination_video_artist_id: int | None = None if source_video_artist_id is not None: destination_video_artist_id = mapping.get(str(source_video_artist_id)) if type(destination_video_artist_id) is not int: raise RuntimeError( f"bulk_ensure_artists returned no destination artist mapping for source video artist " f"{source_video_artist_id} on job {job_id}" ) updates.append( { "release_id": product["release_id"], "destination_artist_id": destination_artist_id, "destination_video_artist_id": destination_video_artist_id, } ) return updates def handler(event: dict[str, Any], context: object) -> dict[str, Any]: del context job_id = _job_id_from_event(event) config.logger.info(f"Ensuring destination artists for job {job_id}") job = get_transfer_job(job_id) destination_vendor_id = job["destination_vendor_id"] originating_artist_id = job["originating_artist_id"] if originating_artist_id is None: raise RuntimeError(f"Cannot ensure destination artists for job {job_id}: originating_artist_id is not set") products = get_transfer_job_products(job_id, destination_vendor_id) unresolved_products = _unresolved_products(products) if not unresolved_products: config.logger.info(f"All destination artists already resolved for job {job_id}") return { **event, "ensure_artists_result": { "requested_source_artist_ids": [], "artist_mapping": {}, "updated_count": 0, "skipped": True, }, } source_artist_ids = sorted(set(_source_artist_ids(unresolved_products, job_id)) | {originating_artist_id}) mapping = bulk_ensure_artists(source_artist_ids, destination_vendor_id) updates = _destination_artist_updates(unresolved_products, mapping, job_id) update_result = set_destination_artists(job_id, updates) destination_artist_id = mapping[str(originating_artist_id)] update_transfer_job(job_id, destination_artist_id) result = { "requested_source_artist_ids": source_artist_ids, "artist_mapping": mapping, "updated_count": update_result["updated_count"], "destination_artist_id": destination_artist_id, "skipped": False, } config.logger.info(f"Ensured destination artists for job {job_id}: {result}") return { **event, "ensure_artists_result": result, }