"""Validation functions for intra-row validations.""" from constants.genres import GENRE_SUBGENRE_MAP def all_or_none_filled(row, header, description, fields): """Ensure that all fields are filled or none are filled. Use Case: Ensure that all fields in the Performer 1 Type, Performer 1 Legal, and Performer 1 Main Role fields are filled or none are filled. Args: row (tuple): A tuple of openpyxl cell objects. header (list): The header row associated with the row. description (str): The description of the validation. fields (list): The fields to check. Returns: None or tuple: None if the validation passes, otherwise a tuple containing the error code and error message. """ if not description: description = 'All-or-None Filled' # Offset the column index by 1 to match the 0-based index of the row tuple values = [cell.value for cell in row if header[cell.column-1] in fields] all_filled = all(value != '' and value is not None for value in values) none_filled = all(value == '' or value is None for value in values) if all_filled or none_filled: return else: return ( description, f'Row {row[0].row}: ' 'All fields must be filled or none must be filled.' ) def conditional_blank( row, header, description, conditional_field, trigger_values, dependent_field): """Ensure that the dependent field is blank when the conditional field has specific trigger values. Args: row (tuple): A tuple of openpyxl cell objects. header (list): The header row associated with the row. description (str): The description of the validation. conditional_field (str): The field to check for trigger values. trigger_values (list): The trigger values. dependent_field (str): The field that should be blank. Returns: None or tuple: None if the validation passes, otherwise a tuple containing the error code and error message. """ if not description: description = 'Conditional Blank' # Map the field names to their respective indices conditional_field_index = header.index(conditional_field) dependent_field_index = header.index(dependent_field) if not trigger_values: trigger_values = [None, ''] # Check the conditions and return the appropriate result if row[conditional_field_index].value in trigger_values \ and not row[dependent_field_index].value: return ( description, f'Row {row[0].row}: ' f'The field {dependent_field} must be blank when ' f'{conditional_field} is {row[conditional_field_index].value}.' ) return def conditional_blank_list( row, header, description, conditional_field, trigger_values, dependent_fields): """Ensure that the dependent fields are blank when the conditional field has specific trigger values. Args: row (tuple): A tuple of openpyxl cell objects. header (list): The header row associated with the row. description (str): The description of the validation. conditional_field (str): The field to check for trigger values. trigger_values (list): The trigger values. dependent_fields (list): The list of fields that should be blank. Returns: None or tuple: None if the validation passes, otherwise a tuple containing the error code and error message. """ if not description: description = 'Conditional Blank List' # Loop through the dependent fields and check if they are not blank for dependent_field in dependent_fields: error = conditional_blank( row, header, description, conditional_field, trigger_values, dependent_field) if error: return (description, f'Row {row[0].row}: ' f'The fields {dependent_fields} must be blank when ' f'{conditional_field} is ' f'{row[header.index(conditional_field)].value}.' ) return def conditional_in_list(row, header, description, conditional_field, trigger_values, dependent_field, dependent_values): """Ensure that a dependent field is within a certain list of values when a conditional field is within a certain list of values. Args: row (tuple): A tuple of openpyxl cell objects. header (list): The header row associated with the row. description (str): The description of the validation. conditional_field (str): The field to check for trigger values. trigger_values (list): The trigger values. dependent_field (str): The field that should be within the list of dependent values. dependent_values (list): The list of values that the dependent field should be within. Returns: None or tuple: None if the validation passes, otherwise a tuple containing the error code and error message. """ if not description: description = 'Conditional In List' # Map the field names to their respective indices conditional_field_index = header.index(conditional_field) dependent_field_index = header.index(dependent_field) # Check the conditions and return the appropriate result if row[conditional_field_index].value in trigger_values \ and row[dependent_field_index].value not in dependent_values: return ( description, f'Row {row[0].row}: ' f'The field {dependent_field} must be within the list of values ' f'{dependent_values} when {conditional_field} is ' f'{row[conditional_field_index].value}.' ) return def conditional_filled( row, header, description, conditional_field, trigger_values, dependent_fields): """Ensure that the dependent fields are filled when the conditional field has specific trigger values. Use Case: Ensure that the Track Artist(s) - Composer(s) field and the Track Artist(s) - Conductors field are both filled when the Genre field is Classical, Soundtracks, or World Music. Args: row (tuple): A tuple of openpyxl cell objects. header (list): The header row associated with the row. description (str): The description of the validation. conditional_field (str): The field to check for trigger values. trigger_values (list): The trigger values. dependent_fields (list): The fields that should be filled. Returns: None or tuple: None if the validation passes, otherwise a tuple containing the error code and error message. """ if not description: description = 'Conditional Filled' # Map the field names to their respective indices conditional_field_index = header.index(conditional_field) dependent_field_indices = \ [header.index(field) for field in dependent_fields] # Check the conditions and return the appropriate result if row[conditional_field_index].value in trigger_values: for dependent_field_index in dependent_field_indices: if not row[dependent_field_index].value: return ( description, f'Row {row[0].row}: ' f'The fields {dependent_fields} must be filled when ' f'{conditional_field} is ' f'{row[conditional_field_index].value}.' ) return def at_least_one_exists(row, header, description, fields, required_values): """Ensure that at least one of the fields has one of the required values. Use Case: Ensure that at least one of the fields 'Performer 1 Type', 'Performer 2 Type', 'Performer 3 Type', 'Performer 4 Type', or 'Performer 5 Type' has 'Primary Performer' as a value. Args: row (tuple): A tuple of openpyxl cell objects. header (list): The header row associated with the row. description (str): The description of the validation. fields (list): The fields to check. required_values (list): The required values. Returns: None or tuple: None if the validation passes, otherwise a tuple containing the error code and error message. """ if not description: description = 'At Least One Exists' for field in fields: field_index = header.index(field) if row[field_index].value in required_values: return return ( 'At Least One Exists', f'Row {row[0].row}: ' f'At least one of the fields {fields} must have one of the values ' f'{required_values}.' ) def valid_date_range(row, header, description, start_date_field, end_date_field): """Ensure that the start date is before or on the end date. Use Case: Ensure the Release Date is before or on the Sales Start Date. Args: row (tuple): A tuple of openpyxl cell objects. header (list): The header row associated with the row. description (str): The description of the validation. start_date_field (str): The start date field. end_date_field (str): The end date field. Returns: None or tuple: None if the validation passes, otherwise a tuple containing the error code and error message. """ if not description: description = 'Valid Date Range' start_date_index = header.index(start_date_field) end_date_index = header.index(end_date_field) # Not our clowns, not our rodeo if not row[start_date_index].value or not row[end_date_index].value: return if row[start_date_index].value <= row[end_date_index].value: return else: return ( description, f'Row {row[0].row}: ' f'The {start_date_field} must be before or on the ' f'{end_date_field}.' ) def validate_subgenre_to_genre(row, header, description, genre_field, subgenre_field): """Ensure that the subgenre is valid for the genre. Use Case: Ensure that the subgenre is valid for the genre. Args: row (tuple): A tuple of openpyxl cell objects. header (list): The header row associated with the row. description (str): The description of the validation. genre_field (str): The genre field. subgenre_field (str): The subgenre field. Returns: None or tuple: None if the validation passes, otherwise a tuple containing the error code and error message. """ if not description: description = 'Check that Subgenre is Valid to Genre' genre_index = header.index(genre_field) subgenre_index = header.index(subgenre_field) genre = row[genre_index].value subgenre = row[subgenre_index].value if genre is None or subgenre is None: return if genre not in GENRE_SUBGENRE_MAP.keys(): return ( description, f'Row {row[0].row}: ' f'The genre {genre} is not valid. Fix this before proceeding.' ) if subgenre not in GENRE_SUBGENRE_MAP[genre]: return ( description, f'Row {row[0].row}: ' f'The subgenre {subgenre} is not valid for the genre {genre}.' ) return