def test_get_distribution(db_conn): create_card_test_data(db_conn) params = { 'entity_id': card_b_uuid, } card_params = get_card_parameters(db_conn, params) dist = get_distribution(card_params, kind='guess') assert dist params = { 'entity_id': card_a_uuid, } card_params = get_card_parameters(db_conn, params) or {} dist = get_distribution(card_params, kind='slip') assert dist
def test_get_slip(db_conn): create_card_test_data(db_conn) params = { 'entity_id': card_b_uuid, } card_params = get_card_parameters(db_conn, params) assert 0.27 < get_slip(card_params) < 0.28
def test_get_guess(db_conn): create_card_test_data(db_conn) params = { 'entity_id': card_b_uuid, } card_params = get_card_parameters(db_conn, params) assert get_guess(card_params) == 0.41
def get_card_route(request, card_id): """ Get a specific card given an ID. Show all relevant data, but not used for the learning interface. """ db_conn = request['db_conn'] card = get_card_by_kind(db_conn, card_id) if not card: return abort(404) unit = Unit.get_latest_accepted(db_conn, entity_id=card['unit_id']) if not unit: return abort(404) # TODO-2 SPLITUP create new endpoints for these instead topics = list_topics_by_entity_id(card_id, {}, db_conn) versions = Card.get_versions(db_conn, entity_id=card_id) requires = Card.list_requires(db_conn, entity_id=card_id) required_by = Card.list_required_by(db_conn, entity_id=card_id) params = get_card_parameters({'entity_id': card_id}, db_conn) return 200, { 'card': card.deliver(access='view'), 'card_parameters': (get_card_parameters_values(params) if params else None), 'unit': unit.deliver(), 'topics': [deliver_topic(topic) for topic in topics], 'versions': [version.deliver() for version in versions], 'requires': [require.deliver() for require in requires], 'required_by': [require.deliver() for require in required_by], }
def get_card_route(request, card_id): """ Get a specific card given an ID. Show all relevant data, but not used for the learning interface. """ db_conn = request['db_conn'] card = get_latest_accepted_card(db_conn, card_id) if not card: return abort(404, 'fPebfwqfRNmOiSWqWISeaA') unit = get_latest_accepted_unit(db_conn, card['unit_id']) if not unit: return abort(404, 'IKSqfvHvRK6hbSAIOkQuLg') # TODO-2 SPLITUP create new endpoints for these instead requires = list_required_cards(db_conn, card_id) required_by = list_required_by_cards(db_conn, card_id) params = get_card_parameters(db_conn, {'entity_id': card_id}) return 200, { 'card': deliver_card(card, access='view'), 'card_parameters': (get_card_parameters_values(params) if params else None), 'unit': deliver_unit(unit), 'requires': [deliver_card(require) for require in requires], 'required_by': [deliver_card(require) for require in required_by], }
def choose_card(db_conn, user, unit): """ Given a user and a unit, choose an appropriate card. Return a card instance. """ # TODO-3 simplify this method unit_id = unit['entity_id'] query = (Card.start_accepted_query().filter({ 'unit_id': unit_id }).sample(10)) # TODO-2 index # TODO-3 does this belong as a model method? # TODO-2 is the sample value decent? # TODO-2 has the learner seen this card recently? cards = [Card(d) for d in query.run(db_conn)] if not len(cards): return None previous_response = get_latest_response(user['id'], unit_id, db_conn) if previous_response: learned = previous_response['learned'] # Don't allow the previous card as the next card cards = [ card for card in cards if card['entity_id'] != previous_response['card_id'] ] else: learned = init_learned shuffle(cards) assessment, nonassessment = partition(cards, lambda c: c.has_assessment()) choose_assessment = random() < p_assessment_map[floor(learned * 10)] if choose_assessment: if not len(assessment): return nonassessment[0] for card in assessment: params = get_card_parameters({'entity_id': card['entity_id']}, db_conn) if params: values = get_card_parameters_values(params) guess = values['guess'] slip = values['slip'] correct = calculate_correct(guess, slip, learned) if 0.25 < correct < 0.75: return card else: return card return assessment[0] if len(nonassessment): return nonassessment[0] if len(assessment): return assessment[0] return None
def test_get_card_parameters_values(db_conn): create_card_test_data(db_conn) params = { 'entity_id': card_b_uuid, } card_params = get_card_parameters(db_conn, params) result = get_card_parameters_values(card_params) assert 'guess' in result assert 'slip' in result assert 'transit' in result assert 'num_learners' in result
def get_card_batch(db_conn, user, unit): unit_id = unit['entity_id'] cards = list_random_cards_in_unit(db_conn, unit_id) if not cards: return None, None, None previous_response = get_latest_response(db_conn, user['id'], unit_id) if previous_response: # Don't allow the previous card as the next card cards = [ card for card in cards if card['entity_id'] != previous_response['card_id'] ] params = {} for card in cards: params[card['entity_id']] = get_card_parameters_values( get_card_parameters(db_conn, {'entity_id': card['entity_id']}) or {}) shuffle(cards) scored, unscored = partition(cards, lambda c: c['kind'] in scored_kinds) return scored, unscored, params
def update(db_conn, user, card, response): """ Update the card's parameters (and its parents') when given a response. """ # TODO-3 split up into smaller methods if not card.has_assessment(): return { 'response': {}, 'feedback': '', } errors = card.validate_response(response) if errors: return {'errors': errors} score, feedback = card.score_response(response) response = { 'user_id': user['id'], 'card_id': card['entity_id'], 'unit_id': card['unit_id'], 'response': response, 'score': score, } card_parameters = get_card_parameters( {'entity_id': card['entity_id']}, db_conn ) or {} previous_response = get_latest_response(user['id'], card['unit_id'], db_conn) now = time() time_delta = now - (int(previous_response['created'].strftime("%s")) if previous_response else now) learned = (previous_response['learned'] if previous_response else init_learned) guess_distribution = get_distribution(card_parameters, 'guess') slip_distribution = get_distribution(card_parameters, 'slip') updates = formula_update(score, time_delta, learned, guess_distribution, slip_distribution) response['learned'] = updates['learned'] response, errors = insert_response(response, db_conn) if errors: return {'errors': errors, 'feedback': feedback} updated_card_parameters = { 'entity_id': card['entity_id'], 'guess_distribution': bundle_distribution(updates['guess_distribution']), 'slip_distribution': bundle_distribution(updates['slip_distribution']), } if card_parameters.get('id'): _, errors = update_card_parameters( card_parameters, updated_card_parameters, db_conn ) else: _, errors = insert_card_parameters( updated_card_parameters, db_conn ) if errors: return {'errors': errors, 'feedback': feedback} return {'response': response, 'feedback': feedback}