"""Release Correction Detail Model.""" from datetime import datetime import json from oto.error import ERROR_CODE_INTERNAL_ERROR from oto.response import create_error_response, create_not_found_response from oto.response import Response from sqlalchemy import Column from sqlalchemy import DateTime from sqlalchemy import Enum from sqlalchemy import ForeignKey from sqlalchemy import Integer from sqlalchemy import String from sqlalchemy import Text from product_workflow.connectors.mysql import BaseModel from product_workflow.connectors.mysql import db_session from product_workflow.connectors.mysql import wrap_db_errors from product_workflow.models.release_correction import ReleaseCorrection class ReleaseCorrectionDetail(BaseModel): """Model for Release Correction Detail.""" __tablename__ = 'release_correction_detail' release_correction_detail_id = Column( Integer, primary_key=True, autoincrement=True, nullable=False) release_correction_id = Column( Integer, ForeignKey('release_correction.release_correction_id'), default=None ) table_name = Column(String(45), nullable=False) field_name = Column(String(45), default=None) key_id = Column(Integer, default=None) key_value = Column(Text, nullable=False) last_updated = Column(DateTime, nullable=False) last_updated_by = Column(Integer, nullable=False) last_updated_type = Column(Enum('oa', 'vendor'), default=None) def to_dict(self): """Return a dict representation of this release correction detail.""" try: structured_value = json.loads(self.key_value) if self.field_name in ['release_name', 'track_name', 'delivered_version']: structured_value = str(self.key_value) elif isinstance(structured_value, dict): structured_value = structured_value.get(str(self.key_id)) elif ( isinstance(structured_value, int) and self.field_name == 'track_name'): raise TypeError except (ValueError, TypeError): structured_value = self.key_value return { 'release_correction_detail_id': self.release_correction_detail_id, 'table_name': self.table_name, 'field_name': self.field_name, 'key_id': self.key_id, 'key_value': structured_value } @wrap_db_errors def upsert_many(correction_id, correction_details): """Create or update a correction detail records for each correction detail. Args: correction_id (str): unique identifier for the release correction. correction_details: List of correction details. Returns: Response containing the created release correction details data. """ def detail_identifier(detail): return '{}-{}-{}'.format( detail.table_name, detail.field_name, detail.key_id) with db_session() as session: correction = session\ .query(ReleaseCorrection)\ .get(correction_id) if not correction: return create_not_found_response( message='No release correction record found' ) correction_details = correction_details.get('items') if len(correction_details) == 0: return create_error_response( code=ERROR_CODE_INTERNAL_ERROR, message='Can not update correction details without data') created_or_updated_details = [] existing_details_map = { detail_identifier(detail): detail for i, detail in enumerate(correction.details) } correction.last_updated = datetime.now() detail = correction_details[0] correction.last_updated_by = detail.get('last_updated_by') correction.last_updated_type = detail.get('last_updated_type') for detail in correction_details: new_pk = detail.pop('release_correction_detail_id', None) if new_pk: return create_error_response( code=ERROR_CODE_INTERNAL_ERROR, message='release_correction_detail_id cannot be updated' ) new_fk = detail.pop('release_correction_id', None) if new_fk: return create_error_response( code=ERROR_CODE_INTERNAL_ERROR, message='release_correction_id cannot be updated' ) identifier = '{}-{}-{}'.format( detail.get('table_name'), detail.get('field_name'), detail.get('key_id')) key_value = detail.get('key_value') if isinstance(key_value, list): key_id = detail.get('key_id') detail['key_value'] = json.dumps({key_id: key_value}) elif detail.get('field_name') in ['coverart', 'track']: # The fields coverart and track save boolean values as strings. detail['key_value'] = json.dumps(key_value) elif detail['key_value'] is None: detail['key_value'] = '' if identifier in existing_details_map: correction_detail = existing_details_map[identifier] correction_detail.last_updated = datetime.now() fields = ['last_updated_by', 'last_updated_type', 'key_value'] for field in fields: setattr(correction_detail, field, detail.get(field)) created_or_updated_details.append(correction_detail) else: correction_detail = ReleaseCorrectionDetail( release_correction_id=correction.release_correction_id, table_name=detail.get('table_name'), field_name=detail.get('field_name'), key_id=detail.get('key_id'), key_value=detail.get('key_value'), last_updated=datetime.now(), last_updated_by=detail.get('last_updated_by'), last_updated_type=detail.get('last_updated_type') ) session.add(correction_detail) created_or_updated_details.append(correction_detail) session.flush() created_or_updated_details = [ detail.to_dict() for detail in created_or_updated_details] return Response(message={'items': created_or_updated_details}, status=201)