From 0808861705c08aede6c0dc9e07ac891911a7bbfb Mon Sep 17 00:00:00 2001 From: prathamesh Date: Tue, 23 Sep 2014 14:44:47 +0530 Subject: Models and views modified. Added number of attempts option for quiz. Also days lag between two attempts. added test status and attempt number for the test. Removed profile foreign key from answer paper models since user foreign key is present in the answer paper model. Urls slightly modified to include attemt number for a given quiz. --- testapp/exam/forms.py | 14 ++++ testapp/exam/models.py | 33 +++++++-- testapp/exam/templates/exam/intro.html | 2 +- testapp/exam/templates/exam/question.html | 9 +-- testapp/exam/templates/exam/quit.html | 4 +- testapp/exam/urls.py | 8 +-- testapp/exam/views.py | 107 ++++++++++++++++++------------ 7 files changed, 113 insertions(+), 64 deletions(-) (limited to 'testapp') diff --git a/testapp/exam/forms.py b/testapp/exam/forms.py index 9bfedbe..9d68ce2 100644 --- a/testapp/exam/forms.py +++ b/testapp/exam/forms.py @@ -32,6 +32,12 @@ question_types = ( UNAME_CHARS = letters + "._" + digits PWD_CHARS = letters + punctuation + digits +attempts = [(i, i) for i in range(1, 6)] +attempts.append((-1, 'Infinite')) + +days_between_attempts = ((j, j) for j in range(401)) + + class UserRegisterForm(forms.Form): """A Class to create new form for User's Registration. It has the various fields and functions required to register @@ -143,6 +149,10 @@ class QuizForm(forms.Form): pass_criteria = forms.FloatField(initial=40, help_text='Will be taken as percentage') language = forms.CharField(widget=forms.Select(choices=languages)) + attempts_allowed = forms.IntegerField(widget=forms.Select(choices=attempts)) + time_between_attempts = forms.IntegerField\ + (widget=forms.Select(choices=days_between_attempts), + help_text='Will be in days') def save(self): start_date = self.cleaned_data["start_date"] @@ -152,6 +162,8 @@ class QuizForm(forms.Form): pass_criteria = self.cleaned_data["pass_criteria"] language = self.cleaned_data["language"] prerequisite = self.cleaned_data["prerequisite"] + attempts_allowed = self.cleaned_data["attempts_allowed"] + time_between_attempts = self.cleaned_data["time_between_attempts"] new_quiz = Quiz() new_quiz.start_date = start_date new_quiz.duration = duration @@ -160,6 +172,8 @@ class QuizForm(forms.Form): new_quiz.pass_criteria = pass_criteria new_quiz.language = language new_quiz.prerequisite_id = prerequisite + new_quiz.attempts_allowed = attempts_allowed + new_quiz.time_between_attempts = time_between_attempts new_quiz.save() diff --git a/testapp/exam/models.py b/testapp/exam/models.py index 196ee73..259f875 100644 --- a/testapp/exam/models.py +++ b/testapp/exam/models.py @@ -31,7 +31,15 @@ question_types = ( ("mcc", "Multiple Correct Choices"), ("code", "Code"), ) +attempts = [(i, i) for i in range(1, 6)] +attempts.append((-1, 'Infinite')) +days_between_attempts = ((j, j) for j in range(401)) + +test_status = ( + ('inprogress', 'Inprogress'), + ('completed', 'Completed'), + ) ############################################################################### class Question(models.Model): @@ -125,6 +133,12 @@ class Quiz(models.Model): # Programming language for a quiz language = models.CharField(max_length=20, choices=languages) + # Number of attempts for the quiz + attempts_allowed = models.IntegerField(default=1, choices=attempts) + + time_between_attempts = models.IntegerField("Number of Days",\ + choices=days_between_attempts) + class Meta: verbose_name_plural = "Quizzes" @@ -171,9 +185,9 @@ class QuestionPaper(models.Model): questions += question_set.get_random_questions() return questions - def make_answerpaper(self, user, ip): + def make_answerpaper(self, user, ip, attempt_no): """Creates an answer paper for the user to attempt the quiz""" - ans_paper = AnswerPaper(user=user, profile=user.profile, user_ip=ip) + ans_paper = AnswerPaper(user=user, user_ip=ip, attempt_number=attempt_no) ans_paper.start_time = datetime.datetime.now() ans_paper.end_time = ans_paper.start_time \ + datetime.timedelta(minutes=self.quiz.duration) @@ -214,10 +228,6 @@ class AnswerPaper(models.Model): # The user taking this question paper. user = models.ForeignKey(User) - # The user's profile, we store a reference to make it easier to access the - # data. - profile = models.ForeignKey(Profile) - # All questions that remain to be attempted for a particular Student # (a list of ids separated by '|') questions = models.CharField(max_length=128) @@ -225,6 +235,9 @@ class AnswerPaper(models.Model): # The Quiz to which this question paper is attached to. question_paper = models.ForeignKey(QuestionPaper) + # The attempt number for the question paper. + attempt_number = models.IntegerField() + # The time when this paper was started by the user. start_time = models.DateTimeField() @@ -252,6 +265,10 @@ class AnswerPaper(models.Model): # Result of the quiz, True if student passes the exam. passed = models.NullBooleanField() + # Status of the quiz attempt + status = models.CharField(max_length=20, choices=test_status,\ + default='inprogress') + def current_question(self): """Returns the current active question to display.""" qs = self.questions.split('|') @@ -343,6 +360,10 @@ class AnswerPaper(models.Model): else: self.passed = False + def update_status(self): + """ Sets status to completed """ + self.status = 'completed' + def get_question_answers(self): """ Return a dictionary with keys as questions and a list of the diff --git a/testapp/exam/templates/exam/intro.html b/testapp/exam/templates/exam/intro.html index ec1888a..58068ed 100644 --- a/testapp/exam/templates/exam/intro.html +++ b/testapp/exam/templates/exam/intro.html @@ -27,7 +27,7 @@

We hope you enjoy taking this exam !!!

-
+ {% csrf_token %}
diff --git a/testapp/exam/templates/exam/question.html b/testapp/exam/templates/exam/question.html index a3e8629..855a29e 100644 --- a/testapp/exam/templates/exam/question.html +++ b/testapp/exam/templates/exam/question.html @@ -74,7 +74,7 @@ function setSnippetHeight() -
+ {% csrf_token %} @@ -94,13 +94,8 @@ function setSnippetHeight() {% endfor%} {% endif %} - {% if success_msg %} - - {% endif %}

- + {% csrf_token %} {% if question.type == "mcq" %} {% for option in question.options.strip.splitlines %} diff --git a/testapp/exam/templates/exam/quit.html b/testapp/exam/templates/exam/quit.html index fee11ed..2c347cb 100644 --- a/testapp/exam/templates/exam/quit.html +++ b/testapp/exam/templates/exam/quit.html @@ -7,8 +7,8 @@

Your current answers are saved.

Are you sure you wish to quit the exam?

Be sure, as you won't be able to restart this exam.

- + {% csrf_token %} -
 
+
 
{% endblock content %} diff --git a/testapp/exam/urls.py b/testapp/exam/urls.py index 37a031d..d553941 100644 --- a/testapp/exam/urls.py +++ b/testapp/exam/urls.py @@ -6,15 +6,15 @@ urlpatterns = patterns('exam.views', url(r'^quizzes/$','quizlist_user'), url(r'^results/$','results_user'), url(r'^start/$', 'start'), - url(r'^start/(?P\d+)/$','start'), - url(r'^quit/(?P\d+)/$', 'quit'), + url(r'^start/(?P\d+)/(?P\d+)/$','start'), + url(r'^quit/(?P\d+)/(?P\d+)/$', 'quit'), url(r'^intro/(?P\d+)/$','intro'), url(r'^complete/$', 'complete'), - url(r'^complete/(?P\d+)/$', 'complete'), + url(r'^complete/(?P\d+)/(?P\d+)/$', 'complete'), url(r'^register/$', 'user_register'), url(r'^(?P\d+)/$', 'question'), url(r'^(?P\d+)/check/$', 'check'), - url(r'^(?P\d+)/check/(?P\d+)/$', 'check'), + url(r'^(?P\d+)/check/(?P\d+)/(?P\d+)/$', 'check'), url(r'^intro/$', 'start'), url(r'^manage/$', 'prof_manage'), diff --git a/testapp/exam/views.py b/testapp/exam/views.py index aeb93ca..44884fd 100644 --- a/testapp/exam/views.py +++ b/testapp/exam/views.py @@ -139,7 +139,7 @@ def quizlist_user(request): user = request.user avail_quizzes = list(QuestionPaper.objects.filter(quiz__active=True)) user_answerpapers = AnswerPaper.objects.filter(user=user) - quizzes_taken = [] + quizzes_taken = user_answerpapers pre_requisites = [] context = {} @@ -152,13 +152,6 @@ def quizlist_user(request): context['quizzes_taken'] = None return my_render_to_response("exam/quizzes_user.html", context) - for answer_paper in user_answerpapers: - for quiz in avail_quizzes: - if answer_paper.question_paper.id == quiz.id and \ - answer_paper.end_time != answer_paper.start_time: - avail_quizzes.remove(quiz) - quizzes_taken.append(answer_paper) - context['quizzes'] = avail_quizzes context['user'] = user context['quizzes_taken'] = quizzes_taken @@ -170,27 +163,45 @@ def intro(request, questionpaper_id): user = request.user ci = RequestContext(request) quest_paper = QuestionPaper.objects.get(id=questionpaper_id) + attempt_number = quest_paper.quiz.attempts_allowed + time_lag = quest_paper.quiz.time_between_attempts + if quest_paper.quiz.prerequisite: try: - pre_quest = QuestionPaper.objects.get(quiz=quest_paper.quiz.prerequisite) - answer_paper = AnswerPaper.objects.get( - question_paper=pre_quest, - user=user) - if answer_paper.passed: - context = {'user': user, 'paper_id': questionpaper_id} - return my_render_to_response('exam/intro.html', context, - context_instance=ci) - else: + pre_quest = QuestionPaper.objects.get( + quiz=quest_paper.quiz.prerequisite) + answer_papers = AnswerPaper.objects.filter( + question_paper=pre_quest, user=user) + answer_papers_failed = AnswerPaper.objects.filter( + question_paper=pre_quest, user=user, passed=False) + if answer_papers.count() == answer_papers_failed.count(): context = {'user': user, 'cannot_attempt': True} return my_redirect("/exam/quizzes/?cannot_attempt=True") - except: context = {'user': user, 'cannot_attempt': True} return my_redirect("/exam/quizzes/?cannot_attempt=True") - context = {'user': user, 'paper_id': questionpaper_id} - ci = RequestContext(request) - return my_render_to_response('exam/intro.html', context, - context_instance=ci) + + attempted_papers = AnswerPaper.objects.filter(question_paper=quest_paper, + user=user) + already_attempted = attempted_papers.count() + if already_attempted == 0: + context = {'user': user, 'paper_id': questionpaper_id,\ + 'attempt_no': already_attempted + 1} + return my_render_to_response('exam/intro.html', context, + context_instance=ci) + if already_attempted < attempt_number: + previous_attempt_day = attempted_papers[already_attempted-1].start_time + today = datetime.datetime.today() + days_after_attempt = (today - previous_attempt_day).days + if days_after_attempt >= time_lag: + context = {'user': user, 'paper_id': questionpaper_id,\ + 'attempt_no': already_attempted + 1} + return my_render_to_response('exam/intro.html', context, + context_instance=ci) + else: + return my_redirect("/exam/quizzes/") + else: + return my_redirect("/exam/quizzes/") def results_user(request): @@ -367,6 +378,8 @@ def add_quiz(request, quiz_id=None): d.pass_criteria = form['pass_criteria'].data d.language = form['language'].data d.prerequisite_id = form['prerequisite'].data + d.attempts_allowed = form['attempts_allowed'].data + d.time_between_attempts = form['time_between_attempts'].data d.save() quiz = Quiz.objects.get(id=quiz_id) return my_redirect("/exam/manage/showquiz") @@ -390,6 +403,8 @@ def add_quiz(request, quiz_id=None): form.initial['pass_criteria'] = d.pass_criteria form.initial['language'] = d.language form.initial['prerequisite'] = d.prerequisite_id + form.initial['attempts_allowed'] = d.attempts_allowed + form.initial['time_between_attempts'] = d.time_between_attempts return my_render_to_response('exam/add_quiz.html', {'form': form}, context_instance=ci) @@ -641,7 +656,7 @@ def user_login(request): context_instance=ci) -def start(request, questionpaper_id=None): +def start(request, attempt_no=None, questionpaper_id=None): """Check the user cedentials and if any quiz is available, start the exam.""" user = request.user @@ -654,13 +669,13 @@ def start(request, questionpaper_id=None): except QuestionPaper.DoesNotExist: msg = 'Quiz not found, please contact your '\ 'instructor/administrator. Please login again thereafter.' - return complete(request, msg, questionpaper_id) + return complete(request, msg, attempt_no, questionpaper_id) try: old_paper = AnswerPaper.objects.get( - question_paper=questionpaper, user=user) + question_paper=questionpaper, user=user, attempt_number=attempt_no) q = old_paper.current_question() - return show_question(request, q, questionpaper_id) + return show_question(request, q, attempt_no, questionpaper_id) except AnswerPaper.DoesNotExist: ip = request.META['REMOTE_ADDR'] key = gen_key(10) @@ -670,13 +685,13 @@ def start(request, questionpaper_id=None): msg = 'You do not have a profile and cannot take the quiz!' raise Http404(msg) - new_paper = questionpaper.make_answerpaper(user, ip,) + new_paper = questionpaper.make_answerpaper(user, ip, attempt_no) # Make user directory. user_dir = get_user_dir(user) - return start(request, questionpaper_id) + return start(request, attempt_no, questionpaper_id) -def question(request, q_id, questionpaper_id, success_msg=None): +def question(request, q_id, attempt_no, questionpaper_id, success_msg=None): """Check the credentials of the user and start the exam.""" user = request.user @@ -686,7 +701,7 @@ def question(request, q_id, questionpaper_id, success_msg=None): try: q_paper = QuestionPaper.objects.get(id=questionpaper_id) paper = AnswerPaper.objects.get( - user=request.user, question_paper=q_paper) + user=request.user, attempt_number=attempt_no, question_paper=q_paper) except AnswerPaper.DoesNotExist: return my_redirect('/exam/start/') if not paper.question_paper.quiz.active: @@ -713,21 +728,22 @@ def question(request, q_id, questionpaper_id, success_msg=None): context_instance=ci) -def show_question(request, q_id, questionpaper_id, success_msg=None): +def show_question(request, q_id, attempt_no, questionpaper_id, success_msg=None): """Show a question if possible.""" if len(q_id) == 0: msg = 'Congratulations! You have successfully completed the quiz.' - return complete(request, msg, questionpaper_id) + return complete(request, msg, attempt_no, questionpaper_id) else: - return question(request, q_id, questionpaper_id, success_msg) + return question(request, q_id, attempt_no, questionpaper_id, success_msg) -def check(request, q_id, questionpaper_id=None): +def check(request, q_id, attempt_no=None, questionpaper_id=None): """Checks the answers of the user for particular question""" user = request.user q_paper = QuestionPaper.objects.get(id=questionpaper_id) - paper = AnswerPaper.objects.get(user=request.user, question_paper=q_paper) + paper = AnswerPaper.objects.get(user=request.user, attempt_number=attempt_no, + question_paper=q_paper) if not user.is_authenticated() or paper.end_time < datetime.datetime.now(): return my_redirect('/exam/login/') question = get_object_or_404(Question, pk=q_id) @@ -737,7 +753,7 @@ def check(request, q_id, questionpaper_id=None): success = True if skip is not None: next_q = paper.skip() - return show_question(request, next_q, questionpaper_id) + return show_question(request, next_q, attempt_no, questionpaper_id) # Add the answer submitted, regardless of it being correct or not. if question.type == 'mcq': @@ -770,10 +786,10 @@ def check(request, q_id, questionpaper_id=None): if not success: # Should only happen for non-mcq questions. if time_left == 0: reason = 'Your time is up!' - return complete(request, reason, questionpaper_id) + return complete(request, reason, attempt_no, questionpaper_id) if not paper.question_paper.quiz.active: reason = 'The quiz has been deactivated!' - return complete(request, reason, questionpaper_id) + return complete(request, reason, attempt_no, questionpaper_id) context = {'question': question, 'error_message': err_msg, 'paper': paper, 'last_attempt': user_code, 'quiz_name': paper.question_paper.quiz.description, @@ -785,10 +801,10 @@ def check(request, q_id, questionpaper_id=None): else: if time_left <= 0: reason = 'Your time is up!' - return complete(request, reason, questionpaper_id) + return complete(request, reason, attempt_no, questionpaper_id) else: next_q = paper.completed_question(question.id) - return show_question(request, next_q, + return show_question(request, next_q, attempt_no, questionpaper_id, success_msg) @@ -824,14 +840,15 @@ def validate_answer(user, user_answer, question): return correct, success, message -def quit(request, questionpaper_id=None): +def quit(request, attempt_no=None, questionpaper_id=None): """Show the quit page when the user logs out.""" - context = {'id': questionpaper_id} + context = {'id': questionpaper_id, + 'attempt_no': attempt_no} return my_render_to_response('exam/quit.html', context, context_instance=RequestContext(request)) -def complete(request, reason=None, questionpaper_id=None): +def complete(request, reason=None, attempt_no=None, questionpaper_id=None): """Show a page to inform user that the quiz has been compeleted.""" user = request.user @@ -842,11 +859,13 @@ def complete(request, reason=None, questionpaper_id=None): return my_render_to_response('exam/complete.html', context) else: q_paper = QuestionPaper.objects.get(id=questionpaper_id) - paper = AnswerPaper.objects.get(user=user, question_paper=q_paper) + paper = AnswerPaper.objects.get(user=user, question_paper=q_paper, + attempt_number=attempt_no) paper.update_marks_obtained() paper.update_percent() paper.update_passed() paper.end_time = datetime.datetime.now() + paper.update_status() paper.save() obt_marks = paper.marks_obtained tot_marks = paper.question_paper.total_marks -- cgit From 534cc561e22da308e31282ab9c598041558aa1b4 Mon Sep 17 00:00:00 2001 From: hardythe1 Date: Wed, 14 Jan 2015 13:43:41 +0530 Subject: Added model to store assignment files --- testapp/exam/models.py | 11 +++++++++++ testapp/exam/urls.py | 2 +- testapp/urls.py | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) (limited to 'testapp') diff --git a/testapp/exam/models.py b/testapp/exam/models.py index 259f875..2386cd2 100644 --- a/testapp/exam/models.py +++ b/testapp/exam/models.py @@ -41,6 +41,9 @@ test_status = ( ('completed', 'Completed'), ) +def get_assignment_dir(instance, filename): + return '%s/%s/' % (instance.user.roll_number, instance.assignment.description) + ############################################################################### class Question(models.Model): """Question for a quiz.""" @@ -381,3 +384,11 @@ class AnswerPaper(models.Model): def __unicode__(self): u = self.user return u'Question paper for {0} {1}'.format(u.first_name, u.last_name) + + +################################################################################ +class AssignmentUpload(models.Model): + user = models.ForeignKey(Profile) + assignment = models.ForeignKey(Quiz) + assignmentQuestion = models.ForeignKey(Question) + assignmentFile = models.FileField(upload_to=get_assignment_dir) diff --git a/testapp/exam/urls.py b/testapp/exam/urls.py index d71329c..6aa395c 100644 --- a/testapp/exam/urls.py +++ b/testapp/exam/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import patterns, include, url +from django.conf.urls.defaults import * urlpatterns = patterns('testapp.exam.views', url(r'^$', 'index'), diff --git a/testapp/urls.py b/testapp/urls.py index 0d126f4..6c19e81 100644 --- a/testapp/urls.py +++ b/testapp/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import patterns, include, url +from django.conf.urls.defaults import * # Uncomment the next two lines to enable the admin: from django.contrib import admin -- cgit From cbdeb90a756832c71c80bac4dbe9ba1a3aceacb2 Mon Sep 17 00:00:00 2001 From: hardythe1 Date: Fri, 16 Jan 2015 16:47:43 +0530 Subject: added view, urls for assignment upload & changed the model --- testapp/exam/models.py | 1 - testapp/exam/templates/exam/question.html | 11 +++++++++++ testapp/exam/urls.py | 1 + testapp/exam/views.py | 16 ++++++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) (limited to 'testapp') diff --git a/testapp/exam/models.py b/testapp/exam/models.py index 2386cd2..1cafa89 100644 --- a/testapp/exam/models.py +++ b/testapp/exam/models.py @@ -389,6 +389,5 @@ class AnswerPaper(models.Model): ################################################################################ class AssignmentUpload(models.Model): user = models.ForeignKey(Profile) - assignment = models.ForeignKey(Quiz) assignmentQuestion = models.ForeignKey(Question) assignmentFile = models.FileField(upload_to=get_assignment_dir) diff --git a/testapp/exam/templates/exam/question.html b/testapp/exam/templates/exam/question.html index 855a29e..f8888b1 100644 --- a/testapp/exam/templates/exam/question.html +++ b/testapp/exam/templates/exam/question.html @@ -95,6 +95,17 @@ function setSnippetHeight() {% endif %}

+ + {% if question.type == "bash assignment" %} +
+

Upload assignment file for the said question

+ +


+
   + +
+ {% endif %} +
{% csrf_token %} {% if question.type == "mcq" %} diff --git a/testapp/exam/urls.py b/testapp/exam/urls.py index 6aa395c..e32b7e1 100644 --- a/testapp/exam/urls.py +++ b/testapp/exam/urls.py @@ -11,6 +11,7 @@ urlpatterns = patterns('testapp.exam.views', url(r'^intro/(?P\d+)/$','intro'), url(r'^complete/$', 'complete'), url(r'^complete/(?P\d+)/(?P\d+)/$', 'complete'), + url(r'^submit-assignment(?P\d+)/$', 'submit_assignment'), url(r'^register/$', 'user_register'), url(r'^(?P\d+)/$', 'question'), url(r'^(?P\d+)/check/$', 'check'), diff --git a/testapp/exam/views.py b/testapp/exam/views.py index 8fde7a7..9902b74 100644 --- a/testapp/exam/views.py +++ b/testapp/exam/views.py @@ -1189,3 +1189,19 @@ def design_questionpaper(request): context = {'form': form} return my_render_to_response('exam/design_questionpaper.html', context, context_instance=ci) + + +def submit_assignment(request, question_id=None): + user = request.user + skip = request.POST.get('skip', None) + if request.method == "POST" and skip is not None: + question = Question.objects.get(id=question_id) + assignment = AssignmentUpload() + assignment.user = user + assignment.assignmentQuestion = question + assignment.assignmentFile = request.FILES['assignment'] + assignment.save() + #next question ke liye code idhar + else: + #code for skipping the question + -- cgit From f2e09edeb2e5b884f0e75ad3747b51e7603d70e1 Mon Sep 17 00:00:00 2001 From: prathamesh Date: Tue, 20 Jan 2015 17:05:41 +0530 Subject: Assignment upload interface implementation --- testapp/exam/forms.py | 1 + testapp/exam/models.py | 3 ++- testapp/exam/templates/exam/question.html | 19 ++++++++----------- testapp/exam/urls.py | 2 +- testapp/exam/views.py | 30 +++++++++++++++++++----------- testapp/settings.py | 4 ++-- testapp/urls.py | 2 +- 7 files changed, 34 insertions(+), 27 deletions(-) (limited to 'testapp') diff --git a/testapp/exam/forms.py b/testapp/exam/forms.py index 169632d..843ed15 100644 --- a/testapp/exam/forms.py +++ b/testapp/exam/forms.py @@ -27,6 +27,7 @@ question_types = ( ("mcq", "Multiple Choice"), ("mcc", "Multiple Correct Choices"), ("code", "Code"), + ("basgn", "Bash Assignment"), ) UNAME_CHARS = letters + "._" + digits diff --git a/testapp/exam/models.py b/testapp/exam/models.py index 1cafa89..88ba9ef 100644 --- a/testapp/exam/models.py +++ b/testapp/exam/models.py @@ -30,6 +30,7 @@ question_types = ( ("mcq", "Multiple Choice"), ("mcc", "Multiple Correct Choices"), ("code", "Code"), + ("basgn", "Bash Assignment"), ) attempts = [(i, i) for i in range(1, 6)] attempts.append((-1, 'Infinite')) @@ -42,7 +43,7 @@ test_status = ( ) def get_assignment_dir(instance, filename): - return '%s/%s/' % (instance.user.roll_number, instance.assignment.description) + return '%s/%s' % (instance.user.roll_number, instance.assignmentQuestion.id) ############################################################################### class Question(models.Model): diff --git a/testapp/exam/templates/exam/question.html b/testapp/exam/templates/exam/question.html index f8888b1..03284ce 100644 --- a/testapp/exam/templates/exam/question.html +++ b/testapp/exam/templates/exam/question.html @@ -96,23 +96,18 @@ function setSnippetHeight()

- {% if question.type == "bash assignment" %} - -

Upload assignment file for the said question

- -


-
   - - - {% endif %} - -
+ {% csrf_token %} {% if question.type == "mcq" %} {% for option in question.options.strip.splitlines %} {{option}}
{% endfor %} {% endif %} + {% if question.type == "basgn" %} +

Upload assignment file for the said question

+ +


+ {% endif %} {% if question.type == "mcc" %} {% for option in question.options.strip.splitlines %} {{ option }} @@ -134,6 +129,8 @@ function setSnippetHeight() {% if question.type == "mcq" or question.type == "mcc "%}
   + {% elif question.type == "basgn" %} +
   {% else %}    {% endif %} diff --git a/testapp/exam/urls.py b/testapp/exam/urls.py index e32b7e1..f7a7c54 100644 --- a/testapp/exam/urls.py +++ b/testapp/exam/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.defaults import * +from django.conf.urls import * urlpatterns = patterns('testapp.exam.views', url(r'^$', 'index'), diff --git a/testapp/exam/views.py b/testapp/exam/views.py index 9902b74..ce1c62e 100644 --- a/testapp/exam/views.py +++ b/testapp/exam/views.py @@ -21,7 +21,7 @@ from testapp.exam.forms import UserRegisterForm, UserLoginForm, QuizForm,\ QuestionForm, RandomQuestionForm from testapp.exam.xmlrpc_clients import code_server from settings import URL_ROOT - +from testapp.exam.models import AssignmentUpload # The directory where user data can be saved. OUTPUT_DIR = abspath(join(dirname(__file__), 'output')) @@ -760,6 +760,13 @@ def check(request, q_id, attempt_no=None, questionpaper_id=None): user_answer = request.POST.get('answer') elif question.type == 'mcc': user_answer = request.POST.getlist('answer') + elif question.type == 'basgn': + assign = AssignmentUpload() + assign.user = user.profile + assign.assignmentQuestion = question + assign.assignmentFile = request.FILES['assignment'] + assign.save() + user_answer = 'ASSIGNMENT UPLOADED' else: user_code = request.POST.get('answer') user_answer = snippet_code + "\n" + user_code @@ -772,15 +779,16 @@ def check(request, q_id, attempt_no=None, questionpaper_id=None): # If we were not skipped, we were asked to check. For any non-mcq # questions, we obtain the results via XML-RPC with the code executed # safely in a separate process (the code_server.py) running as nobody. - correct, success, err_msg = validate_answer(user, user_answer, question) - if correct: - new_answer.correct = correct - new_answer.marks = question.points - new_answer.error = err_msg - success_msg = True - else: - new_answer.error = err_msg - new_answer.save() + if not question.type == 'basgn': + correct, success, err_msg = validate_answer(user, user_answer, question) + if correct: + new_answer.correct = correct + new_answer.marks = question.points + new_answer.error = err_msg + success_msg = True + else: + new_answer.error = err_msg + new_answer.save() time_left = paper.time_left() if not success: # Should only happen for non-mcq questions. @@ -1204,4 +1212,4 @@ def submit_assignment(request, question_id=None): #next question ke liye code idhar else: #code for skipping the question - + pass diff --git a/testapp/settings.py b/testapp/settings.py index 88d4a5a..00a6b17 100644 --- a/testapp/settings.py +++ b/testapp/settings.py @@ -72,12 +72,12 @@ USE_L10N = True # Absolute filesystem path to the directory that will hold user-uploaded files. # Example: "/home/media/media.lawrence.com/media/" -MEDIA_ROOT = '' +MEDIA_ROOT = join(CURDIR, '../uploads') # URL that handles the media served from MEDIA_ROOT. Make sure to use a # trailing slash. # Examples: "http://media.lawrence.com/media/", "http://example.com/media/" -MEDIA_URL = '' +MEDIA_URL = 'upload/' # Absolute path to the directory static files should be collected to. # Don't put anything in this directory yourself; store your static files diff --git a/testapp/urls.py b/testapp/urls.py index 6c19e81..dee297c 100644 --- a/testapp/urls.py +++ b/testapp/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.defaults import * +from django.conf.urls import * # Uncomment the next two lines to enable the admin: from django.contrib import admin -- cgit From 09aed6db0be635d86c2bf7f9c9a989e6df1bceab Mon Sep 17 00:00:00 2001 From: prathamesh Date: Tue, 20 Jan 2015 17:30:48 +0530 Subject: View updated to take infinite quizzes. --- testapp/exam/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'testapp') diff --git a/testapp/exam/views.py b/testapp/exam/views.py index ce1c62e..f94d383 100644 --- a/testapp/exam/views.py +++ b/testapp/exam/views.py @@ -189,7 +189,7 @@ def intro(request, questionpaper_id): 'attempt_no': already_attempted + 1} return my_render_to_response('exam/intro.html', context, context_instance=ci) - if already_attempted < attempt_number: + if already_attempted < attempt_number or attempt_number < 0: previous_attempt_day = attempted_papers[already_attempted-1].start_time today = datetime.datetime.today() days_after_attempt = (today - previous_attempt_day).days -- cgit From caf153bcaa8b9a0096a4653b562198f2ba2cef9a Mon Sep 17 00:00:00 2001 From: prathamesh Date: Tue, 24 Feb 2015 09:29:35 +0530 Subject: Question navigator implemented Students can now move from one question to another through the navigator window. Also, the questions attempted will have different color then the ones which are not attempted. --- testapp/exam/static/exam/css/base.css | 70 ++++++++++++++++++++++++++++++- testapp/exam/templates/exam/question.html | 24 ++++++++++- testapp/exam/urls.py | 1 + testapp/exam/views.py | 63 +++++++++++++++++++++++----- 4 files changed, 145 insertions(+), 13 deletions(-) (limited to 'testapp') diff --git a/testapp/exam/static/exam/css/base.css b/testapp/exam/static/exam/css/base.css index 051ba22..3570098 100644 --- a/testapp/exam/static/exam/css/base.css +++ b/testapp/exam/static/exam/css/base.css @@ -245,7 +245,30 @@ body { .container:after { clear: both; } - +.container-fluid { + position: relative; + min-width: 940px; + padding-left: 20px; + padding-right: 20px; + zoom: 1; +} +.container-fluid:before, .container-fluid:after { + display: table; + content: ""; + zoom: 1; +} +.container-fluid:after { + clear: both; +} +.container-fluid > .sidebar { + position: absolute; + top: 0; + left: 20px; + width: 220px; +} +.container-fluid > .content { + margin-left: 240px; +} a { color: #0069d6; text-decoration: none; @@ -2257,3 +2280,48 @@ button.btn::-moz-focus-inner, input[type=submit].btn::-moz-focus-inner { .tab-content > .active, .pill-content > .active { display: block; } +.pagination { + height: 36px; + margin: 18px 0; +} +.pagination ul { + float: left; + margin: 0; + border: 1px solid #ddd; + border: 1px solid rgba(0, 0, 0, 0.15); + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} +.pagination li { + display: inline; +} +.pagination a { + float: left; + padding: 0 14px; + line-height: 34px; + border-right: 1px solid; + border-right-color: #ddd; + border-right-color: rgba(0, 0, 0, 0.15); + *border-right-color: #ddd; + /* IE6-7 */ + + text-decoration: none; +} +.pagination a:hover, .pagination .active a { + background-color: #c7eefe; +} +/*custom classes*/ +.pagination .done a { + background-color: #00CC66; +} +.pagination .disabled a, .pagination .disabled a:hover { + background-color: transparent; + color: #bfbfbf; +} +.pagination .next a { + border: 0; +} diff --git a/testapp/exam/templates/exam/question.html b/testapp/exam/templates/exam/question.html index 03284ce..96e7bd6 100644 --- a/testapp/exam/templates/exam/question.html +++ b/testapp/exam/templates/exam/question.html @@ -81,10 +81,30 @@ function setSnippetHeight()
+ +
+
- -

{{ question.summary }} (Marks : {{ question.points }})


+

{{ question.summary }} (Marks : {{ question.points }})


{{ question.description|safe }} {% if error_message %}
diff --git a/testapp/exam/urls.py b/testapp/exam/urls.py index f7a7c54..62fa9e1 100644 --- a/testapp/exam/urls.py +++ b/testapp/exam/urls.py @@ -17,6 +17,7 @@ urlpatterns = patterns('testapp.exam.views', url(r'^(?P\d+)/check/$', 'check'), url(r'^(?P\d+)/check/(?P\d+)/(?P\d+)/$', 'check'), url(r'^intro/$', 'start'), + url(r'^(?P\d+)/(?P\d+)/(?P\d+)/$', 'show_question'), url(r'^manage/$', 'prof_manage'), url(r'^manage/addquestion/$', 'add_question'), diff --git a/testapp/exam/views.py b/testapp/exam/views.py index f94d383..94a5ffb 100644 --- a/testapp/exam/views.py +++ b/testapp/exam/views.py @@ -4,7 +4,7 @@ import os import stat from os.path import dirname, pardir, abspath, join, exists import datetime - +import collections from django.http import HttpResponse from django.contrib.auth import login, logout, authenticate from django.shortcuts import render_to_response, get_object_or_404, redirect @@ -714,15 +714,36 @@ def question(request, q_id, attempt_no, questionpaper_id, success_msg=None): if time_left == 0: return complete(request, reason='Your time is up!') quiz_name = paper.question_paper.quiz.description + to_attempt = [] + submitted = [] + if paper.questions: + to_attempt = (paper.questions).split('|') + if paper.questions_answered: + submitted = (paper.questions_answered).split('|') + all_questions = [] + if not to_attempt: + submitted.sort() + all_questions = submitted + if not submitted: + to_attempt.sort() + all_questions = to_attempt + if to_attempt and submitted: + q_append = to_attempt + submitted + q_append.sort() + all_questions = q_append + questions = {} + for num, value in enumerate(all_questions, 1): + print num, value + questions[value] = num + questions = collections.OrderedDict(sorted(questions.items())) if success_msg is None: - context = {'question': q, 'paper': paper, 'user': user, - 'quiz_name': quiz_name, - 'time_left': time_left, } + context = {'question': q, 'questions' : questions, 'paper': paper, + 'user': user, 'quiz_name': quiz_name, 'time_left': time_left, + 'to_attempt' : to_attempt, 'submitted': submitted} else: - context = {'question': q, 'paper': paper, 'user': user, - 'quiz_name': quiz_name, - 'time_left': time_left, - 'success_msg': success_msg} + context = {'question': q, 'questions' : questions, 'paper': paper, + 'user': user, 'quiz_name': quiz_name, 'time_left': time_left, + 'success_msg': success_msg, 'to_attempt' : to_attempt, 'submitted' : submitted} ci = RequestContext(request) return my_render_to_response('exam/question.html', context, context_instance=ci) @@ -798,10 +819,32 @@ def check(request, q_id, attempt_no=None, questionpaper_id=None): if not paper.question_paper.quiz.active: reason = 'The quiz has been deactivated!' return complete(request, reason, attempt_no, questionpaper_id) - context = {'question': question, 'error_message': err_msg, + to_attempt = [] + submitted = [] + if paper.questions: + to_attempt = (paper.questions).split('|') + if paper.questions_answered: + submitted = (paper.questions_answered).split('|') + all_questions = [] + if not to_attempt: + submitted.sort() + all_questions = submitted + if not submitted: + to_attempt.sort() + all_questions = to_attempt + if to_attempt and submitted: + q_append = to_attempt + submitted + q_append.sort() + all_questions = q_append + questions = {} + for num, value in enumerate(all_questions, 1): + questions[value] = num + questions = collections.OrderedDict(sorted(questions.items())) + context = {'question': question, 'questions': questions, + 'error_message': err_msg, 'paper': paper, 'last_attempt': user_code, 'quiz_name': paper.question_paper.quiz.description, - 'time_left': time_left} + 'time_left': time_left, 'to_attempt' : to_attempt, 'submitted': submitted} ci = RequestContext(request) return my_render_to_response('exam/question.html', context, -- cgit From 671df0da02345710ebd4b18d45f2a573de350cc3 Mon Sep 17 00:00:00 2001 From: prathamesh Date: Tue, 24 Feb 2015 11:19:06 +0530 Subject: Removed print statement. --- testapp/exam/views.py | 1 - 1 file changed, 1 deletion(-) (limited to 'testapp') diff --git a/testapp/exam/views.py b/testapp/exam/views.py index 94a5ffb..8d7dd18 100644 --- a/testapp/exam/views.py +++ b/testapp/exam/views.py @@ -733,7 +733,6 @@ def question(request, q_id, attempt_no, questionpaper_id, success_msg=None): all_questions = q_append questions = {} for num, value in enumerate(all_questions, 1): - print num, value questions[value] = num questions = collections.OrderedDict(sorted(questions.items())) if success_msg is None: -- cgit From b0551e3cb8dc59fb83ea622375c9e53003fcb489 Mon Sep 17 00:00:00 2001 From: prathamesh Date: Tue, 31 Mar 2015 16:14:19 +0530 Subject: Function for creating an ordered dictionary of questions --- testapp/exam/views.py | 72 +++++++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 42 deletions(-) (limited to 'testapp') diff --git a/testapp/exam/views.py b/testapp/exam/views.py index 8d7dd18..2c97334 100644 --- a/testapp/exam/views.py +++ b/testapp/exam/views.py @@ -690,6 +690,34 @@ def start(request, attempt_no=None, questionpaper_id=None): user_dir = get_user_dir(user) return start(request, attempt_no, questionpaper_id) +def get_questions(paper): + ''' + Takes answerpaper as an argument. Returns the total questions as + ordered dictionary, the questions yet to attempt and the questions + attempted + ''' + to_attempt = [] + submitted = [] + all_questions = [] + questions = {} + if paper.questions: + to_attempt = (paper.questions).split('|') + if paper.questions_answered: + submitted = (paper.questions_answered).split('|') + if not to_attempt: + submitted.sort() + all_questions = submitted + if not submitted: + to_attempt.sort() + all_questions = to_attempt + if to_attempt and submitted: + q_append = to_attempt + submitted + q_append.sort() + all_questions = q_append + for num, value in enumerate(all_questions, 1): + questions[value] = num + questions = collections.OrderedDict(sorted(questions.items())) + return questions, to_attempt, submitted def question(request, q_id, attempt_no, questionpaper_id, success_msg=None): """Check the credentials of the user and start the exam.""" @@ -714,27 +742,7 @@ def question(request, q_id, attempt_no, questionpaper_id, success_msg=None): if time_left == 0: return complete(request, reason='Your time is up!') quiz_name = paper.question_paper.quiz.description - to_attempt = [] - submitted = [] - if paper.questions: - to_attempt = (paper.questions).split('|') - if paper.questions_answered: - submitted = (paper.questions_answered).split('|') - all_questions = [] - if not to_attempt: - submitted.sort() - all_questions = submitted - if not submitted: - to_attempt.sort() - all_questions = to_attempt - if to_attempt and submitted: - q_append = to_attempt + submitted - q_append.sort() - all_questions = q_append - questions = {} - for num, value in enumerate(all_questions, 1): - questions[value] = num - questions = collections.OrderedDict(sorted(questions.items())) + questions, to_attempt, submitted = get_questions(paper) if success_msg is None: context = {'question': q, 'questions' : questions, 'paper': paper, 'user': user, 'quiz_name': quiz_name, 'time_left': time_left, @@ -818,27 +826,7 @@ def check(request, q_id, attempt_no=None, questionpaper_id=None): if not paper.question_paper.quiz.active: reason = 'The quiz has been deactivated!' return complete(request, reason, attempt_no, questionpaper_id) - to_attempt = [] - submitted = [] - if paper.questions: - to_attempt = (paper.questions).split('|') - if paper.questions_answered: - submitted = (paper.questions_answered).split('|') - all_questions = [] - if not to_attempt: - submitted.sort() - all_questions = submitted - if not submitted: - to_attempt.sort() - all_questions = to_attempt - if to_attempt and submitted: - q_append = to_attempt + submitted - q_append.sort() - all_questions = q_append - questions = {} - for num, value in enumerate(all_questions, 1): - questions[value] = num - questions = collections.OrderedDict(sorted(questions.items())) + questions, to_attempt, submitted = get_questions(paper) context = {'question': question, 'questions': questions, 'error_message': err_msg, 'paper': paper, 'last_attempt': user_code, -- cgit