diff --git a/oneplus/admin.py b/oneplus/admin.py index 4ec667e4..75c7352e 100644 --- a/oneplus/admin.py +++ b/oneplus/admin.py @@ -67,7 +67,8 @@ def import_data(self, dataset, **kwargs): class OnePlusLearnerAdmin(LearnerAdmin): resource_class = OnePlusLearnerResource - list_display = LearnerAdmin.list_display + ("get_completed", "get_perc_correct") + list_display = LearnerAdmin.list_display + ("get_completed", "get_perc_correct", "get_redo_completed", + "get_redo_perc_correct") list_filter = (LearnerActiveFilter, LearnerPercentageCorrectFilter, LearnerPercentageOfQuestionsCompletedFilter, LearnerTimeFrameFilter, LearnerLimitFilter) + LearnerAdmin.list_filter @@ -109,6 +110,16 @@ def get_perc_correct(self, obj): get_perc_correct.short_description = "Percentage Correct" get_perc_correct.admin_order_field = "perc" + def get_redo_completed(self, obj): + return obj.redo_total + get_redo_completed.short_description = "Redo Completed" + get_redo_completed.admin_order_field = "redo_total" + + def get_redo_perc_correct(self, obj): + return obj.redo_perc + get_redo_perc_correct.short_description = "Redo Percentage Correct" + get_redo_perc_correct.admin_order_field = "redo_perc" + @staticmethod def get_total_query(timeframe): qry = """ @@ -143,6 +154,40 @@ def get_perc_correct_query(timeframe): return qry + @staticmethod + def get_redo_total_query(timeframe): + qry = """ + select count(1) + from core_participantredoquestionanswer pqa + INNER JOIN core_participant p + ON p.id = pqa.participant_id + AND p.learner_id = auth_learner.customuser_ptr_id + """ + + if timeframe: + qry += " where answerdate between %s and %s" + + return qry + + @staticmethod + def get_redo_perc_correct_query(timeframe): + rep_str = get_sum_boolean_cast_string() + + qry = """ + coalesce((select sum(coalesce(correct%s, 0)) * 100 / count(1) + from core_participantredoquestionanswer pqa + INNER JOIN core_participant p + ON p.id = pqa.participant_id + AND p.learner_id = auth_learner.customuser_ptr_id + """ % rep_str + + if timeframe: + qry += " where answerdate between %s and %s" + + qry += "), 0)" + + return qry + def queryset(self, request): if "tf" in request.GET: timeframe = request.GET["tf"] @@ -153,10 +198,14 @@ def queryset(self, request): if timeframe: start, end = get_timeframe_range(timeframe) qs = qs.extra(select={"total": self.get_total_query(timeframe)}, select_params=(start, end))\ - .extra(select={"perc": self.get_perc_correct_query(timeframe)}, select_params=(start, end)) + .extra(select={"perc": self.get_perc_correct_query(timeframe)}, select_params=(start, end))\ + .extra(select={"redo_total": self.get_redo_total_query(timeframe)}, select_params=(start, end))\ + .extra(select={"redo_perc": self.get_redo_perc_correct_query(timeframe)}, select_params=(start, end)) else: qs = qs.extra(select={"total": self.get_total_query(timeframe)})\ - .extra(select={"perc": self.get_perc_correct_query(timeframe)}) + .extra(select={"perc": self.get_perc_correct_query(timeframe)})\ + .extra(select={"redo_total": self.get_redo_total_query(timeframe)})\ + .extra(select={"redo_perc": self.get_redo_perc_correct_query(timeframe)}) return qs diff --git a/oneplus/learn_views.py b/oneplus/learn_views.py index c9d1e438..cc75444c 100644 --- a/oneplus/learn_views.py +++ b/oneplus/learn_views.py @@ -13,7 +13,8 @@ from content.models import TestingQuestion, TestingQuestionOption, GoldenEgg, GoldenEggRewardLog, Event, \ EventParticipantRel, EventSplashPage, EventStartPage, EventQuestionRel, EventQuestionAnswer, \ EventEndPage, SUMitLevel, SUMit, SUMitEndPage -from core.models import Participant, ParticipantQuestionAnswer, ParticipantBadgeTemplateRel, Setting +from core.models import Participant, ParticipantQuestionAnswer, ParticipantBadgeTemplateRel, Setting, \ + ParticipantRedoQuestionAnswer from gamification.models import GamificationScenario from organisation.models import CourseModuleRel from oneplus.models import LearnerState @@ -161,6 +162,19 @@ def home(request, state, user): ).count() request.session["state"]["home_goal"] = settings.ONEPLUS_WIN_REQUIRED - request.session["state"]["home_correct"] + redo = None + if (request.session["state"]["home_tasks_week"] >= 15) or \ + (request.session["state"]["home_tasks_today"] >= request.session["state"]["home_required_tasks"]): + correct = ParticipantQuestionAnswer.objects.filter( + participant=_participant, correct=True).distinct().values('question') + redo_correct = ParticipantRedoQuestionAnswer.objects.filter( + participant=_participant, correct=True).distinct().values('question') + answered = ParticipantQuestionAnswer.objects.filter( + participant=_participant).distinct().values('question') + questions = TestingQuestion.objects.filter(id__in=answered).exclude(id__in=correct).exclude(id__in=redo_correct) + if questions: + redo = True + def get(): _learner = Learner.objects.get(id=user['id']) if _learner.last_active_date is None: @@ -185,6 +199,7 @@ def get(): "user": user, "first_sitting": first_sitting, "event_name": event_name, + "redo": redo, "sumit": sumit}) def post(): @@ -358,6 +373,346 @@ def post(): return resolve_http_method(request, [get, post]) +@oneplus_state_required +@oneplus_login_required +def redo(request, state, user): + # get learner state + _participant = Participant.objects.get(pk=user["participant_id"]) + _learnerstate = LearnerState.objects.filter( + participant__id=user["participant_id"] + ).first() + + if _learnerstate is None: + _learnerstate = LearnerState(participant=_participant) + + # check if new question required then show question + _learnerstate.get_next_redo_question() + + answered = ParticipantQuestionAnswer.objects.filter( + participant=_learnerstate.participant + ).distinct().values_list('question') + correct = ParticipantQuestionAnswer.objects.filter( + participant=_learnerstate.participant, correct=True + ).distinct().values_list('question') + questions = TestingQuestion.objects.filter(id__in=answered).exclude(id__in=correct) + + if not questions: + return redirect("learn.home") + + if _learnerstate.redo_question: + question_id = _learnerstate.redo_question.id + request.session["state"]["question_id"] = "" + + def get(): + return render(request, "learn/redo.html", { + "state": state, + "user": user, + "question": _learnerstate.redo_question, + }) + + def post(): + request.session["state"]["report_sent"] = False + + # answer provided + if "answer" in request.POST.keys(): + _ans_id = request.POST["answer"] + + options = _learnerstate.redo_question.testingquestionoption_set + try: + _option = options.get(pk=_ans_id) + except TestingQuestionOption.DoesNotExist: + return redirect("learn.redo") + + _learnerstate.active_redo_result = _option.correct + _learnerstate.save() + + # Answer question + _participant.answer_redo(_option.question, _option) + + # Check for awards + if _option.correct: + return redirect("learn.redo_right") + else: + return redirect("learn.redo_wrong") + else: + return redirect("learn.home") + + return resolve_http_method(request, [get, post]) + + +@oneplus_state_required +@oneplus_login_required +def redo_right(request, state, user): + # get learner state + _participant = Participant.objects.get(pk=user["participant_id"]) + _learnerstate = LearnerState.objects.filter( + participant=_participant + ).first() + + if _learnerstate.redo_question: + question_id = _learnerstate.redo_question.id + request.session["state"]["question_id"] = "" + + questions = len(_learnerstate.get_redo_questions()) + + _usr = Learner.objects.get(pk=user["id"]) + request.session["state"]["banned"] = _usr.is_banned() + + def get(): + if _learnerstate.active_redo_result: + # Max discussion page + request.session["state"]["discussion_page_max"] = \ + Discussion.objects.filter( + course=_participant.classs.course, + question=_learnerstate.redo_question, + moderated=True, + reply=None + ).count() + + # Discussion page? + request.session["state"]["discussion_page"] = \ + min(2, request.session["state"]["discussion_page_max"]) + + # Messages for discussion page + _messages = \ + Discussion.objects.filter( + course=_participant.classs.course, + question=_learnerstate.redo_question, + moderated=True, + reply=None + ).order_by("-publishdate")[:request.session["state"]["discussion_page"]] + + return render( + request, + "learn/redo_right.html", + { + "state": state, + "user": user, + "question": _learnerstate.redo_question, + "messages": _messages, + "questions": questions + } + ) + else: + return HttpResponseRedirect("redo_wrong") + + def post(): + if _learnerstate.active_redo_result: + request.session["state"]["discussion_comment"] = False + request.session["state"]["discussion_responded_id"] = None + request.session["state"]["report_sent"] = False + + # new comment created + if "comment" in request.POST.keys() and request.POST["comment"] != "": + _comment = request.POST["comment"] + _message = Discussion( + course=_participant.classs.course, + question=_learnerstate.redo_question, + reply=None, + content=_comment, + author=_usr, + publishdate=datetime.now(), + moderated=True + ) + _message.save() + _content_profanity_check(_message) + request.session["state"]["discussion_comment"] = True + request.session["state"]["discussion_response_id"] = None + + elif "reply" in request.POST.keys() and request.POST["reply"] != "": + _comment = request.POST["reply"] + _parent = Discussion.objects.get( + pk=request.POST["reply_button"] + ) + _message = Discussion( + course=_participant.classs.course, + question=_learnerstate.redo_question, + reply=_parent, + content=_comment, + author=_usr, + publishdate=datetime.now(), + moderated=True + ) + _message.save() + _content_profanity_check(_message) + request.session["state"]["discussion_responded_id"] \ + = request.session["state"]["discussion_response_id"] + request.session["state"]["discussion_response_id"] = None + + # show more comments + elif "page" in request.POST.keys(): + request.session["state"]["discussion_page"] += 2 + if request.session["state"]["discussion_page"] \ + > request.session["state"]["discussion_page_max"]: + request.session["state"]["discussion_page"] \ + = request.session["state"]["discussion_page_max"] + request.session["state"]["discussion_response_id"] = None + + # show reply to comment comment + elif "comment_response_button" in request.POST.keys(): + request.session["state"]["discussion_response_id"] \ + = request.POST["comment_response_button"] + + _messages = \ + Discussion.objects.filter( + course=_participant.classs.course, + question=_learnerstate.redo_question, + moderated=True, + reply=None + ).order_by("-publishdate")[:request.session["state"]["discussion_page"]] + + return render( + request, + "learn/redo_right.html", + { + "state": state, + "user": user, + "question": _learnerstate.redo_question, + "messages": _messages, + } + ) + else: + return HttpResponseRedirect("redo_wrong") + + return resolve_http_method(request, [get, post]) + + +@oneplus_state_required +@oneplus_login_required +def redo_wrong(request, state, user): + # get learner state + _participant = Participant.objects.get(pk=user["participant_id"]) + _learnerstate = LearnerState.objects.filter( + participant=_participant + ).first() + + if _learnerstate.redo_question: + question_id = _learnerstate.redo_question.id + request.session["state"]["question_id"] = "" + questions = len(_learnerstate.get_redo_questions()) + + _usr = Learner.objects.get(pk=user["id"]) + + request.session["state"]["banned"] = _usr.is_banned() + + def get(): + if not _learnerstate.active_redo_result: + request.session["state"]["discussion_page_max"] = \ + Discussion.objects.filter( + course=_participant.classs.course, + question=_learnerstate.redo_question, + moderated=True, + reply=None + ).count() + + request.session["state"]["discussion_page"] = \ + min(2, request.session["state"]["discussion_page_max"]) + + _messages = \ + Discussion.objects.filter( + course=_participant.classs.course, + question=_learnerstate.redo_question, + moderated=True, + reply=None + ).order_by("-publishdate")[:request.session["state"]["discussion_page"]] + + return render( + request, + "learn/redo_wrong.html", + {"state": state, + "user": user, + "question": _learnerstate.redo_question, + "messages": _messages, + "questions": questions + } + ) + else: + return HttpResponseRedirect("redo_right") + + def post(): + if not _learnerstate.active_redo_result: + request.session["state"]["discussion_comment"] = False + request.session["state"]["discussion_responded_id"] = None + request.session["state"]["report_sent"] = False + + # new comment created + if "comment" in request.POST.keys() and request.POST["comment"] != "": + _comment = request.POST["comment"] + _message = Discussion( + course=_participant.classs.course, + question=_learnerstate.redo_question, + reply=None, + content=_comment, + author=_usr, + publishdate=datetime.now(), + moderated=True + ) + _message.save() + _content_profanity_check(_message) + request.session["state"]["discussion_comment"] = True + request.session["state"]["discussion_response_id"] = None + + elif "reply" in request.POST.keys() and request.POST["reply"] != "": + _comment = request.POST["reply"] + _parent = Discussion.objects.get( + pk=request.POST["reply_button"] + ) + _message = Discussion( + course=_participant.classs.course, + question=_learnerstate.redo_question, + reply=_parent, + content=_comment, + author=_usr, + publishdate=datetime.now(), + moderated=True + ) + _message.save() + _content_profanity_check(_message) + request.session["state"]["discussion_responded_id"] \ + = request.session["state"]["discussion_response_id"] + request.session["state"]["discussion_response_id"] = None + + # show more comments + elif "page" in request.POST.keys(): + request.session["state"]["discussion_page"] += 2 + if request.session["state"]["discussion_page"] \ + > request.session["state"]["discussion_page_max"]: + request.session["state"]["discussion_page"] \ + = request.session["state"]["discussion_page_max"] + request.session["state"]["discussion_response_id"] = None + + # show reply to comment comment + elif "comment_response_button" in request.POST.keys(): + request.session["state"]["discussion_response_id"] \ + = request.POST["comment_response_button"] + + _messages = \ + Discussion.objects.filter( + course=_participant.classs.course, + question=_learnerstate.redo_question, + moderated=True, + reply=None + ).order_by("-publishdate")[:request.session["state"]["discussion_page"]] + + return render( + request, + "learn/redo_wrong.html", + { + "state": state, + "user": user, + "question": _learnerstate.redo_question, + "messages": _messages + } + ) + else: + return HttpResponseRedirect("right") + + return resolve_http_method(request, [get, post]) + + @oneplus_state_required @oneplus_login_required def event(request, state, user): @@ -850,23 +1205,6 @@ def get_badge_awarded(participant): return badgetemplate, badgepoints, -def ts_awarded(participant): - # Get current participant question answer - answer = ParticipantQuestionAnswer.objects.filter( - participant=participant, - ).latest('answerdate') - - # Get question - question = answer.question - - # Get points - if question.points is 0: - question.points = 1 - question.save() - - return question.points - - def get_golden_egg(participant): golden_egg = GoldenEgg.objects.filter( classs=participant.classs, diff --git a/oneplus/migrations/0003_auto__add_field_learnerstate_redo_question.py b/oneplus/migrations/0003_auto__add_field_learnerstate_redo_question.py new file mode 100644 index 00000000..d1dea2a0 --- /dev/null +++ b/oneplus/migrations/0003_auto__add_field_learnerstate_redo_question.py @@ -0,0 +1,231 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'LearnerState.redo_question' + db.add_column(u'oneplus_learnerstate', 'redo_question', + self.gf('django.db.models.fields.related.ForeignKey')(related_name='rquestion', null=True, to=orm['content.TestingQuestion']), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'LearnerState.redo_question' + db.delete_column(u'oneplus_learnerstate', 'redo_question_id') + + + models = { + u'auth.customuser': { + 'Meta': {'object_name': 'CustomUser'}, + 'area': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'mobile': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'}), + 'optin_email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'optin_sms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'unique_token': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}), + 'unique_token_expiry': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.learner': { + 'Meta': {'object_name': 'Learner', '_ormbases': [u'auth.CustomUser']}, + u'customuser_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.CustomUser']", 'unique': 'True', 'primary_key': 'True'}), + 'enrolled': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'blank': 'True'}), + 'grade': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'last_active_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_maths_result': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'school': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['organisation.School']", 'null': 'True'}), + 'welcome_message': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['communication.Sms']", 'null': 'True', 'blank': 'True'}), + 'welcome_message_sent': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'communication.sms': { + 'Meta': {'object_name': 'Sms'}, + 'date_sent': ('django.db.models.fields.DateTimeField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'message': ('django.db.models.fields.TextField', [], {}), + 'msisdn': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'respond_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'responded': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'response': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['communication.SmsQueue']", 'null': 'True', 'blank': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}) + }, + u'communication.smsqueue': { + 'Meta': {'object_name': 'SmsQueue'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'message': ('django.db.models.fields.TextField', [], {}), + 'msisdn': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'db_index': 'True'}), + 'send_date': ('django.db.models.fields.DateTimeField', [], {}), + 'sent': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'sent_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}) + }, + u'content.testingquestion': { + 'Meta': {'ordering': "['name']", 'object_name': 'TestingQuestion'}, + 'answer_content': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + 'difficulty': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'module': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['organisation.Module']", 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'points': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'question_content': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'state': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'textbook_link': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'core.class': { + 'Meta': {'object_name': 'Class'}, + 'course': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['organisation.Course']", 'null': 'True'}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + 'enddate': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'startdate': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'type': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}) + }, + u'core.participant': { + 'Meta': {'object_name': 'Participant'}, + 'badgetemplate': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['gamification.GamificationBadgeTemplate']", 'symmetrical': 'False', 'through': u"orm['core.ParticipantBadgeTemplateRel']", 'blank': 'True'}), + 'classs': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Class']"}), + 'datejoined': ('django.db.models.fields.DateTimeField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'learner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.Learner']"}), + 'pointbonus': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['gamification.GamificationPointBonus']", 'symmetrical': 'False', 'through': u"orm['core.ParticipantPointBonusRel']", 'blank': 'True'}), + 'points': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + u'core.participantbadgetemplaterel': { + 'Meta': {'object_name': 'ParticipantBadgeTemplateRel'}, + 'awarddate': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 6, 23, 0, 0)', 'null': 'True'}), + 'badgetemplate': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gamification.GamificationBadgeTemplate']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'participant': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Participant']"}), + 'scenario': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gamification.GamificationScenario']"}) + }, + u'core.participantpointbonusrel': { + 'Meta': {'object_name': 'ParticipantPointBonusRel'}, + 'awarddate': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 6, 23, 0, 0)', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'participant': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Participant']"}), + 'pointbonus': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gamification.GamificationPointBonus']"}), + 'scenario': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gamification.GamificationScenario']"}) + }, + u'gamification.gamificationbadgetemplate': { + 'Meta': {'object_name': 'GamificationBadgeTemplate'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + }, + u'gamification.gamificationpointbonus': { + 'Meta': {'object_name': 'GamificationPointBonus'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'value': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}) + }, + u'gamification.gamificationscenario': { + 'Meta': {'object_name': 'GamificationScenario'}, + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gamification.GamificationBadgeTemplate']", 'null': 'True', 'blank': 'True'}), + 'course': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['organisation.Course']", 'null': 'True'}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + 'event': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'module': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['organisation.Module']", 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'point': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gamification.GamificationPointBonus']", 'null': 'True', 'blank': 'True'}) + }, + u'oneplus.learnerstate': { + 'Meta': {'object_name': 'LearnerState'}, + 'active_question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'aquestion'", 'null': 'True', 'to': u"orm['content.TestingQuestion']"}), + 'active_result': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'golden_egg_question': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'participant': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Participant']", 'null': 'True'}), + 'redo_question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'rquestion'", 'null': 'True', 'to': u"orm['content.TestingQuestion']"}) + }, + u'organisation.course': { + 'Meta': {'object_name': 'Course'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'question_order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}) + }, + u'organisation.coursemodulerel': { + 'Meta': {'object_name': 'CourseModuleRel'}, + 'course': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['organisation.Course']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'module': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['organisation.Module']"}) + }, + u'organisation.module': { + 'Meta': {'object_name': 'Module'}, + 'courses': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'modules'", 'symmetrical': 'False', 'through': u"orm['organisation.CourseModuleRel']", 'to': u"orm['organisation.Course']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'module_link': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'type': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}) + }, + u'organisation.organisation': { + 'Meta': {'object_name': 'Organisation'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + u'organisation.school': { + 'Meta': {'object_name': 'School'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'organisation': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['organisation.Organisation']", 'null': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + } + } + + complete_apps = ['oneplus'] \ No newline at end of file diff --git a/oneplus/migrations/0004_auto__add_field_learnerstate_active_redo_result.py b/oneplus/migrations/0004_auto__add_field_learnerstate_active_redo_result.py new file mode 100644 index 00000000..f154d19d --- /dev/null +++ b/oneplus/migrations/0004_auto__add_field_learnerstate_active_redo_result.py @@ -0,0 +1,232 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'LearnerState.active_redo_result' + db.add_column(u'oneplus_learnerstate', 'active_redo_result', + self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'LearnerState.active_redo_result' + db.delete_column(u'oneplus_learnerstate', 'active_redo_result') + + + models = { + u'auth.customuser': { + 'Meta': {'object_name': 'CustomUser'}, + 'area': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'mobile': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'}), + 'optin_email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'optin_sms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'unique_token': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}), + 'unique_token_expiry': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.learner': { + 'Meta': {'object_name': 'Learner', '_ormbases': [u'auth.CustomUser']}, + u'customuser_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.CustomUser']", 'unique': 'True', 'primary_key': 'True'}), + 'enrolled': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'blank': 'True'}), + 'grade': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'last_active_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_maths_result': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'school': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['organisation.School']", 'null': 'True'}), + 'welcome_message': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['communication.Sms']", 'null': 'True', 'blank': 'True'}), + 'welcome_message_sent': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'communication.sms': { + 'Meta': {'object_name': 'Sms'}, + 'date_sent': ('django.db.models.fields.DateTimeField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'message': ('django.db.models.fields.TextField', [], {}), + 'msisdn': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'respond_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'responded': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'response': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['communication.SmsQueue']", 'null': 'True', 'blank': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}) + }, + u'communication.smsqueue': { + 'Meta': {'object_name': 'SmsQueue'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'message': ('django.db.models.fields.TextField', [], {}), + 'msisdn': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'db_index': 'True'}), + 'send_date': ('django.db.models.fields.DateTimeField', [], {}), + 'sent': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'sent_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}) + }, + u'content.testingquestion': { + 'Meta': {'ordering': "['name']", 'object_name': 'TestingQuestion'}, + 'answer_content': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + 'difficulty': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'module': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['organisation.Module']", 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'points': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'question_content': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'state': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'textbook_link': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'core.class': { + 'Meta': {'object_name': 'Class'}, + 'course': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['organisation.Course']", 'null': 'True'}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + 'enddate': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'startdate': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'type': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}) + }, + u'core.participant': { + 'Meta': {'object_name': 'Participant'}, + 'badgetemplate': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['gamification.GamificationBadgeTemplate']", 'symmetrical': 'False', 'through': u"orm['core.ParticipantBadgeTemplateRel']", 'blank': 'True'}), + 'classs': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Class']"}), + 'datejoined': ('django.db.models.fields.DateTimeField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'learner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.Learner']"}), + 'pointbonus': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['gamification.GamificationPointBonus']", 'symmetrical': 'False', 'through': u"orm['core.ParticipantPointBonusRel']", 'blank': 'True'}), + 'points': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + u'core.participantbadgetemplaterel': { + 'Meta': {'object_name': 'ParticipantBadgeTemplateRel'}, + 'awarddate': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 6, 23, 0, 0)', 'null': 'True'}), + 'badgetemplate': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gamification.GamificationBadgeTemplate']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'participant': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Participant']"}), + 'scenario': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gamification.GamificationScenario']"}) + }, + u'core.participantpointbonusrel': { + 'Meta': {'object_name': 'ParticipantPointBonusRel'}, + 'awarddate': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 6, 23, 0, 0)', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'participant': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Participant']"}), + 'pointbonus': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gamification.GamificationPointBonus']"}), + 'scenario': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gamification.GamificationScenario']"}) + }, + u'gamification.gamificationbadgetemplate': { + 'Meta': {'object_name': 'GamificationBadgeTemplate'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + }, + u'gamification.gamificationpointbonus': { + 'Meta': {'object_name': 'GamificationPointBonus'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'value': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}) + }, + u'gamification.gamificationscenario': { + 'Meta': {'object_name': 'GamificationScenario'}, + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gamification.GamificationBadgeTemplate']", 'null': 'True', 'blank': 'True'}), + 'course': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['organisation.Course']", 'null': 'True'}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + 'event': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'module': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['organisation.Module']", 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'point': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gamification.GamificationPointBonus']", 'null': 'True', 'blank': 'True'}) + }, + u'oneplus.learnerstate': { + 'Meta': {'object_name': 'LearnerState'}, + 'active_question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'aquestion'", 'null': 'True', 'to': u"orm['content.TestingQuestion']"}), + 'active_redo_result': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'active_result': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'golden_egg_question': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'participant': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.Participant']", 'null': 'True'}), + 'redo_question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'rquestion'", 'null': 'True', 'to': u"orm['content.TestingQuestion']"}) + }, + u'organisation.course': { + 'Meta': {'object_name': 'Course'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'question_order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}) + }, + u'organisation.coursemodulerel': { + 'Meta': {'object_name': 'CourseModuleRel'}, + 'course': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['organisation.Course']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'module': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['organisation.Module']"}) + }, + u'organisation.module': { + 'Meta': {'object_name': 'Module'}, + 'courses': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'modules'", 'symmetrical': 'False', 'through': u"orm['organisation.CourseModuleRel']", 'to': u"orm['organisation.Course']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'module_link': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'type': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}) + }, + u'organisation.organisation': { + 'Meta': {'object_name': 'Organisation'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + u'organisation.school': { + 'Meta': {'object_name': 'School'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'unique': 'True', 'null': 'True'}), + 'organisation': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['organisation.Organisation']", 'null': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + } + } + + complete_apps = ['oneplus'] \ No newline at end of file diff --git a/oneplus/models.py b/oneplus/models.py index f8499688..65161574 100644 --- a/oneplus/models.py +++ b/oneplus/models.py @@ -1,6 +1,6 @@ from django.db import models from content.models import TestingQuestion -from core.models import Participant, ParticipantQuestionAnswer +from core.models import Participant, ParticipantQuestionAnswer, ParticipantRedoQuestionAnswer from datetime import datetime, timedelta, time from random import randint from content.models import GoldenEgg, EventQuestionAnswer, EventQuestionRel, SUMit @@ -13,9 +13,17 @@ class LearnerState(models.Model): active_question = models.ForeignKey( TestingQuestion, null=True, - blank=False + blank=False, + related_name="aquestion" ) active_result = models.NullBooleanField() + redo_question = models.ForeignKey( + TestingQuestion, + null=True, + blank=False, + related_name="rquestion", + ) + active_redo_result = models.NullBooleanField() golden_egg_question = models.PositiveIntegerField(default=0) sumit_level = models.PositiveIntegerField(default=0) sumit_question = models.PositiveIntegerField(default=0) @@ -187,3 +195,24 @@ def getnextquestion(self): self.save() return self.active_question + + def get_next_redo_question(self): + if self.redo_question is None or self.active_redo_result is not None: + questions = self.get_redo_questions() + if questions.count() > 0: + self.redo_question = questions.order_by('?')[0] + self.active_redo_result = None + self.save() + + return self.redo_question + + def get_redo_questions(self): + correct = ParticipantQuestionAnswer.objects.filter( + participant=self.participant, correct=True).distinct().values('question') + redo_correct = ParticipantRedoQuestionAnswer.objects.filter( + participant=self.participant, correct=True).distinct().values('question') + answered = ParticipantQuestionAnswer.objects.filter( + participant=self.participant).distinct().values('question') + + return TestingQuestion.objects.filter(id__in=answered).exclude(id__in=correct).\ + exclude(id__in=redo_correct) diff --git a/oneplus/prog_views.py b/oneplus/prog_views.py index ec6d93e6..2063c3ec 100644 --- a/oneplus/prog_views.py +++ b/oneplus/prog_views.py @@ -21,12 +21,14 @@ def ontrack(request, state, user): for m in _modules: _answers = _participant.participantquestionanswer_set.filter( question__module__id=m.id) - if _answers.count() < 10: + _redo_answers = _participant.participantquestionanswer_set.filter( + question__module__id=m.id) + if (_answers.count() + _redo_answers.count()) < 10: m.score = -1 else: - m.score = _answers.filter( - correct=True - ).count() / _answers.count() * 100 + correct = _answers.filter(correct=True).count() + _redo_answers.filter(correct=True).count() + total = _answers.count() + _redo_answers.count() + m.score = correct / total * 100 def get(): return render( diff --git a/oneplus/static/css/oneplus.css b/oneplus/static/css/oneplus.css index 2ee0aa9e..53e3a485 100644 --- a/oneplus/static/css/oneplus.css +++ b/oneplus/static/css/oneplus.css @@ -591,3 +591,10 @@ ul.bullets{ list-style-type: disc; padding-left: 10px; } + +.redo { + font-size: 20px; + font-weight: bold; + color: #28e690; + text-decoration: underline; +} diff --git a/oneplus/templates/learn/home.html b/oneplus/templates/learn/home.html index 7c0e60bd..25f07380 100644 --- a/oneplus/templates/learn/home.html +++ b/oneplus/templates/learn/home.html @@ -42,9 +42,9 @@ {% if not state.questions_complete %} {% if state.home_tasks_week >= 15 %} -

This week's revision complete. Come back Monday for more.

+

This week's revision complete. Come back Monday for more{% if redo %} or redo today's incorrect answers{% endif %}.

{% elif state.home_tasks_today >= state.home_required_tasks %} -

Today's revision complete. Come back tomorrow for more.

+

Today's revision complete. Come back tomorrow for more{% if redo %} or redo today's incorrect answers{% endif %}.

{% else %} Go on Take the Challenge {% endif %} diff --git a/oneplus/templates/learn/redo.html b/oneplus/templates/learn/redo.html new file mode 100644 index 00000000..98655b6b --- /dev/null +++ b/oneplus/templates/learn/redo.html @@ -0,0 +1,42 @@ +{% extends "core/main.html" %} +{% load bleach_tags %} +{% load oneplus_extras %} +{% block title %}NEXT{% endblock %} + +{% block content %} + {% load humanize %} + {% if state.report_sent %} +
+

Thank you for your Report. A teacher will review the question.

+
+ {% endif %} + +
  • Today's Repeat Questions

    +

    Q {{ state.next_tasks_today }}/{{ state.total_tasks_today }}

    + + {% autoescape off %} + {{ state.question_id }} +
    +
    {% csrf_token %} +

    {{ question.question_content | format_content }}

    + {% for opt in question.testingquestionoption_set.all %} + +

    + {% else %} + {{ opt.name }}

    + {% endif %} + {% endfor %} +
    +
    + +
    +
    +
    + {% endautoescape %} + +
    +

    Find a mistake? Report it here

    +
    +{% endblock %} \ No newline at end of file diff --git a/oneplus/templates/learn/redo_right.html b/oneplus/templates/learn/redo_right.html new file mode 100644 index 00000000..f6f97dfe --- /dev/null +++ b/oneplus/templates/learn/redo_right.html @@ -0,0 +1,149 @@ +{% extends "core/main.html" %} +{% load bleach_tags %} +{% load oneplus_extras %} +{% block title %}RIGHT{% endblock %} + +{% block content %} + {% load humanize %} + + {% if state.report_sent %} +
    +

    Thank you for your Report. A teacher will review the question.

    +
    + {% endif %} + +
  • Today's Repeat Questions

    +

    Q {{ state.right_tasks_today }}/{{ state.total_tasks_today }}

    + + {% autoescape off %} + {{ state.question_id }} +
    +
    +
    Correct
    + {% if questions <= 0 %} + Done For Today + {% else %} + Next Question + {% endif %} +
    +
    + +
    +

    Question

    +

    {{ question.question_content | format_content }}

    + +

    Options

    + {% for opt in question.testingquestionoption_set.all %} + +

    + {% else %} + {{ opt.name }}

    + {% endif %} + {% endfor %} + +

    Correct Answer:

    + {% for opt in question.testingquestionoption_set.all %} + {% if opt.correct %} +

    Option {{ forloop.counter }}: + {% if opt.content %} +

    {{ opt.content | format_option }}
    + {% else %} + {{ opt.name }} + {% endif %} +

    + {% endif %} + {% endfor %} + +

    Solution

    +

    {{ question.answer_content | format_content }}

    +
    + {% endautoescape %} + +
    +

    Find a mistake? Report it here

    +
    + + {% if not state.banned %} +
    +
    Discuss the Challenge
    +
    {% csrf_token %} + + +
    + {% if state.discussion_comment %} +

    Thank you for your contribution. Your message will display shortly!

    + {% endif %} +
    + {% endif %} + + {% if request.session.state.discussion_page_max > 0 %} + + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/oneplus/templates/learn/redo_wrong.html b/oneplus/templates/learn/redo_wrong.html new file mode 100644 index 00000000..175561fc --- /dev/null +++ b/oneplus/templates/learn/redo_wrong.html @@ -0,0 +1,158 @@ +{% extends "core/main.html" %} +{% load bleach_tags %} +{% load oneplus_extras %} +{% block title %}WRONG{% endblock %} + +{% block content %} + {% load humanize %} + + {% if state.report_sent %} +
    +

    Thank you for your Report. A teacher will review the question.

    +
    + {% endif %} + +
  • Today's Repeat Questions

    +

    Q {{ state.wrong_tasks_today }}/{{ state.total_tasks_today }}

    + + {% autoescape off %} + {{ state.question_id }} +
    +
    + +
    Incorrect
    +
    Unfortunately that is not the answer
    + {% if question.textbook_link %} + +
    + Read the Textbook +
    +
    + {% endif %} + {% if questions <= 0 %} + Done For Today + {% else %} + Next Question + {% endif %} +
    +
    + +
    +

    Question

    +

    {{ question.question_content | format_content }}

    + +

    Options

    + {% for opt in question.testingquestionoption_set.all %} + +

    + {% else %} + {{ opt.name }}

    + {% endif %} + {% endfor %} + +

    Correct Answer:

    + {% for opt in question.testingquestionoption_set.all %} + {% if opt.correct %} +

    Option {{ forloop.counter }}: + {% if opt.content %} +

    {{ opt.content | format_option }}
    + {% else %} + {{ opt.name }} + {% endif %} +

    + {% endif %} + {% endfor %} + +

    Solution

    +

    {{ question.answer_content | format_content}}

    +
    + {% endautoescape %} + +
    +

    Find a mistake? Report it here

    +
    + + {% if not state.banned %} +
    +
    Discuss the Challenge
    +
    {% csrf_token %} + + +
    + {% if state.discussion_comment %} +

    Thank you for your contribution. Your message will display shortly!

    + {% endif %} +
    + {% endif %} + + {% if request.session.state.discussion_page_max > 0 %} + + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/oneplus/tests.py b/oneplus/tests.py index 27860e48..1fbecab7 100644 --- a/oneplus/tests.py +++ b/oneplus/tests.py @@ -4,7 +4,8 @@ from django.core.urlresolvers import reverse from django.test import TestCase -from core.models import Participant, Class, Course, ParticipantQuestionAnswer, ParticipantBadgeTemplateRel, Setting +from core.models import Participant, Class, Course, ParticipantQuestionAnswer, ParticipantBadgeTemplateRel, Setting, \ + ParticipantRedoQuestionAnswer from organisation.models import Organisation, School, Module, CourseModuleRel from content.models import TestingQuestion, TestingQuestionOption, GoldenEgg, GoldenEggRewardLog, Event,\ EventQuestionRel, EventSplashPage, EventStartPage, EventEndPage, EventQuestionAnswer, EventParticipantRel, SUMit, \ @@ -287,6 +288,21 @@ def test_home(self): resp = self.client.get(reverse('learn.home')) self.assertContains(resp, "5
    POINTS") + for i in range(1, 15): + question = TestingQuestion.objects.create(name="Question %d" % i, module=self.module) + option = TestingQuestionOption.objects.create(name="Option %d.1" % i, question=question, correct=True) + ParticipantQuestionAnswer.objects.create(participant=self.participant, question=question, + option_selected=option, correct=True) + + question = TestingQuestion.objects.create(name="Question %d" % 15, module=self.module) + option = TestingQuestionOption.objects.create(name="Option %d.1" % 15, question=question, correct=False) + ParticipantQuestionAnswer.objects.create(participant=self.participant, question=question, + option_selected=option, correct=False) + + resp = self.client.get(reverse('learn.home')) + self.assertEquals(resp.status_code, 200) + self.assertContains(resp, "redo today's incorrect answers") + # change event type to sumit event.type = 0 event.save() @@ -676,6 +692,111 @@ def test_sumit(self): self.assertRedirects(resp, "sumit_wrong") self.assertContains(resp, "Finish") + def test_redo(self): + self.client.get(reverse('auth.autologin', kwargs={'token': self.learner.unique_token})) + + resp = self.client.get(reverse("learn.redo")) + self.assertRedirects(resp, "home") + + for i in range(1, 15): + question = TestingQuestion.objects.create(name="Question %d" % i, module=self.module) + correct_option = TestingQuestionOption.objects.create(name="Option %d.1" % i, question=question, correct=True) + ParticipantQuestionAnswer.objects.create(participant=self.participant, question=question, + option_selected=correct_option, correct=True) + + question = TestingQuestion.objects.create(name="Question %d" % 15, module=self.module) + incorrect_option = TestingQuestionOption.objects.create(name="Option %d.1" % 15, question=question, + correct=False) + correct_option = TestingQuestionOption.objects.create(name="Option %d.2" % 15, question=question, correct=True) + ParticipantQuestionAnswer.objects.create(participant=self.participant, question=question, + option_selected=incorrect_option, correct=False) + + resp = self.client.post(reverse('learn.redo'), follow=True) + self.assertEquals(resp.status_code, 200) + + resp = self.client.post(reverse('learn.redo'), + data={'answer': 99}, + follow=True) + self.assertEquals(resp.status_code, 200) + + resp = self.client.post(reverse('learn.redo'), + data={'answer': correct_option.id}, + follow=True) + self.assertEquals(resp.status_code, 200) + self.assertRedirects(resp, "redo_right") + + resp = self.client.post(reverse("learn.redo_right")) + self.assertEquals(resp.status_code, 200) + + resp = self.client.post(reverse("learn.redo_right"), + data={"comment": "test"}, + follow=True) + + self.assertEquals(resp.status_code, 200) + + disc = Discussion.objects.all().first() + + resp = self.client.post( + reverse('learn.redo_right'), + data={ + 'reply': 'test', + "reply_button": disc.id + }, + follow=True + ) + + resp = self.client.post( + reverse('learn.redo_right'), + data={'page': 1}, + follow=True + ) + + self.assertEquals(resp.status_code, 200) + + self.assertEquals(resp.status_code, 200) + self.assertEquals(Discussion.objects.all().count(), 2) + + disc.delete() + ParticipantRedoQuestionAnswer.objects.filter(participant=self.participant, question=question).delete() + + resp = self.client.post(reverse('learn.redo'), + data={'answer': incorrect_option.id}, + follow=True) + self.assertEquals(resp.status_code, 200) + self.assertRedirects(resp, "redo_wrong") + + resp = self.client.post(reverse("learn.redo_wrong")) + self.assertEquals(resp.status_code, 200) + + resp = self.client.post( + reverse('learn.redo_wrong'), + data={'comment': 'test'}, follow=True + ) + + self.assertEquals(resp.status_code, 200) + + disc = Discussion.objects.all().first() + + resp = self.client.post( + reverse('learn.redo_wrong'), + data={ + 'reply': 'test', + "reply_button": disc.id + }, + follow=True + ) + + self.assertEquals(resp.status_code, 200) + self.assertEquals(Discussion.objects.all().count(), 2) + + resp = self.client.post( + reverse('learn.redo_wrong'), + data={'page': 1}, + follow=True + ) + + self.assertEquals(resp.status_code, 200) + def fake_update_all_perc_correct_answers(): update_perc_correct_answers_worker('24hr', 1) update_perc_correct_answers_worker('48hr', 2) @@ -2400,7 +2521,6 @@ def test_leaderboard_screen(self): for y in range(0, counter+1): all_particpants[counter].answer(question_list[y], question_option_list[y]) - #data for class leaderboard new_class = self.create_class('class_%s' % x, self.course) all_learners_classes.append(self.create_learner(self.school, @@ -2512,12 +2632,18 @@ def test_ontrack_screen(self): reverse('auth.autologin', kwargs={'token': self.learner.unique_token}) ) + resp = self.client.get(reverse('prog.ontrack')) self.assertEquals(resp.status_code, 200) resp = self.client.post(reverse('prog.ontrack'), follow=True) self.assertEquals(resp.status_code, 200) + #more than 10 answered + self.create_and_answer_questions(11, "name", datetime.now()) + resp = self.client.get(reverse('prog.ontrack')) + self.assertEquals(resp.status_code, 200) + def test_bloglist_screen(self): self.client.get(reverse( 'auth.autologin', @@ -3149,7 +3275,11 @@ def test_golden_egg(self): new_participant.save() #GOLDEN EGG INACTIVE - golden_egg = GoldenEgg.objects.create(course=self.course, classs=self.classs, active=False, point_value=5) + golden_egg_badge = self.create_badgetemplate('golden egg') + golden_egg_point = self.create_gamification_point_bonus('golden egg', 5) + golden_egg_scenario = self.create_gamification_scenario(badge=golden_egg_badge, point=golden_egg_point) + golden_egg = GoldenEgg.objects.create(course=self.course, classs=self.classs, active=False, point_value=5, + badge=golden_egg_scenario) #set the golden egg number to 1 self.client.get(reverse('learn.next')) @@ -3180,7 +3310,7 @@ def test_golden_egg(self): self.client.post(reverse('learn.next'), data={'answer': q_o.id}, follow=True) new_participant = Participant.objects.filter(learner=new_learner).first() - self.assertEquals(6, new_participant.points) + self.assertEquals(11, new_participant.points) log = GoldenEggRewardLog.objects.filter(participant=new_participant, points=5).count() self.assertEquals(1, log) @@ -3205,8 +3335,6 @@ def test_golden_egg(self): ParticipantQuestionAnswer.objects.filter(participant=new_participant, question=q, option_selected=q_o).delete() - new_participant.points = 0 - new_participant.save() #TEST BADGE bt1 = GamificationBadgeTemplate.objects.get(name="Golden Egg") @@ -3229,8 +3357,6 @@ def test_golden_egg(self): scenario=sc1 ).count() self.assertEquals(cnt, 1) - # we are using the existing golden egg basge with no points - self.assertEquals(1, new_participant.points) log = GoldenEggRewardLog.objects.filter(participant=new_participant, badge=sc1).count() self.assertEquals(1, log) #check log diff --git a/oneplus/urls.py b/oneplus/urls.py index 78d0b201..2d743033 100644 --- a/oneplus/urls.py +++ b/oneplus/urls.py @@ -38,6 +38,9 @@ url(r"^event$", oneplus.learn_views.event, name="learn.event"), url(r"^event_right$", oneplus.learn_views.event_right, name="learn.event_right"), url(r"^event_wrong$", oneplus.learn_views.event_wrong, name="learn.event_wrong"), + url(r"^redo$", oneplus.learn_views.redo, name="learn.redo"), + url(r"^redo_right$", oneplus.learn_views.redo_right, name="learn.redo_right"), + url(r"^redo_wrong$", oneplus.learn_views.redo_wrong, name="learn.redo_wrong"), url(r"^sumit$", oneplus.learn_views.sumit, name="learn.sumit"), url(r"^sumit_right$", oneplus.learn_views.sumit_right, name="learn.sumit_right"), url(r"^sumit_wrong$", oneplus.learn_views.sumit_wrong, name="learn.sumit_wrong"),