Example #1
0
def _load_questions(node):
    """Load a list of questions.

    Args:
        node (List[dict]): list of question information, the key of each dict should be the question type,
            the value should be the content.

    Returns:
        List[Question]: list of question objects, parsed from the provided information
    """
    results = []
    exceptions = []
    for ind, question in enumerate(node):
        try:
            results.append(_load_question(question))
        except YbeLoadingError as ex:
            ex.question_ind = ind
            ex.question_id = list(question.values())[0].get('id')
            exceptions.append(ex)
            continue

    question_ids = [question.id for question in results]
    if len(question_ids) != len(set(question_ids)):
        duplicates = [item for item, count in collections.Counter(question_ids).items() if count > 1]
        exceptions.append(YbeLoadingError(f'There were multiple questions with the same id "{duplicates}"'))

    if len(exceptions):
        str(YbeMultipleLoadingErrors(exceptions))
        raise YbeMultipleLoadingErrors(exceptions)

    return results
Example #2
0
def _load_open_question(node):
    """Load the information of an open question.

    Args:
        node (dict): the question information

    Returns:
        ybe.lib.ybe_contents.OpenQuestion: the loaded question object, parsed from the provided information
    """
    exceptions = []

    try:
        basic_info = _load_question_basics(node)
    except YbeLoadingError as ex:
        exceptions.append(ex)

    try:
        options = OpenQuestionOptions(**node.get('options', {}))
    except YbeLoadingError as ex:
        exceptions.append(ex)

    if len(exceptions):
        raise YbeMultipleLoadingErrors(exceptions)

    return OpenQuestion(options=options, **basic_info)
Example #3
0
def _load_multiple_response(node):
    """Load the information of a multiple response question.

    Args:
        node (dict): the question information

    Returns:
        ybe.lib.ybe_contents.MultipleResponse: the loaded question object, parsed from the provided information
    """
    exceptions = []

    try:
        basic_info = _load_question_basics(node)
    except YbeLoadingError as ex:
        exceptions.append(ex)

    try:
        answers = _load_multiple_response_answers(node.get('answers', []))
    except YbeLoadingError as ex:
        exceptions.append(ex)

    if len(exceptions):
        raise YbeMultipleLoadingErrors(exceptions)

    return MultipleResponse(answers=answers, **basic_info)
Example #4
0
def _load_question_basics(node, points_must_be_set=True):
    """Load the basic information of an Ybe question.

    Args:
        node (dict): the question information
        points_must_be_set (boolean): if points must be set as a field for this question.

    Returns:
        dict: basic information for a Ybe question.
    """
    exceptions = []

    try:
        text = _load_text_from_node(node)
    except YbeLoadingError as ex:
        exceptions.append(ex)

    try:
        meta_data = _load_question_meta_data(node.get('meta_data', {}))
    except YbeLoadingError as ex:
        exceptions.append(ex)

    try:
        points = _load_points(node.get('points'), must_be_set=points_must_be_set)
    except YbeLoadingError as ex:
        exceptions.append(ex)

    if len(exceptions):
        raise YbeMultipleLoadingErrors(exceptions)

    return {'id': node.get('id'), 'text': text, 'meta_data': meta_data, 'points': points}
Example #5
0
def _load_meta_data_classification(node):
    """Load the classification meta data of a question.

    Args:
        node (dict): the content of the classification meta data node

    Returns:
        ybe.lib.ybe_contents.ClassificationQuestionMetaData: the question classification meta data
    """
    if not len(node):
        return ClassificationQuestionMetaData()

    exceptions = []

    related_concepts = node.get('related_concepts')
    if not (isinstance(related_concepts, list) or related_concepts is None):
        exceptions.append(YbeLoadingError(f'The value for ``meta_data.classification.related_concepts`` '
                                          f'should be a list, "{related_concepts}" given.'))

    skill_level = node.get('skill_level')
    skill_levels = ClassificationQuestionMetaData.available_skill_levels
    if skill_level not in skill_levels and skill_level is not None:
        exceptions.append(YbeLoadingError(f'The value for ``meta_data.classification.skill_level`` should be one of '
                                          f'"{skill_levels}", while "{skill_level}" was given.'))

    chapter = node.get('chapter')
    if not isinstance(chapter, int) and chapter is not None:
        exceptions.append(YbeLoadingError(f'The value for ``meta_data.classification.chapter`` should be an integer, '
                                          f'"{chapter}" was given.'))

    difficulty = node.get('difficulty')
    if (not isinstance(difficulty, int) or difficulty not in range(0, 11)) and difficulty is not None:
        exceptions.append(YbeLoadingError(f'The value for ``meta_data.classification.difficulty`` should be an '
                                          f'integer between [1-10], "{difficulty}" was given.'))

    if len(exceptions):
        raise YbeMultipleLoadingErrors(exceptions)

    return ClassificationQuestionMetaData(
        skill_level=skill_level,
        related_concepts=related_concepts,
        module=node.get('module'),
        chapter=chapter,
        difficulty=difficulty
    )
Example #6
0
def _load_text_only_question(node):
    """Load the information of text only question.

    Args:
        node (dict): the question information

    Returns:
        ybe.lib.ybe_contents.TextOnlyQuestion: the loaded question object, parsed from the provided information
    """
    exceptions = []

    try:
        basic_info = _load_question_basics(node, points_must_be_set=False)
    except YbeLoadingError as ex:
        exceptions.append(ex)

    if len(exceptions):
        raise YbeMultipleLoadingErrors(exceptions)

    return TextOnlyQuestion(**basic_info)
Example #7
0
    exceptions = []

    answers = []
    for ind, item in enumerate(node):
        content = item['answer']
        answers.append(MultipleChoiceAnswer(
            text=_load_text_from_node(content),
            correct=content.get('correct', False)
        ))

    if not (s := sum(answer.correct for answer in answers)) == 1:
        exceptions.append(YbeLoadingError(f'A multiple choice question must have exactly '
                                          f'1 answer marked as correct, {s} marked.'))

    if len(exceptions):
        raise YbeMultipleLoadingErrors(exceptions)

    return answers


def _load_multiple_response_answers(node):
    """Load all the answers of a multiple response question.

    Args:
        node (List[dict]): the list of answer items

    Returns:
        List[ybe.lib.ybe_contents.MultipleResponseAnswer]: the multiple reponse answers
    """
    exceptions = []