def setlesson(request,lesson_id): """Adds this lesson to the active deck. """ # put this lesson into the model model = get_model(request) if model is None: resetdeck(request) model = get_model(request) model.set_active_lesson(lesson_id) save_model(request, model) # send them off to study return HttpResponseRedirect("/study/")
def setlesson(request, lesson_id): """Adds this lesson to the active deck. """ # put this lesson into the model model = get_model(request) if model is None: resetdeck(request) model = get_model(request) model.set_active_lesson(lesson_id) save_model(request, model) # send them off to study return HttpResponseRedirect("/study/")
def impression(request): """Logs that the user had an impression of a card. This is called frequently by the studyui. Turns user action into an Impression object. """ # For testing slow/unreliable server... #time.sleep(3) # put this into a database table... i = Impression() #print request.POST i.answer = request.POST['answer'] i.concept_id = long(request.POST['id']) i.user = request.user i.timer_show = request.POST.get('showtimer') i.timer_submit = request.POST.get('submittimer') i.save() #print "times: %s,%s ms" % (request.POST['showtime'], request.POST['submittime']) # tell the learning model about the impression model = get_model(request) concept = model.log_impression(i) save_model(request, model) # return a simple HTTP response return HttpResponse("OK", mimetype='text/plain')
def jsondeck(request): """Renders the entire deck in JSON for use with the dnd deckview """ model = get_model(request) data = {} for pile in model.supported_piles(): data[pile]=[] for card in model.cards_in_pile(pile): ert = model._get_card_metadata(card,'ert') #TODO: Not portable! next_exposure_str = None if ert: impression = card.history().lookup_last_impression() if impression: next_exposure = impression.answered_date + datetime.timedelta(seconds = ert) next_exposure_str = str(next_exposure - datetime.datetime.now()) datum = {'card': card.json(), 'ert': ert, 'next': next_exposure_str, } data[pile].append( datum ) return HttpResponse( json.dumps(data), mimetype='text/plain' )
def get_many_qa(request, numcards): """Fetches the next 'numcard' cards to be displayed. Similar to getqa. Returns a json array, with the first element being the sequence number and each subsequent element being a json card object. Allows smart client to pre-fetch multiple cards to minimize user latency. Guaranteed not to modify the model state -- it won't be saved! """ # For testing slow/unreliable server... #time.sleep(3) #if random.uniform(0,1) < 0.5: #raise NotImplmentedError() # manually casting seems to avoid unicode wierdness numcards = int(numcards) model = get_model(request) # stash the sequence number at the beginning data = [model.get_sequence()] # ask the model for all the cards to show cards = model.choose_many_cards(numcards) for card in cards: data.append(card.json()) return HttpResponse(json.dumps(data), mimetype='text/plain')
def get_many_qa(request,numcards): """Fetches the next 'numcard' cards to be displayed. Similar to getqa. Returns a json array, with the first element being the sequence number and each subsequent element being a json card object. Allows smart client to pre-fetch multiple cards to minimize user latency. Guaranteed not to modify the model state -- it won't be saved! """ # For testing slow/unreliable server... #time.sleep(3) #if random.uniform(0,1) < 0.5: #raise NotImplmentedError() # manually casting seems to avoid unicode wierdness numcards = int(numcards) model = get_model(request) # stash the sequence number at the beginning data = [ model.get_sequence() ] # ask the model for all the cards to show cards = model.choose_many_cards(numcards) for card in cards: data.append( card.json() ) return HttpResponse( json.dumps(data), mimetype='text/plain' )
def dnddeckview(request): """Shows the old drag-n-drop deck view which doesn't do much """ model = get_model(request) return render_to_response("deck/dnddeck.html", { 'piles': model.supported_piles(), }, context_instance=RequestContext(request))
def debugmodel(request): """Dumps out the model in human-readable form. Also displays a form for allowing the debugger to post an impression and see any error message """ model = get_model(request) str = u"%s" % model return render_to_response("study/debug.html", {'debugstring': str})
def debugmodel(request): """Dumps out the model in human-readable form. Also displays a form for allowing the debugger to post an impression and see any error message """ model = get_model(request) str = u"%s" % model return render_to_response("study/debug.html", { 'debugstring': str } )
def jsoncard(request, card_id): """Renders JSON for a single card TODO: HTTP-cache these, since they're immutable (This is not actually used...) """ model = get_model(request) card = Card.lookup_card(card_id) data = card.json() return HttpResponse(json.dumps(data), mimetype='text/plain')
def studyui(request): """Shows the UI which presents cards and solicits the responses. This UI is all ajaxy and doesn't need any server-side data. #TODO: move this to static media """ if get_model(request) is None: #TODO: generalize this to send you to a 'pick a lesson' page return HttpResponseRedirect("/chinese/") return render_to_response("study/studyui.html", context_instance=RequestContext(request))
def jsoncard(request, card_id): """Renders JSON for a single card TODO: HTTP-cache these, since they're immutable (This is not actually used...) """ model = get_model(request) card = Card.lookup_card(card_id) data = card.json() return HttpResponse( json.dumps(data), mimetype='text/plain' )
def debug_card(request,card_id): card = Card.lookup_card(card_id) model = get_model(request) history = card.history() params = {} params['pile'] = model.which_pile(card) params['ert'] = safe_timedelta(seconds = model.get_ert(card)) params['estimated_ert'] = safe_timedelta(model._estimate_ert(card)) params['longest_yes'] = history.delay_on_longest_yes() params['most_recent_yes'] = history.delay_on_most_recent_yes()[0] params['how_far_back_most_recent_yes'] = history.delay_on_most_recent_yes()[1] params['next_exposure'] = model.next_exposure_date(card) params['last_exposure'] = history.lookup_last_impression() params['answer_history'] = history.full_impression_history(20) return render_to_response("deck/debug_card.html", params, context_instance=RequestContext(request))
def getqa(request): """Returns the next card to display to the user. This is called frequently by the studyui. Guaranteed not to modify the model state -- it won't be saved! """ # call the model to pick the next card to show model = get_model(request) card = model.choose_card() # Note: I'm changing the contract here. # Models aren't allowed to change their state when picking a card. # Picking a card has no side-effects. # This is not a big burden on the model, but greatly simplifies # the client. #save_model(request, model) data = card.json() return HttpResponse(json.dumps(data), mimetype='text/plain')
def show_meta(request): # first, fetch the recent impressions recent_impressions = Impression.objects.order_by('-answered_date').filter(user=request.user) # limit to last 30 impressions recent_impressions = recent_impressions[0:29] # now build a list of card Q's, with the pile for each card. # don't eliminate duplicate cards model = get_model(request) recent_cards = [] for impression in recent_impressions: #print "id %s" % impression.concept.id card = Card.lookup_card(impression.concept.id) pile = model.which_pile(card) ert = model._get_card_metadata(card,'ert') #TODO: Not portable! logging.debug("ert for %s is %s" % (card,ert)) if ert: next_exposure = impression.answered_date + datetime.timedelta(seconds = ert) else: next_exposure = None recent_cards.append( (card.question(), pile, card, ert, next_exposure) ) # # fetch counts for each pile # pilecount = [] for pile in model.supported_piles(): pilecount.append( (pile, len( model.cards_in_pile(pile) ) ) ) # and the description description = model.description templatevars = { 'recent_cards': recent_cards, 'pilecount': pilecount, 'description': description, } return render_to_response("deck/show_meta.html", templatevars)
def getqa(request): """Returns the next card to display to the user. This is called frequently by the studyui. Guaranteed not to modify the model state -- it won't be saved! """ # call the model to pick the next card to show model = get_model(request) card = model.choose_card() # Note: I'm changing the contract here. # Models aren't allowed to change their state when picking a card. # Picking a card has no side-effects. # This is not a big burden on the model, but greatly simplifies # the client. #save_model(request, model) data = card.json() return HttpResponse( json.dumps(data), mimetype='text/plain' )
def deckview(request): """Shows you a list of recent cards. """ # first, fetch the recent impressions recent_impressions = Impression.objects.order_by('-answered_date').filter(user=request.user) # limit to last 30 impressions recent_impressions = recent_impressions[0:29] # now build a list of card Q's, with the pile for each card. # don't eliminate duplicate cards model = get_model(request) recent_cards = [] for impression in recent_impressions: #print "id %s" % impression.concept.id card = Card.lookup_card(impression.concept.id) pile = model.which_pile(card) recent_cards.append( (card.question(), pile, card) ) # # fetch counts for each pile # pilecount = [] for pile in model.supported_piles(): pilecount.append( (pile, len( model.cards_in_pile(pile) ) ) ) # and the description description = model.description templatevars = { 'recent_cards': recent_cards, 'pilecount': pilecount, 'description': description, } return render_to_response("deck/deckview.html", templatevars)
def create_review_deck(request): """ Creates a deck forreviewing old material. Wipes out the current deck """ # start over with a new model resetdeck(request) model = get_model(request) # Fetch a bunch of impressions all_no = Impression.objects.filter(user=request.user, answer="No") all_kinda = Impression.objects.filter(user=request.user, answer="Kinda") # add up the bad impressions bad_total = {} for impression in all_kinda: id = impression.concept_id # TUNE: 1 point per kinda if id not in bad_total: bad_total[ impression.concept_id ] = 1 else: bad_total[ impression.concept_id ] += 1 for impression in all_no: id = impression.concept_id # TUNE: 3 points per no if id not in bad_total: bad_total[ impression.concept_id ] = 3 else: bad_total[ impression.concept_id ] += 3 # invert the map so it maps # bad points to concepts. # Note there could be collisions here, so build a list for each num pts inverted_bad_total = {} for id, pts in bad_total.items(): # TUNE: Minimum threshhold for something to be reviewable if pts > 2: if pts not in inverted_bad_total: inverted_bad_total[pts]=[id] else: inverted_bad_total[pts].append(id) # Prepare results list review_cards = [] # Now we put together our list of cards, in order of badness #print "ibt: %s" % inverted_bad_total pts_order_desc = inverted_bad_total.keys() pts_order_desc.reverse() #print "pod: %s" % pts_order_desc for pts in pts_order_desc: for card in inverted_bad_total[pts]: review_cards.append(card) # Now put them all into the model model.add_new_cards("Review of difficult cards", review_cards) # Write the model back save_model(request,model) # redirect to a standard deck view return HttpResponseRedirect("/deck/")