"""Audio Attributes persister. Handles doing CRUD operations on the track_audio_attributes table. """ from oto import response from backend.connectors import mysql from backend.constants import error from backend.models.audio_attribute import TrackAudioAttributes from backend.models.audio_attribute import TrackAudioAttributesEdits from backend.models.audio_attribute import TrackAudioAttributesChangelog from backend.models.audio_attribute import TrackAudioAttributeSuggestions class TrackAudioAttributePersister(object): """Handles high level operations for track audio attributes.""" @classmethod @mysql.db_session_wrap def sync_track_audio_attributes( cls, tracks, orchard_identity_id, session=None ): """Sync track audio attributes. Args: tracks: The list of tuid / audio attribute combinations to set orchard_identity_id: The orchard identity id session (object): SQLAlchemy database session (optional) Returns: response.Response: success message or database error """ try: for track in tracks: existing_audio_attributes = cls._get_audio_attributes_by_tuid( track['tuid'], session).with_for_update().all() cls._sync_audio_attributes( track['tuid'], existing_audio_attributes, track['track_attributes'], orchard_identity_id, session, track.get('change_source'), ) session.commit() return response.Response(status=200, message='success') except Exception as e: session.rollback() return response.create_error_response( code=error.TRACK_AUDIO_ATTRIBUTE_MODEL_ERROR, message=str(e)) @classmethod @mysql.db_session_wrap def sync_track_audio_attributes_edits(cls, tracks, session): """Sync track audio attributes edits. Args: tracks: The list of tuid / audio attribute combinations to set session (object): SQLAlchemy database session (optional) Returns: response.Response: success message or database error """ try: for track in tracks: existing_audio_attributes = cls._get_audio_attributes_edits_by_tuid( track['tuid'], session).with_for_update().all() cls._sync_audio_attributes_edits( track['tuid'], existing_audio_attributes, track['track_attributes'], session) session.commit() return response.Response(status=200, message='success') except Exception as e: session.rollback() return response.create_error_response( code=error.TRACK_AUDIO_ATTRIBUTE_EDITS_MODEL_ERROR, message=str(e)) @classmethod def _get_audio_attributes_by_tuid(cls, tuid, session): return session.query(TrackAudioAttributes) \ .filter(TrackAudioAttributes.unique_track_id == tuid) \ .order_by(TrackAudioAttributes.audio_attribute_id) @classmethod def _get_audio_attributes_edits_by_tuid(cls, tuid, session): return session.query(TrackAudioAttributesEdits) \ .filter(TrackAudioAttributesEdits.unique_track_id == tuid) \ .order_by(TrackAudioAttributesEdits.audio_attribute_id) @classmethod def _sync_audio_attributes( cls, tuid, existing_audio_attributes, new_audio_attribute_ids, orchard_identity_id, session, change_source): """Sync audio attributes for a track. Args: tuid (int): The track primary key. existing_audio_attributes (list): Currently set audio attributes for this track new_audio_attribute_ids (list): List of audio attribute IDs to set the track to orchard_identity_id: The orchard identity id session (object): SQLAlchemy database session change_source: The source of the change """ models_to_delete = [ existing_audio_attribute for existing_audio_attribute in existing_audio_attributes if existing_audio_attribute.audio_attribute_id not in new_audio_attribute_ids] existing_audio_attribute_ids = [ existing_audio_attribute.audio_attribute_id for existing_audio_attribute in existing_audio_attributes] audio_attribute_ids_to_create = [ new_audio_attribute_id for new_audio_attribute_id in new_audio_attribute_ids if new_audio_attribute_id not in existing_audio_attribute_ids] for audio_attribute_id_to_create in audio_attribute_ids_to_create: new_model = TrackAudioAttributes( unique_track_id=tuid, audio_attribute_id=audio_attribute_id_to_create) log_entry = TrackAudioAttributesChangelog( unique_track_id=tuid, audio_attribute_id=audio_attribute_id_to_create, user_uuid=orchard_identity_id, change_source=change_source, action='insert') session.add(new_model) session.add(log_entry) for model_to_delete in models_to_delete: log_entry = TrackAudioAttributesChangelog( unique_track_id=tuid, audio_attribute_id=model_to_delete.audio_attribute_id, user_uuid=orchard_identity_id, change_source=change_source, action='delete') session.delete(model_to_delete) session.add(log_entry) @classmethod def _sync_audio_attributes_edits( cls, tuid, existing_audio_attributes, new_audio_attribute_ids, session): """Sync audio attributes edits for a track. Args: tuid (int): The track primary key. existing_audio_attributes (list): Currently set audio attributes for this track new_audio_attribute_ids (list): List of audio attribute IDs to set the track to session (object): SQLAlchemy database session """ models_to_delete = [ existing_audio_attribute for existing_audio_attribute in existing_audio_attributes if existing_audio_attribute.audio_attribute_id not in new_audio_attribute_ids] existing_audio_attribute_ids = [ existing_audio_attribute.audio_attribute_id for existing_audio_attribute in existing_audio_attributes] audio_attribute_ids_to_create = [ new_audio_attribute_id for new_audio_attribute_id in new_audio_attribute_ids if new_audio_attribute_id not in existing_audio_attribute_ids] for audio_attribute_id_to_create in audio_attribute_ids_to_create: new_model = TrackAudioAttributesEdits( unique_track_id=tuid, audio_attribute_id=audio_attribute_id_to_create) session.add(new_model) for model_to_delete in models_to_delete: session.delete(model_to_delete) @classmethod @mysql.db_session_wrap def write_audio_attribute_suggestions( cls, unique_track_id, suggestions, user_uuid, review_queue_id, session=None ): """Insert a suggestion record for a track's suggested audio attributes. Args: unique_track_id (int): The track primary key. suggestions (list): The full suggestion items from the model. user_uuid (str): The user UUID from Orchard-Identity-Id header. review_queue_id (int): The review queue ID from the request. session (object): SQLAlchemy database session (optional) """ session.add(TrackAudioAttributeSuggestions( unique_track_id=unique_track_id, suggestions=suggestions, user_uuid=user_uuid, review_queue_id=review_queue_id, )) @classmethod @mysql.db_session_wrap def bulk_delete_track_audio_attributes(cls, tuids, session): """Delete track audio attributes for multiple tracks. Args: tuids (list): The track primary keys. session (object): SQLAlchemy database session """ rows_to_delete = session.query( TrackAudioAttributeSuggestions ).filter( TrackAudioAttributeSuggestions.unique_track_id.in_(tuids) ).all() for row in rows_to_delete: session.delete(row) rows_to_delete = session.query( TrackAudioAttributesEdits ).filter( TrackAudioAttributesEdits.unique_track_id.in_(tuids) ).all() for row in rows_to_delete: session.delete(row) rows_to_delete = session.query( TrackAudioAttributesChangelog ).filter( TrackAudioAttributesChangelog.unique_track_id.in_(tuids) ).all() for row in rows_to_delete: session.delete(row) rows_to_delete = session.query( TrackAudioAttributes ).filter( TrackAudioAttributes.unique_track_id.in_(tuids) ).all() for row in rows_to_delete: session.delete(row)