"""TrackSpatial model to store spatial ISRC and other data for spatial assets.""" from oto import response import sqlalchemy from sqlalchemy import Column from sqlalchemy import DateTime from sqlalchemy import ForeignKey from sqlalchemy import func from sqlalchemy import Integer from sqlalchemy import String from backend.connectors import mysql from backend.constants import error from backend.models import track_additional_isrc class TrackSpatial(mysql.BaseModel): """Contains track spatial ISRC and other data. Used to store ISRC and other data when Spatial asset is uploading for track """ __tablename__ = 'track_spatial' track_id = Column( Integer, ForeignKey('track.id'), primary_key=True, nullable=False) isrc = Column('isrc', String(16), nullable=False) created_at = Column(DateTime, default=func.now()) updated_at = Column(DateTime, default=func.now(), onupdate=func.now()) deleted_at = Column(DateTime, nullable=True) def to_dict(self): """Convert the track spatial data to dict.""" return { 'track_id': self.track_id, 'isrc': self.isrc, 'created_at': self.created_at, 'updated_at': self.updated_at, 'deleted_at': self.deleted_at } def update(self, **kwargs): """Update object with kwargs data.""" for key, value in kwargs.items(): setattr(self, key, value) @mysql.wrap_db_errors @mysql.db_session_wrap def get_spatial_isrc_map_by_track_ids(track_ids, session): """Return a mapping of track_id to spatial ISRC for the given track ids. Args: track_ids (list): list of track primary keys session (object): SQLAlchemy database session (optional) Returns: Response: message is a dict of {track_id: isrc} """ if not track_ids: return response.Response(message={}) records = session.query(TrackSpatial.track_id, TrackSpatial.isrc).filter( TrackSpatial.track_id.in_(track_ids), TrackSpatial.deleted_at.is_(None) ).all() return response.Response(message={track_id: isrc for track_id, isrc in records}) @mysql.wrap_db_errors @mysql.db_session_wrap def get_track_spatial(tuid, session): """Get track spatial data by track id. Args: tuid (int): track primary key session (object): SQLAlchemy database session (optional) Returns: Response: track spatial object with its data. """ track_spatial = session.query(TrackSpatial).filter( TrackSpatial.track_id == tuid, TrackSpatial.deleted_at.is_(None) ).first() if not track_spatial: return response.create_not_found_response( error.ERROR_MESSAGE_TRACK_SPATIAL_NOT_FOUND) return response.Response(message=track_spatial.to_dict()) @mysql.wrap_db_errors @mysql.db_session_wrap def create_track_spatial(spatial_data, session): """Add a new track spatial data. Args: spatial_data (dict): dictionary of spatial data. session (object): SQLAlchemy database session (optional) Returns: Response: track spatial object with its data, or 409 if a record already exists for the given track_id. """ new_spatial = TrackSpatial() new_spatial.update(**spatial_data) session.add(new_spatial) try: # Flush the track_spatial insert by itself so that only its duplicate-PK # violation maps to 409. A constraint error from the mirror write below # is a real dual-write failure -- let it surface (and reach Sentry via # wrap_db_errors) rather than masquerade as "already exists". session.flush() except sqlalchemy.exc.IntegrityError: session.rollback() return response.create_error_response( code=error.ERROR_CODE_TRACK_SPATIAL_ALREADY_EXISTS, message=error.ERROR_MESSAGE_TRACK_SPATIAL_ALREADY_EXISTS, status=409) track_additional_isrc.mirror_create_or_restore(spatial_data, session) session.commit() return response.Response(message=new_spatial.to_dict()) @mysql.wrap_db_errors @mysql.db_session_wrap def update_track_spatial(spatial_data, session): """Upsert track spatial data: update when present, otherwise insert. Args: spatial_data (dict): dictionary of spatial data. session (object): SQLAlchemy database session (optional) Returns: Response: track spatial object with its data. """ track_spatial = session.merge(TrackSpatial(**spatial_data)) track_additional_isrc.mirror_create_or_restore( spatial_data, session, deleted_at=track_spatial.deleted_at) session.commit() return response.Response(message=track_spatial.to_dict()) @mysql.wrap_db_errors @mysql.db_session_wrap def delete_track_spatial(tuid, session): """Add a new track spatial data. Args: tuid (int): track primary key session (object): SQLAlchemy database session (optional) Returns: dict: track spatial object with it's data. """ track_spatial = session.query(TrackSpatial) \ .filter_by( track_id=tuid ).first() if not track_spatial: return response.create_not_found_response( error.ERROR_MESSAGE_TRACK_SPATIAL_NOT_FOUND) session.delete(track_spatial) track_additional_isrc.mirror_soft_delete(tuid, session) return response.Response()