Пример #1
0
def read(request, key):
    if 'user' in request.GET:
        user = get_user_id(request)
    else:
        user = None
    item = int(request.GET['item']) if 'item' in request.GET else None
    item_secondary = int(request.GET['item_secondary']) if 'item_secondary' in request.GET else None
    time = get_time(request)
    environment = get_environment()
    if is_time_overridden(request):
        environment.shift_time(time)
    value = environment.read(key, user=user, item=item, item_secondary=item_secondary)
    if value is None:
        return render_json(
            request,
            {'error': 'value with key "%s" not found' % key},
            template='models_json.html', status=404)
    else:
        return render_json(
            request,
            {
                'object_type': 'value',
                'key': key,
                'item_primary_id': item,
                'item_secondary_id': item_secondary,
                'user_id': user,
                'value': value
            },
            template='models_json.html'
        )
Пример #2
0
def read(request, key):
    if 'user' in request.GET:
        user = get_user_id(request)
    else:
        user = None
    item = int(request.GET['item']) if 'item' in request.GET else None
    item_secondary = int(request.GET['item_secondary']
                         ) if 'item_secondary' in request.GET else None
    time = get_time(request)
    environment = get_environment()
    if is_time_overridden(request):
        environment.shift_time(time)
    value = environment.read(key,
                             user=user,
                             item=item,
                             item_secondary=item_secondary)
    if value is None:
        return render_json(request,
                           {'error': 'value with key "%s" not found' % key},
                           template='models_json.html',
                           status=404)
    else:
        return render_json(request, {
            'object_type': 'value',
            'key': key,
            'item_primary_id': item,
            'item_secondary_id': item_secondary,
            'user_id': user,
            'value': value
        },
                           template='models_json.html')
Пример #3
0
def audit(request, key):
    if 'user' in request.GET:
        user = get_user_id(request)
    else:
        user = None
    limit = 100
    if request.user.is_staff:
        limit = request.GET.get('limit', limit)
    item = int(request.GET['item']) if 'item' in request.GET else None
    item_secondary = int(request.GET['item_secondary']) if 'item_secondary' in request.GET else None
    time = get_time(request)
    environment = get_environment()
    if is_time_overridden(request):
        environment.shift_time(time)
    values = environment.audit(
        key, user=user, item=item, item_secondary=item_secondary, limit=limit)

    def _to_json_audit(audit):
        (time, value) = audit
        return {
            'object_type': 'value',
            'key': key,
            'item_primary_id': item,
            'item_secondary_id': item_secondary,
            'user_id': user,
            'value': value,
            'time': time.strftime('%Y-%m-%d %H:%M:%S')
        }
    return render_json(request, list(map(_to_json_audit, values)), template='models_json.html')
Пример #4
0
def options(request, json_list, nested):
    environment = get_environment()
    user_id = get_user_id(request)
    time = get_time(request)
    if is_time_overridden(request):
        environment.shift_time(time)
    item_selector = get_item_selector()
    option_selector = get_option_selector(item_selector)
    option_sets = get_option_set().get_option_for_flashcards([
        (question['payload'], question['question_type'])
        for question in json_list
        if question['payload']['object_type'] == 'fc_flashcard'
    ])
    metas = [question.get('meta', {}) for question in json_list]
    test_position = _test_index(metas)
    selected_items = [question['payload']['item_id'] for question in json_list
                      if question['payload']['object_type'] == 'fc_flashcard']
    allow_zero_option = {}
    for question in json_list:
        if question['payload']['object_type'] != 'fc_flashcard':
            continue
        if len(option_sets[question['payload']['item_id']]) == 0 and 'term_secondary' not in question['payload']:
            # If we do not have enough options, we have to force direction
            question['question_type'] = FlashcardAnswer.FROM_TERM
        disable_open_questions = False
        if question['payload']['disable_open_questions']:
            disable_open_questions = True
        elif question['payload']['restrict_open_questions']:
            disable_open_questions = question['question_type'] in {FlashcardAnswer.FROM_DESCRIPTION, FlashcardAnswer.FROM_TERM_TO_TERM_SECONDARY}
        allow_zero_option[question['payload']['item_id']] = question['question_type'] in {FlashcardAnswer.FROM_TERM, FlashcardAnswer.FROM_TERM_SECONDARY_TO_TERM} and not disable_open_questions

    all_options = {i: options for i, options in zip(selected_items, option_selector.select_options_more_items(
        environment, user_id, selected_items, time, option_sets,
        allow_zero_options=allow_zero_option
    ))}
    options_json_list = []
    # HACK: Here, we have to take into account reference questions with zero
    # options. In case of zero options we have to force a question type if the
    # restriction for zero options is enabled.
    config_zero_options_restriction = get_config('proso_models', 'options_count.parameters.allow_zero_options_restriction', default=False)
    for i, question in enumerate(json_list):
        if question['payload']['object_type'] != 'fc_flashcard':
            continue
        if test_position is not None and test_position == i:
            if 'term_secondary' not in question['payload'] and config_zero_options_restriction:
                question['question_type'] = FlashcardAnswer.FROM_TERM
            question['payload']['options'] = []
            continue
        options = all_options[question['payload']['item_id']]
        question['payload']['options'] = [Item.objects.item_id_to_json(o) for o in options]
        options_json_list += question['payload']['options']
    item2object(request, options_json_list, nested=True)
    for question in json_list:
        if question['payload']['object_type'] != 'fc_flashcard':
            continue
        sort_key = 'term_secondary' if question['question_type'] == FlashcardAnswer.FROM_TERM_TO_TERM_SECONDARY else 'term'
        question['payload']['options'] = sorted(question['payload']['options'], key=lambda o: o[sort_key]['name'])
Пример #5
0
def status(request):
    user_id = get_user_id(request)
    time = get_time(request)
    environment = get_environment()
    if is_time_overridden(request):
        environment.shift_time(time)
    return render_json(request, {
        'object_type': 'status',
        'number_of_answers': environment.number_of_answers(user=user_id),
        'number_of_correct_answers': environment.number_of_correct_answers(user=user_id),
        'environment_info': get_active_environment_info(),
    }, template='models_json.html')
Пример #6
0
def options(request, json_list, nested):
    environment = get_environment()
    user_id = get_user_id(request)
    time = get_time(request)
    if is_time_overridden(request):
        environment.shift_time(time)
    item_selector = get_item_selector()
    option_selector = get_option_selector(item_selector)
    option_sets = get_option_set().get_option_for_flashcards([
        question['payload'] for question in json_list
        if question['payload']['object_type'] == 'fc_flashcard'
    ])
    metas = [question.get('meta', {}) for question in json_list]
    test_position = _test_index(metas)
    selected_items = [question['payload']['item_id'] for question in json_list]
    allow_zero_option = {}
    for question in json_list:
        if question['payload']['object_type'] != 'fc_flashcard':
            continue
        if len(option_sets[question['payload']['item_id']]) == 0:
            # If we do not have enough options, we have to force direction
            question['question_type'] = FlashcardAnswer.FROM_TERM
        allow_zero_option[question['payload']['item_id']] = question[
            'question_type'] == FlashcardAnswer.FROM_TERM

    is_flashcard_question = [
        question['payload']['object_type'] == 'fc_flashcard'
        for question in json_list
    ]
    if not all(is_flashcard_question):
        # TODO: We should support mixed questions in the future
        raise Exception('All questions must be for flashcards!')

    all_options = option_selector.select_options_more_items(
        environment,
        user_id,
        selected_items,
        time,
        option_sets,
        allow_zero_options=allow_zero_option)
    options_json_list = []
    for i, (question, options) in enumerate(zip(json_list, all_options)):
        if test_position is not None and test_position == i:
            question['question_type'] = FlashcardAnswer.FROM_TERM
            question['payload']['options'] = []
            continue
        question['payload']['options'] = [
            Item.objects.item_id_to_json(o) for o in options
        ]
        options_json_list += question['payload']['options']
    item2object(request, options_json_list, nested=False)
Пример #7
0
def prediction(request, json_list, nested):
    if 'stats' not in request.GET:
        return
    object_item_ids = [x['item_id'] for x in json_list]
    user = get_user_id(request)
    time = get_time(request)
    predictions = _predictive_model().predict_more_items(_environment(request), user, object_item_ids, time)
    mastery_threshold = get_mastery_trashold()
    for object_json, prediction in zip(json_list, predictions):
        object_json['prediction'] = float("{0:.2f}".format(prediction))
        object_json['mastered'] = prediction >= mastery_threshold
    if "new_user_predictions" in request.GET:
        user = -1
        predictions = _predictive_model().predict_more_items(_environment(request), user, object_item_ids, time)
        for object_json, prediction in zip(json_list, predictions):
            object_json['new_user_prediction'] = float("{0:.2f}".format(prediction))
    return json_list
Пример #8
0
def status(request):
    user_id = get_user_id(request)
    time = get_time(request)
    environment = get_environment()
    if is_time_overridden(request):
        environment.shift_time(time)
    return render_json(request, {
        'object_type':
        'status',
        'number_of_answers':
        environment.number_of_answers(user=user_id),
        'number_of_correct_answers':
        environment.number_of_correct_answers(user=user_id),
        'environment_info':
        get_active_environment_info(),
    },
                       template='models_json.html')
Пример #9
0
def avg_prediction(request, json_list, nested):
    if 'stats' not in request.GET:
        return
    object_item_ids = [x['item_id'] for x in json_list]
    leaves = models.Item.objects.get_leaves(object_item_ids)
    all_leaves = list(set(flatten(leaves.values())))
    user = get_user_id(request)
    time = get_time(request)
    predictions = dict(list(zip(all_leaves, _predictive_model().predict_more_items(
        _environment(request),
        user,
        all_leaves,
        time
    ))))
    mastery_threshold = get_mastery_trashold()
    for object_json in json_list:
        leaf_predictions = [predictions[leave] for leave in leaves[object_json['item_id']]]
        object_json['avg_predicton'] = numpy.mean(leaf_predictions)
        object_json['mastered'] = sum([p > mastery_threshold for p in leaf_predictions])
Пример #10
0
def audit(request, key):
    if 'user' in request.GET:
        user = get_user_id(request)
    else:
        user = None
    limit = 100
    if request.user.is_staff:
        limit = request.GET.get('limit', limit)
    item_identifier = request.GET['item'] if 'item' in request.GET else None
    item_secondary_identifier = request.GET[
        'item_secondary'] if 'item_secondary' in request.GET else None
    translated = Item.objects.translate_identifiers([
        i
        for i in [item_identifier, item_secondary_identifier] if i is not None
    ], get_language(request))
    item = translated.get(item_identifier)
    item_secondary = translated.get(item_secondary_identifier)
    time = get_time(request)
    environment = get_environment()
    if is_time_overridden(request):
        environment.shift_time(time)
    values = environment.audit(key,
                               user=user,
                               item=item,
                               item_secondary=item_secondary,
                               limit=limit)

    def _to_json_audit(audit):
        (time, value) = audit
        return {
            'object_type': 'value',
            'key': key,
            'item_primary_id': item,
            'item_secondary_id': item_secondary,
            'user_id': user,
            'value': value,
            'time': time.strftime('%Y-%m-%d %H:%M:%S')
        }

    return render_json(request,
                       list(map(_to_json_audit, values)),
                       template='models_json.html')
Пример #11
0
def options(request, json_list, nested):
    environment = get_environment()
    user_id = get_user_id(request)
    time = get_time(request)
    if is_time_overridden(request):
        environment.shift_time(time)
    item_selector = get_item_selector()
    option_selector = get_option_selector(item_selector)
    option_sets = get_option_set().get_option_for_flashcards([
        (question['payload'], question['question_type'])
        for question in json_list
        if question['payload']['object_type'] == 'fc_flashcard'
    ])
    metas = [question.get('meta', {}) for question in json_list]
    test_position = _test_index(metas)
    selected_items = [
        question['payload']['item_id'] for question in json_list
        if question['payload']['object_type'] == 'fc_flashcard'
    ]
    allow_zero_option = {}
    for question in json_list:
        if question['payload']['object_type'] != 'fc_flashcard':
            continue
        if len(option_sets[question['payload']['item_id']]
               ) == 0 and 'term_secondary' not in question['payload']:
            # If we do not have enough options, we have to force direction
            question['question_type'] = FlashcardAnswer.FROM_TERM
        disable_open_questions = False
        if question['payload']['disable_open_questions']:
            disable_open_questions = True
        elif question['payload']['restrict_open_questions']:
            disable_open_questions = question['question_type'] in {
                FlashcardAnswer.FROM_DESCRIPTION,
                FlashcardAnswer.FROM_TERM_TO_TERM_SECONDARY
            }
        allow_zero_option[question['payload']
                          ['item_id']] = question['question_type'] in {
                              FlashcardAnswer.FROM_TERM,
                              FlashcardAnswer.FROM_TERM_SECONDARY_TO_TERM
                          } and not disable_open_questions

    all_options = {
        i: options
        for i, options in zip(
            selected_items,
            option_selector.select_options_more_items(
                environment,
                user_id,
                selected_items,
                time,
                option_sets,
                allow_zero_options=allow_zero_option))
    }
    options_json_list = []
    # HACK: Here, we have to take into account reference questions with zero
    # options. In case of zero options we have to force a question type if the
    # restriction for zero options is enabled.
    config_zero_options_restriction = get_config(
        'proso_models',
        'options_count.parameters.allow_zero_options_restriction',
        default=False)
    for i, question in enumerate(json_list):
        if question['payload']['object_type'] != 'fc_flashcard':
            continue
        if test_position is not None and test_position == i:
            if 'term_secondary' not in question[
                    'payload'] and config_zero_options_restriction:
                question['question_type'] = FlashcardAnswer.FROM_TERM
            question['payload']['options'] = []
            continue
        options = all_options[question['payload']['item_id']]
        question['payload']['options'] = [
            Item.objects.item_id_to_json(o) for o in options
        ]
        options_json_list += question['payload']['options']
    item2object(request, options_json_list, nested=True)
    for question in json_list:
        if question['payload']['object_type'] != 'fc_flashcard':
            continue
        sort_key = 'term_secondary' if question[
            'question_type'] == FlashcardAnswer.FROM_TERM_TO_TERM_SECONDARY else 'term'
        question['payload']['options'] = sorted(
            question['payload']['options'], key=lambda o: o[sort_key]['name'])
Пример #12
0
def _environment(request):
    environment = models.get_environment()
    if is_time_overridden(request):
        time = get_time(request)
        environment.shift_time(time)
    return environment
Пример #13
0
def practice(request):
    """
    Return the given number of questions to practice adaptively. In case of
    POST request, try to save the answer(s).

    GET parameters:
        filter:
            list of lists of identifiers (may be prefixed by minus sign to
            mark complement)
        language:
            language (str) of items
        avoid:
            list of item ids to avoid
        limit:
            number of returned questions (default 10, maximum 100)
        time:
            time in format '%Y-%m-%d_%H:%M:%S' used for practicing
        user:
            identifier for the practicing user (only for stuff users)
        stats:
            turn on the enrichment of the objects by some statistics
        html:
            turn on the HTML version of the API

    BODY:
        see answer resource
    """
    if request.user.id is None:  # Google Bot
        return render_json(
            request, {
                'error': _('There is no user available for the practice.'),
                'error_type': 'user_undefined'
            },
            status=400,
            template='models_json.html')

    limit = min(int(request.GET.get('limit', 10)), 100)
    # prepare
    user = get_user_id(request)
    time = get_time(request)
    avoid = load_query_json(request.GET, "avoid", "[]")
    practice_filter = get_filter(request)
    practice_context = PracticeContext.objects.from_content(practice_filter)
    environment = get_environment()
    item_selector = get_item_selector()
    if is_time_overridden(request):
        environment.shift_time(time)

    # save answers
    if request.method == 'POST':
        _save_answers(request, practice_context, False)
    elif request.method == 'GET':
        PracticeSet.objects.filter(answer__user_id=request.user.id).update(
            finished=True)

    if limit > 0:
        item_ids = Item.objects.filter_all_reachable_leaves(
            practice_filter, get_language(request))
        item_ids = list(set(item_ids) - set(avoid))
        limit_size = get_config('proso_models',
                                'practice.limit_item_set_size_to_select_from',
                                default=None)
        if limit_size is not None and limit_size < len(item_ids):
            item_ids = sample(item_ids, limit_size)
        if len(item_ids) == 0:
            return render_json(request, {
                'error':
                _('There is no item for the given filter to practice.'),
                'error_type':
                'empty_practice'
            },
                               status=404,
                               template='models_json.html')
        selected_items, meta = item_selector.select(environment,
                                                    user,
                                                    item_ids,
                                                    time,
                                                    practice_context.id,
                                                    limit,
                                                    items_in_queue=len(avoid))
        result = []
        for item, item_meta in zip(selected_items, meta):
            question = {
                'object_type': 'question',
                'payload': Item.objects.item_id_to_json(item),
            }
            if item_meta is not None:
                question['meta'] = item_meta
            result.append(question)
    else:
        result = []

    return render_json(request,
                       result,
                       template='models_json.html',
                       help_text=practice.__doc__)
Пример #14
0
def user_stats(request):
    """
    Get user statistics for selected groups of items

    time:
      time in format '%Y-%m-%d_%H:%M:%S' used for practicing
    user:
      identifier of the user (only for stuff users)
    username:
      username of user (only for users with public profile)
    filters:                -- use this or body
      json as in BODY
    mastered:
      use model to compute number of mastered items - can be slowed
    language:
      language of the items

    BODY
      json in following format:
      {
        "#identifier": []         -- custom identifier (str) and filter
        ...
      }
    """
    timer('user_stats')
    response = {}
    data = None
    if request.method == "POST":
        data = json.loads(request.body.decode("utf-8"))["filters"]
    if "filters" in request.GET:
        data = load_query_json(request.GET, "filters")
    if data is None:
        return render_json(request, {},
                           template='models_user_stats.html',
                           help_text=user_stats.__doc__)
    environment = get_environment()
    if is_time_overridden(request):
        environment.shift_time(get_time(request))
    user_id = get_user_id(request)
    language = get_language(request)
    filter_names, filter_filters = list(zip(*sorted(data.items())))
    reachable_leaves = Item.objects.filter_all_reachable_leaves_many(
        filter_filters, language)
    all_leaves = sorted(list(set(flatten(reachable_leaves))))
    answers = environment.number_of_answers_more_items(all_leaves, user_id)
    correct_answers = environment.number_of_correct_answers_more_items(
        all_leaves, user_id)
    if request.GET.get("mastered"):
        timer('user_stats_mastered')
        mastery_threshold = get_mastery_trashold()
        predictions = Item.objects.predict_for_overview(
            environment, user_id, all_leaves)
        mastered = dict(
            list(zip(all_leaves,
                     [p >= mastery_threshold for p in predictions])))
        LOGGER.debug(
            "user_stats - getting predictions for items took %s seconds",
            (timer('user_stats_mastered')))
    for identifier, items in zip(filter_names, reachable_leaves):
        if len(items) == 0:
            response[identifier] = {
                "filter": data[identifier],
                "number_of_items": 0,
            }
        else:
            response[identifier] = {
                "filter":
                data[identifier],
                "number_of_items":
                len(items),
                "number_of_practiced_items":
                sum(answers[i] > 0 for i in items),
                "number_of_answers":
                sum(answers[i] for i in items),
                "number_of_correct_answers":
                sum(correct_answers[i] for i in items),
            }
            if request.GET.get("mastered"):
                response[identifier]["number_of_mastered_items"] = sum(
                    mastered[i] for i in items)
    return render_json(request,
                       response,
                       template='models_user_stats.html',
                       help_text=user_stats.__doc__)
Пример #15
0
def practice(request):
    """
    Return the given number of questions to practice adaptively. In case of
    POST request, try to save the answer(s).

    GET parameters:
        filter:
            list of lists of identifiers (may be prefixed by minus sign to
            mark complement)
        language:
            language (str) of flashcards
        avoid:
            list of item ids to avoid
        limit:
            number of returned questions (default 10, maximum 100)
        time:
            time in format '%Y-%m-%d_%H:%M:%S' used for practicing
        user:
            identifier for the practicing user (only for stuff users)
        stats:
            turn on the enrichment of the objects by some statistics
        html:
            turn on the HTML version of the API

    BODY:
        see answer resource
    """
    if request.user.id is None:  # Google Bot
        return render_json(request, {
            'error': _('There is no user available for the practice.'),
            'error_type': 'user_undefined'
        }, status=400, template='models_json.html')

    limit = min(int(request.GET.get('limit', 10)), 100)
    # prepare
    user = get_user_id(request)
    time = get_time(request)
    avoid = load_query_json(request.GET, "avoid", "[]")
    practice_filter = get_filter(request)
    practice_context = PracticeContext.objects.from_content(practice_filter)
    environment = get_environment()
    item_selector = get_item_selector()
    if is_time_overridden(request):
        environment.shift_time(time)

    # save answers
    if request.method == 'POST':
        _save_answers(request, practice_context)

    if len(practice_filter) > 0:
        item_ids = Item.objects.filter_all_reachable_leaves(practice_filter, get_language(request))
    else:
        item_ids = Item.objects.get_all_available_leaves()
    item_ids = list(set(item_ids) - set(avoid))
    if len(item_ids) == 0:
        return render_json(request, {
            'error': _('There is no item for the given filter to practice.'),
            'error_type': 'empty_practice'
        }, status=404, template='models_json.html')
    selected_items, meta = item_selector.select(environment, user, item_ids, time, practice_context.id, limit, items_in_queue=len(avoid))
    result = []
    for item, item_meta in zip(selected_items, meta):
        question = {
            'object_type': 'question',
            'payload': Item.objects.item_id_to_json(item),
        }
        if item_meta is not None:
            question['meta'] = item_meta
        result.append(question)

    return render_json(request, result, template='models_json.html', help_text=practice.__doc__)
Пример #16
0
def user_stats(request):
    """
    Get user statistics for selected groups of items

    time:
      time in format '%Y-%m-%d_%H:%M:%S' used for practicing
    user:
      identifier of the user (only for stuff users)
    username:
      username of user (only for users with public profile)
    filters:                -- use this or body
      json as in BODY
    mastered:
      use model to compute number of mastered items - can be slowed
    language:
      language of the items

    BODY
      json in following format:
      {
        "#identifier": []         -- custom identifier (str) and filter
        ...
      }
    """
    timer('user_stats')
    response = {}
    data = None
    if request.method == "POST":
        data = json.loads(request.body.decode("utf-8"))["filters"]
    if "filters" in request.GET:
        data = load_query_json(request.GET, "filters")
    if data is None:
        return render_json(request, {}, template='models_user_stats.html', help_text=user_stats.__doc__)
    environment = get_environment()
    if is_time_overridden(request):
        environment.shift_time(get_time(request))
    user_id = get_user_id(request)
    language = get_language(request)
    filter_names, filter_filters = list(zip(*sorted(data.items())))
    reachable_leaves = Item.objects.filter_all_reachable_leaves_many(filter_filters, language)
    all_leaves = flatten(reachable_leaves)
    answers = dict(list(zip(all_leaves, environment.number_of_answers_more_items(all_leaves, user_id))))
    correct_answers = dict(list(zip(all_leaves, environment.number_of_correct_answers_more_items(all_leaves, user_id))))
    if request.GET.get("mastered"):
        timer('user_stats_mastered')
        mastery_threshold = get_mastery_trashold()
        predictions = get_predictive_model().predict_more_items(environment, user_id, all_leaves, get_time(request))
        mastered = dict(list(zip(all_leaves, [p >= mastery_threshold for p in predictions])))
        LOGGER.debug("user_stats - getting predictions for flashcards took %s seconds", (timer('user_stats_mastered')))
    for identifier, items in zip(filter_names, reachable_leaves):
        if len(items) == 0:
            response[identifier] = {
                "filter": data[identifier],
                "number_of_flashcards": 0,
            }
        else:
            response[identifier] = {
                "filter": data[identifier],
                "number_of_flashcards": len(items),
                "number_of_practiced_flashcards": sum(answers[i] > 0 for i in items),
                "number_of_answers": sum(answers[i] for i in items),
                "number_of_correct_answers": sum(correct_answers[i] for i in items),
            }
            if request.GET.get("mastered"):
                response[identifier]["number_of_mastered_flashcards"]= sum(mastered[i] for i in items)
    return render_json(request, response, template='models_user_stats.html', help_text=user_stats.__doc__)