"""Generic Validation functions. Do not import any logic controllers, models, or handlers. """ import re from oto import response from backend.constants import artist_role from backend.constants import error from backend.constants import field as field_const from backend.constants import validation from backend.utils import api as api_utils def is_list_unique_and_contiguous(number_list, starts_with=None): """Verify number_list is unique and contiguous.""" # Test uniqueness if len(number_list) != len(set(number_list)): return False, error.ERROR_NOT_UNIQUE_CODE # Test contiguous if max(number_list) - min(number_list) + 1 != len(number_list): return False, error.ERROR_SEQ_INTEGRITY_CODE # Optional test for minimum number start with if starts_with is not None: if min(number_list) != starts_with: return False, error.ERROR_SEQ_RANGE_CODE return True, '' def is_positive_int(number): """Verify number is a positive int.""" return isinstance(number, int) and number > 0 def is_pinfo(pline): """Verify string is 4 numbers, followed by characters. Also allow optional additional PYears, separated by ', ' """ regex = r'^(19|20)\d{2}(,\s(19|20)\d{2})*\s\S.*$' return bool(pline and re.match(regex, pline)) def is_meta_language_code_format(meta_language_code): """Verify meta_language_code has valid format. This doesn't check if the actual code is valid. """ return bool( meta_language_code and (re.fullmatch(r'^[A-Z]{3}|zh|cmn(-[A-Za-z]+)?$', meta_language_code) or meta_language_code == 'N/A')) def is_artist_valid(artist, subgenre=None): """Verify that the artist name is valid for the subgenre.""" if subgenre and (subgenre in validation.SOUNDTRACK_SUBGENRE): return is_valid_artist_name(artist['name']) return True def is_isrc(isrc): """Verify the string is an ISRC.""" return re.match( r'^[A-Za-z]{2}[-]?[0-9A-Za-z]{3}[-]?[0-9]{2}[-]?[0-9]{5}$', isrc) def is_vendor_track_id(vendor_id): """Verify the string is a vendor track id.""" return re.match(r'^(G0){1}[0-9A-Za-z]{,30}(?<=[^\s])$', vendor_id) def format_missing_error(field_name, error_code): """Return a uniform dict of errors.""" msg = error.VALIDATION_ERROR_MISSING_FIELD_MSG.format(field_name) return format_error( validator=error.REQUIRED_CODE, message=msg, error_code=error_code ) def format_error(validator, message, error_code, validator_value=True): """Format error in standard object format.""" return { field_const.VALIDATOR: validator, field_const.VALIDATOR_VALUE: validator_value, field_const.MESSAGE: message, field_const.ERROR_CODE: error_code } def is_artist_name_empty(name): """Check if the artist name is empty.""" return name.strip() == '' def is_valid_artist_name(name): """Return True if artist_name is valid. Args: name (str): The artist name to test Returns: bool: Is valid artist_name """ return name.strip().lower() not in validation.VARIOUS_ARTISTS def validate_artist_names(names): """Return True if artist_name is valid. Args: names (list): A list of artist names to test Returns: Response: Is valid artist_name """ for name in names: if not is_valid_artist_name(name): return api_utils.create_validation_error_response( error.INVALID_ARTIST_NAME_ERROR_MSG, name) return response.Response() def transform_to_performers_tuple(artists_list): """Return a tuple of performing artists within artists_list. Args: artists_list (list): List of artists within a track Returns: tuple: tuple of performer artist name """ performer_list = [] for artist in artists_list: if artist['type'] == 'performer': performer_list.append(artist['name']) return tuple(sorted(performer_list)) def is_artist_required(artist_type): """Return True if artist_type is required. Args: artist_type (str): The artist type to test Returns: bool: Is required artist_type """ return (artist_type not in artist_role.NON_REQUIRED_CLASSICAL_ARTIST_ROLES and # noqa artist_type not in artist_role.NON_REQUIRED_ARTIST_ROLES)