def select_subject_route(request, user_id, subject_id): """ Select the subject to work on. NEXT STATE POST Choose Subject (Update Learner Context) -> GET Choose Subject ...when subject is complete -> GET Choose Unit ...when in learn or review mode -> GET Learn Card ...when in diagnosis (Unit auto chosen) """ db_conn = request['db_conn'] current_user = get_current_user(request) if not current_user: return abort(401, 'f8IynoM9RLmW0Ae14_Hukw') subject = get_latest_accepted_subject(db_conn, subject_id) set_learning_context(current_user, subject=subject) buckets = traverse(db_conn, current_user, subject) # When in diagnosis, choose the unit and card automagically. # if buckets.get('diagnose'): # unit = buckets['diagnose'][0] # card = choose_card(db_conn, current_user, unit) # next_ = { # 'method': 'GET', # 'path': '/s/cards/{card_id}/learn' # .format(card_id=convert_uuid_to_slug(card['entity_id'])), # } # set_learning_context( # current_user, # next=next_, unit=unit, card=card) # When in learn or review mode, lead me to choose a unit. # elif buckets.get('review') or if buckets.get('learn'): next_ = { 'method': 'GET', 'path': '/s/subjects/{subject_id}/units'.format( subject_id=convert_uuid_to_slug(subject_id) ), } set_learning_context(current_user, next=next_) # If the subject is complete, lead the learner to choose another subject. else: next_ = { 'method': 'GET', 'path': '/s/users/{user_id}/subjects'.format( user_id=convert_uuid_to_slug(current_user['id']) ), } set_learning_context(current_user, next=next_, unit=None, subject=None) return 200, {'next': next_}
def test_traverse(db_conn, units_table, users_table, responses_table, sets_table): """ Expect to take a list of units and traverse them correctly. Basic test. """ add_test_set(db_conn, users_table, units_table, responses_table, sets_table) set_ = Set.get(db_conn, entity_id="set") user = get_user({"id": "user"}, db_conn) buckets = traverse(db_conn, user, set_) assert buckets["diagnose"][0]["entity_id"] == "divide" assert buckets["learn"][0]["entity_id"] == "multiply" assert buckets["review"][0]["entity_id"] == "subtract"
def test_traverse(db_conn, units_table, users_table, responses_table, sets_table): """ Expect to take a list of units and traverse them correctly. Basic test. """ add_test_set(db_conn, users_table, units_table, responses_table, sets_table) set_ = Set.get(db_conn, entity_id='set') user = get_user({'id': 'user'}, db_conn) buckets = traverse(db_conn, user, set_) assert buckets['diagnose'][0]['entity_id'] == 'divide' assert buckets['learn'][0]['entity_id'] == 'multiply' assert buckets['review'][0]['entity_id'] == 'subtract'
def test_traverse(db_conn, units_table, users_table, responses_table, sets_table): """ Expect to take a list of units and traverse them correctly. Basic test. """ add_test_set(db_conn, users_table, units_table, responses_table, sets_table) set_ = Set.get(entity_id='set') user = User.get(id='user') buckets = traverse(user, set_) assert buckets['diagnose'][0]['entity_id'] == 'divide' assert buckets['learn'][0]['entity_id'] == 'multiply' assert buckets['review'][0]['entity_id'] == 'subtract'
def get_set_units_route(request, set_id): """ Present a small number of units the learner can choose from. NEXT STATE GET Choose Unit -> POST Choose Unit """ db_conn = request['db_conn'] # TODO-3 simplify this method. should it be part of the models? current_user = get_current_user(request) if not current_user: return abort(401) context = get_learning_context(current_user) next_ = { 'method': 'POST', 'path': '/s/sets/{set_id}/units/{unit_id}'.format(set_id=context.get( 'set', {}).get('entity_id'), unit_id='{unit_id}'), } set_learning_context(current_user, next=next_) set_ = Set.get_latest_accepted(db_conn, set_id) # Pull a list of up to 5 units to choose from based on priority. buckets = traverse(db_conn, current_user, set_) units = buckets['learn'][:5] # TODO-3 Time estimates per unit for mastery. return 200, { 'next': next_, 'units': [unit.deliver() for unit in units], # For the menu, it must return the name and ID of the set 'set': set_.deliver(), 'current_unit_id': context.get('unit', {}).get('entity_id'), }
def test_traverse(db_conn, session): """ Expect to take a list of units and traverse them correctly. Basic test. """ add_test_subject(db_conn) subject = get_latest_accepted_subject(db_conn, entity_id=subject_uuid) assert subject is not None user = get_user(db_conn, {'id': user_id}) buckets = traverse(db_conn, user, subject) assert buckets['learn'][0]['entity_id'] in ( unit_subtract_uuid, unit_multiply_uuid, ) assert buckets['learn'][1]['entity_id'] in ( unit_subtract_uuid, unit_multiply_uuid, ) assert buckets['blocked'][0]['entity_id'] == unit_divide_uuid
def get_set_units_route(request, set_id): """ Present a small number of units the learner can choose from. NEXT STATE GET Choose Unit -> POST Choose Unit """ db_conn = request['db_conn'] # TODO-3 simplify this method. should it be part of the models? current_user = get_current_user(request) if not current_user: return abort(401) context = get_learning_context(current_user) next_ = { 'method': 'POST', 'path': '/s/sets/{set_id}/units/{unit_id}' .format(set_id=context.get('set', {}).get('entity_id'), unit_id='{unit_id}'), } set_learning_context(current_user, next=next_) set_ = Set.get_latest_accepted(db_conn, set_id) # Pull a list of up to 5 units to choose from based on priority. buckets = traverse(db_conn, current_user, set_) units = buckets['learn'][:5] # TODO-3 Time estimates per unit for mastery. return 200, { 'next': next_, 'units': [unit.deliver() for unit in units], # For the menu, it must return the name and ID of the set 'set': set_.deliver(), 'current_unit_id': context.get('unit', {}).get('entity_id'), }
def get_subject_units_route(request, subject_id): """ Present a small number of units the learner can choose from. NEXT STATE GET Choose Unit -> POST Choose Unit """ db_conn = request['db_conn'] # TODO-3 simplify this method. should it be part of the models? current_user = get_current_user(request) if not current_user: return abort(401, 'p_wleq0FRQ2HEuHKOJIb7Q') context = get_learning_context(current_user) next_ = { 'method': 'POST', 'path': '/s/subjects/{subject_id}/units/{unit_id}'.format( subject_id=context.get('subject', {}).get('entity_id'), unit_id='{unit_id}'), } set_learning_context(current_user, next=next_) subject = get_latest_accepted_subject(db_conn, subject_id) if not subject: return abort(404, '68VOXmd6Shq2cwAAktkFtw') # Pull a list of up to 5 units to choose from based on priority. buckets = traverse(db_conn, current_user, subject) units = buckets['learn'][:5] # TODO-3 Time estimates per unit for mastery. return 200, { 'next': next_, 'units': [deliver_unit(unit) for unit in units], # For the menu, it must return the name and ID of the subject 'subject': deliver_subject(subject), 'current_unit_id': context.get('unit', {}).get('entity_id'), }
def get_subject_tree_route(request, subject_id): """ Render the tree of units that exists within a subject. Contexts: - Search subject, preview units in subject - Pre diagnosis - Learner view progress in subject - Subject complete TODO-2 merge with get_subject_units_route TODO-2 simplify this method """ db_conn = request['db_conn'] subject = get_latest_accepted_subject(db_conn, subject_id) if not subject: return abort(404, '66ejBlFdQ4aYy5MBuP501Q') units = list_units_in_subject_recursive(db_conn, subject) # For the menu, it must return the name and ID of the subject output = { 'subjects': deliver_subject(subject), 'units': [deliver_unit(unit) for unit in units], } current_user = get_current_user(request) if not current_user: return 200, output buckets = traverse(db_conn, current_user, subject) output['buckets'] = { # 'diagnose': [u['entity_id'] for u in buckets['diagnose']], # 'review': [u['entity_id'] for u in buckets['review']], 'blocked': [u['entity_id'] for u in buckets['blocked']], 'learn': [u['entity_id'] for u in buckets['learn']], 'done': [u['entity_id'] for u in buckets['done']], } return 200, output
def get_set_tree_route(request, set_id): """ Render the tree of units that exists within a set. Contexts: - Search set, preview units in set - Pre diagnosis - Learner view progress in set - Set complete NEXT STATE GET View Set Tree -> GET Choose Set ...when set is complete -> GET Choose Unit ...when in learn or review mode -> GET Learn Card ...when in diagnosis (Unit auto chosen) TODO-2 merge with get_set_units_route TODO-2 simplify this method """ db_conn = request['db_conn'] set_ = Set.get(db_conn, entity_id=set_id) if not set_: return abort(404) units = set_.list_units(db_conn) # For the menu, it must return the name and ID of the set output = { 'set': set_.deliver(), 'units': [u.deliver() for u in units], } current_user = get_current_user(request) if not current_user: return 200, output context = get_learning_context(current_user) if current_user else {} buckets = traverse(db_conn, current_user, set_) output['buckets'] = { 'diagnose': [u['entity_id'] for u in buckets['diagnose']], 'review': [u['entity_id'] for u in buckets['review']], 'learn': [u['entity_id'] for u in buckets['learn']], 'done': [u['entity_id'] for u in buckets['done']], } # If we are just previewing, don't update anything if set_id != context.get('set', {}).get('entity_id'): return 200, output # When in diagnosis, choose the unit and card automagically. if buckets['diagnose']: unit = buckets['diagnose'][0] card = choose_card(db_conn, current_user, unit) next_ = { 'method': 'GET', 'path': '/s/cards/{card_id}/learn'.format(card_id=card['entity_id']), } set_learning_context(current_user, next=next_, unit=unit.data, card=card.data) # When in learn or review mode, lead me to choose a unit. elif buckets['review'] or buckets['learn']: next_ = { 'method': 'GET', 'path': '/s/sets/{set_id}/units'.format(set_id=set_id), } set_learning_context(current_user, next=next_) # If the set is complete, lead the learner to choose another set. else: next_ = { 'method': 'GET', 'path': '/s/users/{user_id}/sets'.format(user_id=current_user['id']), } set_learning_context(current_user, next=next_, unit=None, set=None) output['next'] = next_ return 200, output
def respond_to_card_route(request, card_id): """ Record and process a learner's response to a card. NEXT STATE POST Respond Card -> GET Learn Card ...when not ready -> GET Choose Unit ...when ready, but still units -> GET View Set Tree ...when ready and done """ # TODO-3 simplify this method. # perhaps smaller methods or move to model layer? current_user = get_current_user(request) if not current_user: return abort(401) card = get_card_by_kind(card_id) if not card: return abort(404) # Make sure the card is the current one context = current_user.get_learning_context() if context.get('card', {}).get('entity_id') != card['entity_id']: return abort(400) r = seq_update(current_user, card, request['params'].get('response')) errors, response, feedback = (r.get('errors'), r.get('response'), r.get('feedback')) if errors: return 400, { 'errors': errors, 'ref': 'wtyOJPoy4bh76OIbYp8mS3LP', } set_ = Set(context.get('set')) unit = Unit(context.get('unit')) status = judge(unit, current_user) # If we are done with this current unit... if status == "done": buckets = traverse(current_user, set_) # If there are units to be diagnosed... if buckets['diagnose']: unit = buckets['diagnose'][0] next_card = choose_card(current_user, unit) next_ = { 'method': 'GET', 'path': '/s/cards/{card_id}/learn'.format( card_id=next_card['entity_id']), } current_user.set_learning_context(card=next_card.data, unit=unit.data, next=next_) # If there are units to be learned or reviewed... elif buckets['learn'] or buckets['review']: next_ = { 'method': 'GET', 'path': '/s/sets/{set_id}/units'.format(set_id=set_['entity_id']), } current_user.set_learning_context(card=None, unit=None, next=next_) # If we are out of units... else: next_ = { 'method': 'GET', 'path': '/s/sets/{set_id}/tree'.format(set_id=set_['entity_id']), } current_user.set_learning_context(card=None, unit=None, next=next_) # If we are still reviewing, learning or diagnosing this unit... else: next_card = choose_card(current_user, unit) if next_card: next_ = { 'method': 'GET', 'path': '/s/cards/{card_id}/learn'.format( card_id=next_card['entity_id']), } current_user.set_learning_context(card=next_card.data, next=next_) else: next_ = {} current_user.set_learning_context(next=next_) return 200, { 'response': response.deliver(), 'feedback': feedback, 'next': next_, }
def respond_to_card_route(request, card_id): """ Record and process a learner's response to a card. NEXT STATE POST Respond Card -> GET Learn Card ...when not ready -> GET Choose Unit ...when ready, but still units -> GET View Set Tree ...when ready and done """ # TODO-3 simplify this method. # perhaps smaller methods or move to model layer? current_user = get_current_user(request) if not current_user: return abort(401) card = get_card_by_kind(card_id) if not card: return abort(404) # Make sure the card is the current one context = current_user.get_learning_context() if context.get('card', {}).get('entity_id') != card['entity_id']: return abort(400) r = seq_update(current_user, card, request['params'].get('response')) errors, response, feedback = (r.get('errors'), r.get('response'), r.get('feedback')) if errors: return 400, { 'errors': errors, 'ref': 'wtyOJPoy4bh76OIbYp8mS3LP', } set_ = Set(context.get('set')) unit = Unit(context.get('unit')) status = judge(unit, current_user) # If we are done with this current unit... if status == "done": buckets = traverse(current_user, set_) # If there are units to be diagnosed... if buckets['diagnose']: unit = buckets['diagnose'][0] next_card = choose_card(current_user, unit) next_ = { 'method': 'GET', 'path': '/s/cards/{card_id}/learn' .format(card_id=next_card['entity_id']), } current_user.set_learning_context( card=next_card.data, unit=unit.data, next=next_) # If there are units to be learned or reviewed... elif buckets['learn'] or buckets['review']: next_ = { 'method': 'GET', 'path': '/s/sets/{set_id}/units' .format(set_id=set_['entity_id']), } current_user.set_learning_context(card=None, unit=None, next=next_) # If we are out of units... else: next_ = { 'method': 'GET', 'path': '/s/sets/{set_id}/tree' .format(set_id=set_['entity_id']), } current_user.set_learning_context(card=None, unit=None, next=next_) # If we are still reviewing, learning or diagnosing this unit... else: next_card = choose_card(current_user, unit) if next_card: next_ = { 'method': 'GET', 'path': '/s/cards/{card_id}/learn' .format(card_id=next_card['entity_id']), } current_user.set_learning_context(card=next_card.data, next=next_) else: next_ = {} current_user.set_learning_context(next=next_) return 200, { 'response': response.deliver(), 'feedback': feedback, 'next': next_, }
def get_set_tree_route(request, set_id): """ Render the tree of units that exists within a set. Contexts: - Search set, preview units in set - Pre diagnosis - Learner view progress in set - Set complete NEXT STATE GET View Set Tree -> GET Choose Set ...when set is complete -> GET Choose Unit ...when in learn or review mode -> GET Learn Card ...when in diagnosis (Unit auto chosen) TODO-2 merge with get_set_units_route TODO-2 simplify this method """ db_conn = request['db_conn'] set_ = Set.get(db_conn, entity_id=set_id) if not set_: return abort(404) units = set_.list_units(db_conn) # For the menu, it must return the name and ID of the set output = { 'set': set_.deliver(), 'units': [u.deliver() for u in units], } current_user = get_current_user(request) if not current_user: return 200, output context = get_learning_context(current_user) if current_user else {} buckets = traverse(db_conn, current_user, set_) output['buckets'] = { 'diagnose': [u['entity_id'] for u in buckets['diagnose']], 'review': [u['entity_id'] for u in buckets['review']], 'learn': [u['entity_id'] for u in buckets['learn']], 'done': [u['entity_id'] for u in buckets['done']], } # If we are just previewing, don't update anything if set_id != context.get('set', {}).get('entity_id'): return 200, output # When in diagnosis, choose the unit and card automagically. if buckets['diagnose']: unit = buckets['diagnose'][0] card = choose_card(db_conn, current_user, unit) next_ = { 'method': 'GET', 'path': '/s/cards/{card_id}/learn' .format(card_id=card['entity_id']), } set_learning_context( current_user, next=next_, unit=unit.data, card=card.data) # When in learn or review mode, lead me to choose a unit. elif buckets['review'] or buckets['learn']: next_ = { 'method': 'GET', 'path': '/s/sets/{set_id}/units' .format(set_id=set_id), } set_learning_context(current_user, next=next_) # If the set is complete, lead the learner to choose another set. else: next_ = { 'method': 'GET', 'path': '/s/users/{user_id}/sets' .format(user_id=current_user['id']), } set_learning_context(current_user, next=next_, unit=None, set=None) output['next'] = next_ return 200, output
def respond_to_card_route(request, card_id): """ Record and process a learner's response to a card. NEXT STATE POST Respond Card -> GET Learn Card ...when not ready -> GET Choose Unit ...when ready, but still units -> GET View Subject Tree ...when ready and done """ # TODO-3 simplify this method. # perhaps smaller methods or move to model layer? db_conn = request['db_conn'] current_user = get_current_user(request) if not current_user: return abort(401, 'XDVEHHLRSZqQNJW4Zi_iqw') card = get_latest_accepted_card(db_conn, card_id) if not card: return abort(404, 'TQZ3SmAhS1qBd274C9DG0w') # Make sure the card is the current one context = get_learning_context(current_user) context_card_id = context.get('card', {}).get('entity_id') if context_card_id != convert_uuid_to_slug(card['entity_id']): return 400, { 'errors': [{ 'message': 'Not the current card.', 'ref': 'XfmF52NmQnK_bbaxx-p8dg', }] } result = seq_update(db_conn, current_user, card, request['params'].get('response')) errors, response, feedback = (result.get('errors'), result.get('response'), result.get('feedback')) if errors: return 400, { 'errors': errors, 'ref': 'HfuW7_B-TByy8yh4FwgdrA', } subject = context.get('subject') unit = context.get('unit') status = judge(db_conn, unit, current_user) # If we are done with this current unit... if status == "done": buckets = traverse(db_conn, current_user, subject) # If there are units to be diagnosed... if buckets.get('diagnose'): unit = buckets['diagnose'][0] next_card = choose_card(db_conn, current_user, unit) next_ = { 'method': 'GET', 'path': '/s/cards/{card_id}/learn'.format( card_id=convert_uuid_to_slug(next_card['entity_id'])), } set_learning_context(current_user, card=next_card.data, unit=unit, next=next_) # If there are units to be learned or reviewed... elif buckets.get('learn') or buckets.get('review'): next_ = { 'method': 'GET', 'path': '/s/subjects/{subject_id}/units'.format( subject_id=convert_uuid_to_slug(subject['entity_id'])), } set_learning_context(current_user, card=None, unit=None, next=next_) # If we are out of units... else: next_ = { 'method': 'GET', 'path': '/s/subjects/{subject_id}/tree'.format( subject_id=convert_uuid_to_slug(subject['entity_id'])), } set_learning_context(current_user, card=None, unit=None, next=next_) # If we are still reviewing, learning or diagnosing this unit... else: next_card = choose_card(db_conn, current_user, unit) if next_card: next_ = { 'method': 'GET', 'path': '/s/cards/{card_id}/learn'.format( card_id=convert_uuid_to_slug(next_card['entity_id'])), } set_learning_context(current_user, card=next_card, next=next_) else: next_ = {} set_learning_context(current_user, next=next_) return 200, { 'response': deliver_response(response), 'feedback': feedback, 'next': next_, }