def is_tutorial_default_message(default_message, languages=None): """Validates the specified set of default messages. Args: default_message (dict): A set of default messages. languages (list): A list of available languages. Returns: <bool, str|None>: A pair containing the value True if the specified message set is valid, False otherwise; and an error message in case the set is invalid. Raises: TypeError: If the default_message argument is not a dictionary, or if the languages argument is not a list or NoneType. """ check_arg_type(is_tutorial_default_message, "default_message", default_message, dict) check_arg_type(is_tutorial_default_message, "languages", languages, (list, type(None))) unexpected_keys = find_unexpected_keys(default_message, is_tutorial_configuration.DEFAULT_MESSAGE_FIELDS) if unexpected_keys: message = "The tutorial's 'default-message' contains the following unrecognized fields: '{}'." return (False, message.format("', '".join(unexpected_keys))) for key, message in default_message.iteritems(): if not is_configuration_string(message, languages): return (False, "The tutorial's default message field '{}' is invalid. A message must be a non-empty or normalized string.".format(key)) return (True, None)
def is_question_input(question_input, languages=None): """Validates the specified input configuration. A question's input configuration is a non-empty dictionary comprised of different fields that define a question's input parameters. Args: question_input (dict): An input configuration to validate. Returns: <bool, str|None>: A pair containing the value True if the specified configuration is valid, False otherwise; as well as an error message in case it is invalid. Raises: TypeError: If the question_input argument is not a dictionary, or if languages is not a list or NoneType. """ check_arg_type(is_question_input, "question_input", question_input, dict) check_arg_type(is_question_input, "languages", languages, (list, type(None))) missing = [k for k in is_question_input.REQUIRED_FIELDS if k not in question_input or question_input[k] is None] if missing: message = "The question input configuration is missing the following fields: '{}'." return (False, message.format("', '".join(missing))) input_type = question_input["type"] valid, message = is_question_input_type(input_type) if not valid: return (False, message) validator = is_question_input.VALIDATORS.get(input_type) if not validator: return (False, "The question input type '{}' is not recognized.".format(input_type)) return validator(question_input, languages)
def is_question_branch(question_branch): """Validates the specified question branch. A branch is either a non-empty string which is a valid question or reserved key, or a dictionary where each possible question answer is mapped to a key. Args: question_branch (str|dict): A branch to validate. Returns: <bool, str|None>: A pair containing the value True if the branch is valid, False otherwise; as well as an error message in case it is invalid. Raises: TypeError: If the question_branch argument is not a string or dictionary. """ check_arg_type(is_question_branch, "question_branch", question_branch, (basestring, dict)) if isinstance(question_branch, basestring): if not __is_key(question_branch): return (False, "A question branch string must be a valid key, reserved or otherwise.") elif isinstance(question_branch, dict): if question_branch: if any(not __is_key(key) for key in question_branch.itervalues()): return (False, "A question branch string must be a valid key, reserved or otherwise.") else: return (False, "A question branch dictionary must contain at least one answer-key pair.") return (True, None)
def is_subjects(subjects): check_arg_type(is_subjects, "subjects", subjects, list) if not subjects: return (False, "A project tutorial must contain at least one subject.") for subject in subjects: valid, message = is_tutorial_subject(subject, available_languages) if not valid: return (False, message) return (True, None)
def is_configuration_set(configurations, enable_logging=False): """Validates the specified set of configurations. A configuration set must contain the project (project.json) and task presenter (task_presenter.json) configurations for a given GeoTag-X project. Optionally, a tutorial configuration (tutorial.json) may be included. A configuration set is considered valid if and only if each of its configurations is valid. Args: configurations (dict): A dictionary containing a set of configurations to validate. enable_logging (bool): If set to True, the function will log the operations it performs. Returns: <bool, str|None>: A pair containing the value True if the specified configuration set is valid, False otherwise; and an error message in case the set is invalid. Raises: TypeError: If the configurations argument is not a dictionary or enable_logging is not a boolean. ValueError: If a required configuration is missing from the configuration set. """ check_arg_type(is_configuration_set, "configurations", configurations, dict) check_arg_type(is_configuration_set, "enable_logging", enable_logging, bool) is_nonempty_dictionary = lambda d: isinstance(d, dict) and len(d) > 0 if not all( is_nonempty_dictionary(configurations.get(k)) for k in ["project", "task_presenter"]): raise ValueError( "A required configuration is missing from the specified configuration set." ) from functools import partial validators = { "project": is_project_configuration, "task_presenter": is_task_presenter_configuration, "tutorial": partial(is_tutorial_configuration, task_presenter_configuration=configurations["task_presenter"], validate_task_presenter_configuration=False), } for key, configuration in configurations.iteritems(): validator = validators[key] valid, message = validator(configuration, enable_logging=enable_logging) if not valid: return (False, message) return (True, None)
def is_tutorial_subject_assertion(tutorial_subject_assertion, languages=None): """Validates the specified tutorial subject assertion. Args: tutorial_subject_assertion (dict): A subject assertion to validate. languages (list): A list of available languages. Returns: <bool, str|NoneType>: A pair containing the value True if the specified assertion is valid, False otherwise; and an error message in case validation failed. Raises: TypeError: If the tutorial_subject_assertion argument is not a dictionary, or the languages argument is not a list or NoneType. """ check_arg_type(is_tutorial_subject_assertion, "tutorial_subject_assertion", tutorial_subject_assertion, dict) check_arg_type(is_tutorial_subject_assertion, "languages", languages, (list, type(None))) missing = [k for k in is_tutorial_subject_assertion.REQUIRED_FIELDS if k not in tutorial_subject_assertion] if missing: message = "A tutorial subject assertion is missing the following fields: '{}'." return (False, message.format("', '".join(missing))) def is_expects(assertion_expects): message = "A tutorial subject assertion's 'expects' field must be a non-empty string." return (False, message) if is_empty_string(assertion_expects) else (True, None) def is_messages(assertion_messages): check_arg_type(is_messages, "assertion_messages", assertion_messages, dict) if any(not is_configuration_string(m, languages) for m in assertion_messages.itervalues()): return (False, "A tutorial subject assertion message must be a non-empty or normalized string.") return (True, None) def is_autocomplete(assertion_autocomplete): message = "A tutorial subject assertion's 'autocomplete' field must contain a boolean value." return (True, None) if isinstance(assertion_autocomplete, bool) else (False, message) validators = { "expects": is_expects, "messages": is_messages, "autocomplete": is_autocomplete, } for key, field in tutorial_subject_assertion.iteritems(): validator = validators.get(key) if not validator: return (False, "The tutorial subject assertion field '{}' is not recognized.".format(key)) else: valid, message = validator(field) if not valid: return (False, message) return (True, None)
def is_question(question, available_languages=None): """Validates the specified question configuration. A valid question configuration is comprised of the following fields: - key: the question's unique identifier, - title: the question's title, - hint (optional): a short hint that may help clarify the question, - help (optional): a longer, more elaborate explanation of the question, - input: a configuration for the question's input, - branch (optional): a configuration that determines the next question depending on the answer to the this question. Args: question (dict): A question configuration to validate. available_languages (list): A list of available languages. Returns: <bool, str|None>: A pair containing the value True if the specified configuration is valid, False otherwise; as well as an error message in case it is invalid. Raises: TypeError: If the question argument is not a dictionary or available_languages is not a list or NoneType. """ check_arg_type(is_question, "question", question, dict) check_arg_type(is_question, "available_languages", available_languages, (list, type(None))) missing = [k for k in is_question.REQUIRED_FIELDS if k not in question or question[k] is None] if missing: message = "The question configuration is missing the following fields: '{}'." return (False, message.format("', '".join(missing))) from functools import partial validators = { "key": is_question_key, "title": partial(is_question_title, languages=available_languages), "hint": partial(is_question_help, languages=available_languages), "help": partial(is_question_help, languages=available_languages), "input": partial(is_question_input, languages=available_languages), "branch": is_question_branch, } for key, configuration in question.iteritems(): validator = validators.get(key) if not validator: return (False, "The question configuration key '{}' is not recognized.".format(key)) valid, message = validator(configuration) if not valid: return (False, message) return (True, None)
def is_assertions(subject_assertions): check_arg_type(is_assertions, "subject_assertions", subject_assertions, dict) from question import is_question_key for key, assertion in subject_assertions.iteritems(): valid, message = is_question_key(key) if not valid: return (False, message) valid, message = is_tutorial_subject_assertion(assertion, languages) if not valid: return (False, message) return (True, None)
def is_task_presenter_language(language): """Validates the specified language configuration. A valid language configuration is comprised of the following fields: - available: a list of available languages where each language is identified by a code, - default: a task presenter's default language, also identified by a unique code. The default language must also be a member of the list of available languages. Args: language (dict): A task presenter language configuration to validate. Returns: <bool, str|None>: A pair containing the value True if the specified configuration valid, False otherwise; as well as an error message in case it is invalid. Raises: TypeError: If the 'language' argument is not a dictionary. """ check_arg_type(is_task_presenter_language, "language", language, dict) missing = [ k for k in is_task_presenter_language.REQUIRED_FIELDS if k not in language ] if missing: message = "The task presenter's language configuration is missing the following fields: '{}'." return (False, message.format("', '".join(missing))) available_languages = language["available"] if not isinstance(available_languages, list) or len(available_languages) < 1: return ( False, "The list of available languages must be a non-empty list of language codes." ) invalid_language_codes = [ l for l in available_languages if not is_language_code(l) ] if invalid_language_codes: message = "The task presenter's list of available languages contains the following invalid codes: '{}'." return (False, message.format("', '".join(invalid_language_codes))) default_language = language["default"] if default_language not in available_languages: message = "The task presenter's default language '{}' is not listed as an available language." return (False, message.format(default_language)) return (True, None)
def is_project_configuration(configuration, enable_logging=False): """Validates the specified project configuration. Args: configuration (dict): A project configuration to validate. enable_logging (bool): If set to True, the function will log the operations it performs. Returns: <bool, str|None>: A pair containing the value True if the specified configuration is valid, False otherwise; and an error message in case the name is invalid. Raises: TypeError: If the configuration argument is not a dictionary or enable_logging is not a boolean. """ check_arg_type(is_project_configuration, "configuration", configuration, dict) check_arg_type(is_project_configuration, "enable_logging", enable_logging, bool) missing = [ k for k in is_project_configuration.REQUIRED_FIELDS if k not in configuration ] if missing: return ( False, "The project configuration is missing the following fields: '{}'.". format("', '".join(missing))) validators = { "name": is_project_name, "short_name": is_project_short_name, "description": is_project_description, "repository": is_project_repository, "do_not_track": is_project_do_not_track, } for key, configuration in configuration.iteritems(): validator = validators.get(key) if not validator: return (False, "The project configuration key '{}' is not recognized.". format(key)) valid, message = validator(configuration) if not valid: return (False, message) return (True, None)
def is_task_presenter_configuration(configuration, enable_logging=False): """Validates the specified task presenter configuration. Args: configuration (dict): A task presenter configuration to validate. enable_logging (bool): If set to True, the function will log the operations it performs. Returns: <bool, str|None>: A pair containing the value True if the specified configuration is valid, False otherwise; and an error message in case the name is invalid. Raises: TypeError: If the configuration argument is not a dictionary or enable_logging is not a boolean. """ check_arg_type(is_task_presenter_configuration, "configuration", configuration, dict) check_arg_type(is_task_presenter_configuration, "enable_logging", enable_logging, bool) missing = [ k for k in is_task_presenter_configuration.REQUIRED_FIELDS if k not in configuration ] if missing: message = "The task presenter configuration is missing the following fields: '{}'." return (False, message.format("', '".join(missing))) validators = { "language": is_task_presenter_language, "subject": is_task_presenter_subject, "questionnaire": is_task_presenter_questionnaire, } for key, configuration in configuration.iteritems(): validator = validators.get(key) if not validator: return ( False, "The task presenter configuration key '{}' is not recognized.". format(key)) valid, message = validator(configuration) if not valid: return (False, message) return (True, None)
def is_configuration_set(configurations, enable_logging=False): """Validates the specified set of configurations. A configuration set must contain the project (project.json) and task presenter (task_presenter.json) configurations for a given GeoTag-X project. Optionally, a tutorial configuration (tutorial.json) may be included. A configuration set is considered valid if and only if each of its configurations is valid. Args: configurations (dict): A dictionary containing a set of configurations to validate. enable_logging (bool): If set to True, the function will log the operations it performs. Returns: <bool, str|None>: A pair containing the value True if the specified configuration set is valid, False otherwise; and an error message in case the set is invalid. Raises: TypeError: If the configurations argument is not a dictionary or enable_logging is not a boolean. ValueError: If a required configuration is missing from the configuration set. """ check_arg_type(is_configuration_set, "configurations", configurations, dict) check_arg_type(is_configuration_set, "enable_logging", enable_logging, bool) is_nonempty_dictionary = lambda d: isinstance(d, dict) and len(d) > 0 if not all(is_nonempty_dictionary(configurations.get(k)) for k in ["project", "task_presenter"]): raise ValueError("A required configuration is missing from the specified configuration set.") from functools import partial validators = { "project": is_project_configuration, "task_presenter": is_task_presenter_configuration, "tutorial": partial(is_tutorial_configuration, task_presenter_configuration=configurations["task_presenter"], validate_task_presenter_configuration=False), } for key, configuration in configurations.iteritems(): validator = validators[key] valid, message = validator(configuration, enable_logging=enable_logging) if not valid: return (False, message) return (True, None)
def is_task_presenter_subject(subject): """Validates the specified subject configuration. A valid subject configuration is comprised of the following fields: - type: a string denoting the subject type. Args: subject (dict): A task presenter subject configuration to validate. Returns: <bool, str|None>: A pair containing the value True if the specified configuration is valid, False otherwise; as well as an error message in case it is invalid. Raises: TypeError: If the 'subject' argument is not a dictionary. """ check_arg_type(is_task_presenter_subject, "subject", subject, dict) missing = [ k for k in is_task_presenter_subject.REQUIRED_FIELDS if k not in subject ] if missing: message = "The task presenter's subject configuration is missing the following fields: '{}'." return (False, message.format("', '".join(missing))) validators = { "type": is_subject_type, } for key, configuration in subject.iteritems(): validator = validators.get(key) if not validator: return ( False, "The task presenter subject configuration key '{}' is not recognized." .format(key)) valid, message = validator(configuration) if not valid: return (False, message) return (True, None)
def is_task_presenter_language(language): """Validates the specified language configuration. A valid language configuration is comprised of the following fields: - available: a list of available languages where each language is identified by a code, - default: a task presenter's default language, also identified by a unique code. The default language must also be a member of the list of available languages. Args: language (dict): A task presenter language configuration to validate. Returns: <bool, str|None>: A pair containing the value True if the specified configuration valid, False otherwise; as well as an error message in case it is invalid. Raises: TypeError: If the 'language' argument is not a dictionary. """ check_arg_type(is_task_presenter_language, "language", language, dict) missing = [k for k in is_task_presenter_language.REQUIRED_FIELDS if k not in language] if missing: message = "The task presenter's language configuration is missing the following fields: '{}'." return (False, message.format("', '".join(missing))) available_languages = language["available"] if not isinstance(available_languages, list) or len(available_languages) < 1: return (False, "The list of available languages must be a non-empty list of language codes.") invalid_language_codes = [l for l in available_languages if not is_language_code(l)] if invalid_language_codes: message = "The task presenter's list of available languages contains the following invalid codes: '{}'." return (False, message.format("', '".join(invalid_language_codes))) default_language = language["default"] if default_language not in available_languages: message = "The task presenter's default language '{}' is not listed as an available language." return (False, message.format(default_language)) return (True, None)
def is_task_presenter_questionnaire(questionnaire, languages=None): """Validates the specified questionnaire configuration. A valid questionnanire configuration is comprised of the following fields: - questions: a list of question configurations. Args: questionnanire (dict): A task presenter questionnanire configuration to validate. languages (list): A list of available languages. Returns: <bool, str|None>: A pair containing the value True if the specified configuration is valid, False otherwise; as well as an error message in case it is invalid. Raises: TypeError: If the questionnaire argument is not a dictionary or languages is not a list or NoneType. """ check_arg_type(is_task_presenter_questionnaire, "questionnaire", questionnaire, dict) check_arg_type(is_task_presenter_questionnaire, "languages", languages, (list, type(None))) missing = [k for k in ["questions"] if k not in questionnaire] if missing: message = "The task presenter's questionnaire configuration is missing the following fields: '{}'." return (False, message.format("', '".join(missing))) questions = questionnaire["questions"] if not isinstance(questions, list) or len(questions) < 1: return (False, "A questionnaire must have a non-empty list of questions.") else: from question import is_question for q in questions: valid, message = is_question(q, languages) if not valid: return (False, message) return (True, None)
def is_project_configuration(configuration, enable_logging=False): """Validates the specified project configuration. Args: configuration (dict): A project configuration to validate. enable_logging (bool): If set to True, the function will log the operations it performs. Returns: <bool, str|None>: A pair containing the value True if the specified configuration is valid, False otherwise; and an error message in case the name is invalid. Raises: TypeError: If the configuration argument is not a dictionary or enable_logging is not a boolean. """ check_arg_type(is_project_configuration, "configuration", configuration, dict) check_arg_type(is_project_configuration, "enable_logging", enable_logging, bool) missing = [k for k in is_project_configuration.REQUIRED_FIELDS if k not in configuration] if missing: return (False, "The project configuration is missing the following fields: '{}'.".format("', '".join(missing))) validators = { "name": is_project_name, "short_name": is_project_short_name, "description": is_project_description, "repository": is_project_repository, "do_not_track": is_project_do_not_track, } for key, configuration in configuration.iteritems(): validator = validators.get(key) if not validator: return (False, "The project configuration key '{}' is not recognized.".format(key)) valid, message = validator(configuration) if not valid: return (False, message) return (True, None)
def is_task_presenter_configuration(configuration, enable_logging=False): """Validates the specified task presenter configuration. Args: configuration (dict): A task presenter configuration to validate. enable_logging (bool): If set to True, the function will log the operations it performs. Returns: <bool, str|None>: A pair containing the value True if the specified configuration is valid, False otherwise; and an error message in case the name is invalid. Raises: TypeError: If the configuration argument is not a dictionary or enable_logging is not a boolean. """ check_arg_type(is_task_presenter_configuration, "configuration", configuration, dict) check_arg_type(is_task_presenter_configuration, "enable_logging", enable_logging, bool) missing = [k for k in is_task_presenter_configuration.REQUIRED_FIELDS if k not in configuration] if missing: message = "The task presenter configuration is missing the following fields: '{}'." return (False, message.format("', '".join(missing))) validators = { "language": is_task_presenter_language, "subject": is_task_presenter_subject, "questionnaire": is_task_presenter_questionnaire, } for key, configuration in configuration.iteritems(): validator = validators.get(key) if not validator: return (False, "The task presenter configuration key '{}' is not recognized.".format(key)) valid, message = validator(configuration) if not valid: return (False, message) return (True, None)
def is_task_presenter_questionnaire(questionnaire, languages=None): """Validates the specified questionnaire configuration. A valid questionnanire configuration is comprised of the following fields: - questions: a list of question configurations. Args: questionnanire (dict): A task presenter questionnanire configuration to validate. languages (list): A list of available languages. Returns: <bool, str|None>: A pair containing the value True if the specified configuration is valid, False otherwise; as well as an error message in case it is invalid. Raises: TypeError: If the questionnaire argument is not a dictionary or languages is not a list or NoneType. """ check_arg_type(is_task_presenter_questionnaire, "questionnaire", questionnaire, dict) check_arg_type(is_task_presenter_questionnaire, "languages", languages, (list, type(None))) missing = [k for k in ["questions"] if k not in questionnaire] if missing: message = "The task presenter's questionnaire configuration is missing the following fields: '{}'." return (False, message.format("', '".join(missing))) questions = questionnaire["questions"] if not isinstance(questions, list) or len(questions) < 1: return (False, "A questionnaire must have a non-empty list of questions.") else: from question import is_question for q in questions: valid, message = is_question(q, languages) if not valid: return (False, message) return (True, None)
def is_task_presenter_subject(subject): """Validates the specified subject configuration. A valid subject configuration is comprised of the following fields: - type: a string denoting the subject type. Args: subject (dict): A task presenter subject configuration to validate. Returns: <bool, str|None>: A pair containing the value True if the specified configuration is valid, False otherwise; as well as an error message in case it is invalid. Raises: TypeError: If the 'subject' argument is not a dictionary. """ check_arg_type(is_task_presenter_subject, "subject", subject, dict) missing = [k for k in is_task_presenter_subject.REQUIRED_FIELDS if k not in subject] if missing: message = "The task presenter's subject configuration is missing the following fields: '{}'." return (False, message.format("', '".join(missing))) validators = { "type": is_subject_type, } for key, configuration in subject.iteritems(): validator = validators.get(key) if not validator: return (False, "The task presenter subject configuration key '{}' is not recognized.".format(key)) valid, message = validator(configuration) if not valid: return (False, message) return (True, None)
def is_messages(assertion_messages): check_arg_type(is_messages, "assertion_messages", assertion_messages, dict) if any(not is_configuration_string(m, languages) for m in assertion_messages.itervalues()): return (False, "A tutorial subject assertion message must be a non-empty or normalized string.") return (True, None)
def is_tutorial_subject(tutorial_subject, languages=None): """Validates the specified tutorial subject. Args: tutorial_subject (dict): A tutorial subject to validate. languages (list): A list of available languages. Returns: <bool, str|NoneType>: A pair containing the value True if the specified subject is valid, False otherwise; and an error message in case the subject is invalid. Raises: TypeError: If the tutorial_subject argument is not a dictionary, or the languages argument is not a list or NoneType. """ check_arg_type(is_tutorial_subject, "tutorial_subject", tutorial_subject, dict) check_arg_type(is_tutorial_subject, "languages", languages, (list, type(None))) missing = [k for k in is_tutorial_subject.REQUIRED_FIELDS if k not in tutorial_subject] if missing: message = "A tutorial's subject configuration is missing the following fields: '{}'." return (False, message.format("', '".join(missing))) def is_source(subject_source): message = "A tutorial subject's 'source' field must be a non-empty string." return (False, message) if is_empty_string(subject_source) else (True, None) def is_page(subject_page): message = "A tutorial subject's 'page' field must be a non-empty string." return (False, message) if is_empty_string(subject_page) else (True, None) def is_assertions(subject_assertions): check_arg_type(is_assertions, "subject_assertions", subject_assertions, dict) from question import is_question_key for key, assertion in subject_assertions.iteritems(): valid, message = is_question_key(key) if not valid: return (False, message) valid, message = is_tutorial_subject_assertion(assertion, languages) if not valid: return (False, message) return (True, None) validators = { "source": is_source, "page": is_page, "assertions": is_assertions, } for key, field in tutorial_subject.iteritems(): validator = validators.get(key) if not validator: return (False, "The tutorial subject field '{}' is not recognized.".format(key)) valid, message = validator(field) if not valid: return (False, message) return (True, None)
def is_tutorial_configuration( tutorial_configuration, task_presenter_configuration, enable_logging=False, validate_task_presenter_configuration=True ): """Validates the specified tutorial configuration. Args: tutorial_configuration (dict): A tutorial configuration to validate. task_presenter_configuration (dict): The task presenter configuration complemented by the tutorial configuration. enable_logging (bool): If set to True, the function will log the operations it performs. validate_task_presenter_configuration (bool): If set to True, the specified task presenter configuration is validated too. Returns: <bool, str|None>: A pair containing the value True if the specified configuration is valid, False otherwise; and an error message in case the configuration is invalid. Raises: TypeError: If either of the configuration arguments is not a dictionary, or the remaining arguments are not booleans. """ check_arg_type(is_tutorial_configuration, "tutorial_configuration", tutorial_configuration, dict) check_arg_type(is_tutorial_configuration, "task_presenter_configuration", task_presenter_configuration, dict) check_arg_type(is_tutorial_configuration, "enable_logging", enable_logging, bool) check_arg_type(is_tutorial_configuration, "validate_task_presenter_configuration", validate_task_presenter_configuration, bool) if validate_task_presenter_configuration: from task_presenter import is_task_presenter_configuration valid, message = is_task_presenter_configuration(task_presenter_configuration) if not valid: return (False, message) missing = [k for k in is_tutorial_configuration.REQUIRED_FIELDS if k not in tutorial_configuration] if missing: return (False, "The tutorial configuration is missing the following fields: '{}'.".format("', '".join(missing))) available_languages = task_presenter_configuration["language"]["available"] if "language" in task_presenter_configuration else None def is_subjects(subjects): check_arg_type(is_subjects, "subjects", subjects, list) if not subjects: return (False, "A project tutorial must contain at least one subject.") for subject in subjects: valid, message = is_tutorial_subject(subject, available_languages) if not valid: return (False, message) return (True, None) from functools import partial validators = { "enable-random-order": is_tutorial_enable_random_order, "default-message": partial(is_tutorial_default_message, languages=available_languages), "subjects": is_subjects, } for key, configuration in tutorial_configuration.iteritems(): validator = validators.get(key) if not validator: return (False, "The tutorial configuration key '{}' is not recognized.".format(key)) valid, message = validator(configuration) if not valid: return (False, message) return (True, None)