Permalink
Browse files

Merge branch 'master' of github.com:malcolmt/remember_me

  • Loading branch information...
malcolmt committed Aug 16, 2010
2 parents 13039e4 + e8a8e20 commit 69355153c5d3c830f934030ef1c32b0bf30dfeee
Showing with 102 additions and 11 deletions.
  1. +5 −0 minerva/admin.py
  2. +7 −0 minerva/models.py
  3. +74 −1 minerva/questions.py
  4. +16 −10 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,89 @@
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, last_question):
+ 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:
+ new_word = dict(query)
+ new_word["word"] = i
+ new_entry = SessionProgress.objects.create(**new_word)
+ available_words = SessionProgress.objects.filter(**query)
+ if last_question:
+ last_word = Word.objects.get(id=last_question)
+ available_words = available_words.exclude(word=last_word)
+ 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_base, data):
+ # FIXME - big assumption here, that the progress object exists
+ correct_answer = data['meta'][0]
+ query = dict(query_base)
+ query['word'] = correct_answer
+ progress_on_correct_answer = SessionProgress.objects.get(**query)
+ 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
+ query['word'] = data['answer']
+ progress_on_incorrect_select = SessionProgress.objects.get(**query)
+ 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(query_base, language, level, last_question, num_choices=4):
+ # get word with lowest weight, fill session with more words if necessary
+ query = dict(query_base)
+ available_words = get_available_words(query, language, level, last_question)
+ 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,35 +1,35 @@
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
-def validate_answer(request):
+def validate_answer(request, query_base):
"""
For now just update the correct answer with the data
FIXME - how do I get a question form to validate across fields and against the db
"""
+ query = dict(query_base)
form = QuestionForm(request.POST)
if not form.is_valid():
# Either form-tampering or submitting without data. In either case,
# we'll just ignore it and generate a new question.
return {}
data = form.cleaned_data
word = Word.objects.get(id=data["meta"][0])
- query = {'word': word}
- 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
result = {
+ "prev_id": word.id,
"prev_word": word.word,
"prev_meaning": word.meaning
}
- if int(form.cleaned_data['answer']) == (word.pk):
+ if int(form.cleaned_data['answer']) == word.pk:
progress.correct += 1
result["prev_result"] = True
else:
@@ -40,15 +40,21 @@ def validate_answer(request):
def question(request):
context = {}
+ query= {}
+ if request.user.is_authenticated():
+ query['student'] = request.user
+ else:
+ query['anon_student'] = request.session.session_key
+
if request.method == 'POST':
- result = validate_answer(request)
+ result = validate_answer(request, query)
context.update(result)
# TODO: Things needed -
# - a way to select a language.
# - a way to select difficulty level.
# - ...
- problem, answers = create_question(None, "zho", 1)
+ problem, answers = create_question_complex(query, "zho", 1, context.get('prev_id', None))
form = QuestionForm(question=problem, answers = answers)
context['question'] = problem[1]
context['form'] = form

0 comments on commit 6935515

Please sign in to comment.