Exemplo n.º 1
0
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)
Exemplo n.º 2
0
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)
Exemplo n.º 3
0
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)
Exemplo n.º 4
0
 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)
Exemplo n.º 5
0
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)
Exemplo n.º 6
0
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)
Exemplo n.º 7
0
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)
Exemplo n.º 8
0
    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)
Exemplo n.º 9
0
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)
Exemplo n.º 10
0
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)
Exemplo n.º 11
0
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)
Exemplo n.º 12
0
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)
Exemplo n.º 13
0
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)
Exemplo n.º 15
0
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)
Exemplo n.º 16
0
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)
Exemplo n.º 20
0
 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)
Exemplo n.º 21
0
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)
Exemplo n.º 22
0
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)