Skip to content

Commit

Permalink
Changed the question to be based on a corpus of questions selected pe…
Browse files Browse the repository at this point in the history
…r user, added to dynamically, and removed when you get it correct enough times
  • Loading branch information
matthewstevens committed Aug 16, 2010
1 parent b2bc53e commit 7813f1d
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 4 deletions.
5 changes: 5 additions & 0 deletions minerva/admin.py
Expand Up @@ -16,8 +16,13 @@ class UserProfileAdmin(admin.ModelAdmin):
list_display = ("student", "language")
list_filter = ["language"]

class SessionProgressAdmin(admin.ModelAdmin):
list_display = ("student_string", "word", "weight", "correct")
list_filter = ["student"]

admin.site.register(models.Word, WordAdmin)
admin.site.register(models.Progress, ProgressAdmin)
admin.site.register(models.UserProfile, UserProfileAdmin)
admin.site.register(models.SessionProgress, SessionProgressAdmin)
admin.site.register(models.Language)

7 changes: 7 additions & 0 deletions minerva/models.py
Expand Up @@ -52,6 +52,13 @@ class SessionProgress(models.Model):
weight = models.IntegerField(default=0) # default weight of zero, we should check out this word!!
correct = models.IntegerField(default=0)

def student_string(self):
if self.is_anonymous():
return u"Anon: %s" % self.anon_student
else:
return unicode(self.student)
student_string.short_description = "student" # pylint: disable-msg=W0612

class Progress(models.Model):
"""
Tracking historical progress for a particular user on a word.
Expand Down
76 changes: 75 additions & 1 deletion minerva/questions.py
Expand Up @@ -3,16 +3,90 @@

from django.conf import settings
from django.core.cache import cache
from django.db.models import Min

from minerva.models import Word

from minerva.models import SessionProgress

QUESTION_SCENARIOS = (
# Question, answer
('word', 'meaning'),
('meaning', 'word'),
)

def get_available_words(query, language, level):
available_words = SessionProgress.objects.filter(**query)
if len(available_words) < 10:
num_required = 5 - len(available_words)
pks = word_keys(language, level)
current_words = set(available_words.values_list("word", flat=True))
# Collisions should be infrequent, so we should be able to get surplus
sampled_words = set(Word.objects.filter(pk__in=random.sample(pks, num_required + 10)))
# remove duplicate words from our candidates
selected = sampled_words - current_words
for i in selected:
query["word"] = i
new_entry = SessionProgress.objects.create(**query)
available_words = SessionProgress.objects.filter(**query)

return available_words.order_by("weight")

def decrement_weight(progress):
if progress.weight <= 0:
# if progress is zero we don't want to see this one again, so secretly increment
progress.weight += 5
progress.save()
return
delta = 5 + (5 * random.random())
if progress.weight - delta < 0:
progress.weight = 0
else:
progress.weight -= delta
progress.save()

def process_answer(query, data):
# FIXME - big assumption here, that the progress object exists
correct_answer = data['meta'][0]
q = dict(query)
q['word'] = correct_answer
progress_on_correct_answer = SessionProgress.objects.get(**q)
if int(correct_answer) != int(data['answer']):
# data['answer'] is the selected answer
# Relax the weights because we got them wrong, we want to be more likely to select it next time
q['word'] = data['answer']
progress_on_incorrect_select = SessionProgress.objects.get(**q)
decrement_weight(progress_on_correct_answer)
decrement_weight(progress_on_incorrect_select)
else:
progress_on_correct_answer.correct += 1
if progress_on_correct_answer.correct > 5:
# we have seen this word enough times and answered correctly remove it from the fold
progress_on_correct_answer.delete()
else:
progress_on_correct_answer.weight += 10 + (10 * random.random())
progress_on_correct_answer.save()

def create_question_complex(request, language, level, num_choices=4):
# get word with lowest weight, fill session with more words if necessary
query = {}
if request.user.is_authenticated():
query['student'] = request.user
else:
query['anon_student'] = request.session.session_key
available_words = get_available_words(query, language, level)
query['language'] = language
next_word = available_words[0]
possible_answers = random.sample(available_words[1:], num_choices - 1) + [next_word]
random.shuffle(possible_answers)
question_attribute, answer_attribute = random.choice(QUESTION_SCENARIOS)
question_data = (
[next_word.word.pk, getattr(next_word.word, question_attribute)],
[(word.word.pk, getattr(word.word, answer_attribute)) for word in possible_answers]
)
return question_data
#next_word = .aggregate(Min('weight'))
# randomly select other words from corpus

def create_question(user, language, level, num_choices=4):
"""
Returns a question and multiple plausible answers, given the user, their
Expand Down
9 changes: 6 additions & 3 deletions minerva/views.py
@@ -1,7 +1,7 @@
from django.shortcuts import render_to_response
from django.template import RequestContext

from minerva.questions import create_question
from minerva.questions import create_question_complex, process_answer
from minerva.models import Progress, Word
from minerva.forms import QuestionForm

Expand All @@ -16,11 +16,14 @@ def validate_answer(request):
raise Exception(form.errors)
data = form.cleaned_data
word = Word.objects.get(id=data["meta"][0])
query = {'word': word}
query = {}
if request.user.is_authenticated():
query['student'] = request.user
else:
query['anon_student'] = request.session.session_key

process_answer(query, data)
query['word'] = word

progress, _ = Progress.objects.get_or_create(**query)
progress.attempts += 1
Expand All @@ -47,7 +50,7 @@ def question(request):
# - a way to select a language.
# - a way to select difficulty level.
# - ...
problem, answers = create_question(None, "zho", 1)
problem, answers = create_question_complex(request, "zho", 1)
form = QuestionForm(question=problem, answers = answers)
context['question'] = problem[1]
context['form'] = form
Expand Down

0 comments on commit 7813f1d

Please sign in to comment.