import re from constants.languages import LANGUAGE_CODE_MAP from constants.genres import GENRE_SUBGENRE_MAP from constants.territories import COUNTRY_NAMES from constants.country_codes import COUNTRY_CODES_3_TO_2 from datetime import datetime def convert_to_list(val): """Convert a string to a list.""" # Try to split the string by commas try: return val.split(',') except AttributeError: pass # Try to split the string by pipes try: return val.split('|') except AttributeError: pass # Fail if the value cannot be converted to a list raise ValueError('Could not convert the value to a list.') def validate_length(val, length=None): """Return a validation message if the value is not the specified length.""" if not length: length = 5 if not val: return try: if len(str(val)) < 5: return ('Invalid Length', 'This value is too short (>5)') except TypeError: return ('Invalid Length', 'This value cannot be checked for length.') return def validate_int(val): """Return a validation message if the value is not an integer.""" if not val: return try: int(val) except ValueError: return ('Invalid Type', 'This value is not an integer.') return def validate_constant(val, constant_list): """Return a validation message if the value is not in the constant list.""" if not val: return if val not in constant_list: return ('Invalid Value', 'This value is not in the list of valid ' 'values for this field.') return def validate_string(val): """Return a validation message if the value is not a string.""" if not val: return if not isinstance(val, str): return ('Invalid Type', 'This value is not a string.') return def validate_isrc(val): """Return a validation message if the value is not an ISRC.""" if not val: return if not re.match(r'^[A-Z]{2}[A-Z0-9]{3}\d{7}$', str(val)): return ('Invalid ISRC', 'This value is not an ISRC.') return def validate_date(val): """Return a validation message if the value is not a date in the format YYYY-MM-DD.""" if not val: return if isinstance(val, datetime): # convert to string fixed_val = val.strftime('%Y-%m-%d') return ( 'Invalid Date', 'This value is not a date in the format YYYY-MM-DD.', fixed_val ) if not re.match(r'^\d{4}-\d{2}-\d{2}$', str(val)): return ('Invalid Date', 'This value is not a date in the format ' '"YYYY-MM-DD".') return def validate_blank(val): """Return a validation message if the value is blank.""" if not val: return ('Blank Value', 'This value cannot be blank.') return def validate_url(val): """"Return a validation message if the value is not a URL.""" if not val: return if not re.match(r'^http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+$', str(val)): # noqa return ('Invalid URL', 'This value is not a URL.') return def validate_country(val): """Return a validation message if the value is not a valid country.""" if not val: return if val not in COUNTRY_NAMES: return ('Invalid Country', 'This value is not a valid country.') return def validate_country_code(val, type=None): """Return a validation message if the value is not a valid country code. Args: val (str): The value to validate. type (str): The type of country code to validate. Can be 'iso639_1' or 'iso639_2'. Returns: tuple: A tuple containing the validation message. """ if not val: return if not type: # set it to two-digit country codes type = 'iso639_1' # if type is 2 digit country codes if type.lower() in ['iso639_1', '2', 'two']: if val not in COUNTRY_CODES_3_TO_2.values(): return ('Invalid Language', 'This value is not a valid country ' 'code.') # elif type is 3 digit country codes elif type.lower() in ['iso639_2', '3', 'three']: if val not in COUNTRY_CODES_3_TO_2.keys(): return ('Invalid Language', 'This value is not a valid country ' 'code.') return def validate_country_code_list(val, type=None): """Return a validation message if the list of values is not comprised exclusively of valid country codes.""" if not val: return if not isinstance(val, list): try: val = convert_to_list(val) except ValueError: return ('Invalid Type', 'This value is not a list separated with ' 'pipes or commas.') for country_code in val: if validate_country_code(country_code, type): return ('Invalid Country Code', 'This list contains an invalid ' f'country code - {country_code}.') return def validate_yes_or_no(val, full_word=False): """Return a validation message if the value is not 'yes' or 'no'. Can return a correction. Args: val (str): The value to validate. Returns: tuple: A tuple containing the validation message. If the value can be corrected, the correction is also returned.""" if not val: return if full_word: fixed_y = 'Yes' fixed_n = 'No' else: fixed_y = 'Y' fixed_n = 'N' if val in [fixed_y, fixed_n]: return if val.lower() in ['yes', 'y', 'true', 't']: fixed_val = fixed_y return ( 'Invalid Value', f'This value must be either "{fixed_y}" or "{fixed_n}".', fixed_val ) if val.lower() in ['false', 'f', 'no', 'n']: fixed_val = fixed_n return ( 'Invalid Value', f'This value must be either "{fixed_y}" or "{fixed_n}".', fixed_val ) if val.upper() not in [fixed_n, fixed_y]: return ('Invalid Value', f'This value must be either "{fixed_y}" or "{fixed_n}".') return def validate_yes_no_clean(val): """Return a validation message if the value is not 'Yes', 'No', or 'Clean'.""" if not val: return if val.lower() in ['yes', 'no', 'y', 'n', 't', 'f', 'true', 'false']: return validate_yes_or_no(val, full_word=True) elif val.lower() == 'c': fixed_val = 'Clean' return ( 'Invalid Value', 'This value must be either "Yes", "No", or "Clean".', fixed_val ) elif val.lower() not in ['clean', 'c']: return ('Invalid Value', 'This value must be either "Yes", "No", or "Clean".') return def validate_language(val): """Return a validation message if the value is not a valid language.""" if not val: return if val not in LANGUAGE_CODE_MAP.keys(): return ('Invalid Language', 'This value is not a valid language.') return def validate_language_code(val): """Return a validation message if the value is not a valid language.""" if not val: return if val not in LANGUAGE_CODE_MAP.values(): return ('Invalid Language Code', 'This value is not a valid language code.') return def validate_genre(val): """Return a validation message if the value is not a valid genre.""" if not val: return if val not in GENRE_SUBGENRE_MAP.keys(): return ('Invalid Genre', 'This value is not a valid genre.') return def validate_subgenre(val): """Return a validation message if the value is not a valid subgenre.""" if not val: return for key in GENRE_SUBGENRE_MAP.keys(): if val in GENRE_SUBGENRE_MAP[key]: return return ('Invalid Subgenre', 'This value is not a valid subgenre.') def validate_upc(val): """Return a validation message if the value is not a valid UPC.""" if not val: return if not re.match(r'^\d{12}$', str(val)): return ('Invalid UPC', 'This value is not a valid UPC.') return def validate_p_line(val): """Return a validation message if the value is not a valid P Line.""" if not val: return try: if not re.match(r'^\d{4} .+$', str(val)): return ('Invalid P Line', 'This value is not a valid P Line.') except TypeError: return ('Invalid P Line', 'This value is not a valid P Line.') return def validate_c_line(val): """Return a validation message if the value is not a valid C Line.""" if not val: return if not re.match(r'^\d{4} .+$', str(val)): return ('Invalid C Line', 'This value is not a valid C Line.') return