Permalink
Browse files

Changed the question to be based on a corpus of questions selected pe…

…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 7813f1d91bdf55df648ffd41f7f6d896b3074cde
Showing with 93 additions and 4 deletions.
  1. +5 −0 minerva/admin.py
  2. +7 −0 minerva/models.py
  3. +75 −1 minerva/questions.py
  4. +6 −3 minerva/views.py
View
@@ -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)
View
@@ -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.
View
@@ -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
View
@@ -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
@@ -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
@@ -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

0 comments on commit 7813f1d

Please sign in to comment.