diff options
-rw-r--r-- | yaksh/live_server_tests/selenium_test.py | 4 | ||||
-rw-r--r-- | yaksh/models.py | 17 | ||||
-rw-r--r-- | yaksh/templates/exam.html | 4 | ||||
-rw-r--r-- | yaksh/templates/user.html | 2 | ||||
-rw-r--r-- | yaksh/templates/yaksh/course_detail.html | 6 | ||||
-rw-r--r-- | yaksh/templates/yaksh/question.html | 37 | ||||
-rw-r--r-- | yaksh/templates/yaksh/quizzes_user.html | 2 | ||||
-rw-r--r-- | yaksh/templates/yaksh/user_data.html | 4 | ||||
-rw-r--r-- | yaksh/test_models.py | 10 | ||||
-rw-r--r-- | yaksh/views.py | 27 |
10 files changed, 70 insertions, 43 deletions
diff --git a/yaksh/live_server_tests/selenium_test.py b/yaksh/live_server_tests/selenium_test.py index d91f3ec..277f08e 100644 --- a/yaksh/live_server_tests/selenium_test.py +++ b/yaksh/live_server_tests/selenium_test.py @@ -29,8 +29,8 @@ class SeleniumTest(): except Exception as e: self.driver.close() msg = ("An Error occurred while running the Selenium load" - " test on Yaksh!" - "Error:\n ".format(e)) + " test on Yaksh!\n" + "Error:\n{0}".format(e)) raise SeleniumTestError(msg) diff --git a/yaksh/models.py b/yaksh/models.py index 2d84622..398f508 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -982,15 +982,17 @@ class AnswerPaper(models.Model): """Returns the number of questions left.""" return self.questions_unanswered.count() - def completed_question(self, question_id): + def add_completed_question(self, question_id): """ Adds the completed question to the list of answered questions and returns the next question. """ - next_question = self.next_question(question_id) - self.questions_answered.add(question_id) + if question_id not in self.questions_answered.all(): + self.questions_answered.add(question_id) self.questions_unanswered.remove(question_id) - if next_question.id == int(question_id): + + next_question = self.next_question(question_id) + if next_question and next_question.id == int(question_id): return None return next_question @@ -999,16 +1001,19 @@ class AnswerPaper(models.Model): Skips the current question and returns the next sequentially available question. """ + all_questions = self.questions.all() unanswered_questions = self.questions_unanswered.all() - questions = list(unanswered_questions.values_list('id', flat=True)) + questions = list(all_questions.values_list('id', flat=True)) if len(questions) == 0: return None + if unanswered_questions.count() == 0: + return None try: index = questions.index(int(question_id)) next_id = questions[index+1] except (ValueError, IndexError): next_id = questions[0] - return unanswered_questions.get(id=next_id) + return all_questions.get(id=next_id) def time_left(self): """Return the time remaining for the user in seconds.""" diff --git a/yaksh/templates/exam.html b/yaksh/templates/exam.html index b497cc0..02ff70a 100644 --- a/yaksh/templates/exam.html +++ b/yaksh/templates/exam.html @@ -52,7 +52,9 @@ {% endif %} {% endif %} {% if qid in paper.get_questions_answered %} - <li class="disabled"><a style="background-color:#B4B8BA; width:25%" href="#" data-toggle="tooltip" title="{{ qid.description }}" >{{ forloop.counter }}</a></li> + <li><a style="background-color:#B4B8BA; width:25%" href="#" data-toggle="tooltip" + onclick="call_skip('{{ URL_ROOT }}/exam/{{ question.id }}/skip/{{ qid.id }}/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/')" + title="{{ qid.description }}">{{ forloop.counter }}</a></li> {% endif %} {% else %} {% if qid.id == question.id %} diff --git a/yaksh/templates/user.html b/yaksh/templates/user.html index 0d1f980..b068fae 100644 --- a/yaksh/templates/user.html +++ b/yaksh/templates/user.html @@ -16,7 +16,7 @@ <div class= "collapse navbar-collapse" id="navbar"> <ul class="nav navbar-nav navbar-right"> <li><a href="{{ URL_ROOT }}/exam/viewprofile"> {{ user.get_full_name.title }} </a></li> - <li><a style='cursor:pointer' id='logout' onClick='location.replace("{{URL_ROOT}}/exam/complete/");'> <span class="glyphicon glyphicon-log-out">Logout </span></a></li> + <li><a style='cursor:pointer' id='logout' onClick='location.replace("{{URL_ROOT}}/exam/logout/");'> <span class="glyphicon glyphicon-log-out">Logout </span></a></li> </ul> </div><!-- /.navbar --> </div><!-- /.container --> diff --git a/yaksh/templates/yaksh/course_detail.html b/yaksh/templates/yaksh/course_detail.html index dc85080..4b7efaf 100644 --- a/yaksh/templates/yaksh/course_detail.html +++ b/yaksh/templates/yaksh/course_detail.html @@ -32,6 +32,7 @@ <div id="enroll-all"> <table class="table table-striped"> <th></th> + <th></th> <th>Full Name</th> <th>Email</th> <th>Roll Number</th> @@ -43,6 +44,7 @@ {% for request in course.get_requests %} <tr> <td><input type="checkbox" name="check" value="{{ request.id }}"></td> + <td>{{ forloop.counter }}.</td> <td>{{request.get_full_name}}</td> <td> {{request.email}}</td> <td> {{request.profile.roll_number}}</td> @@ -72,6 +74,7 @@ <div id="reject"> <table class="table table-striped"> <th></th> + <th></th> <th>Full Name</th> <th>Email</th> <th>Roll Number</th> @@ -83,6 +86,7 @@ {% csrf_token %} <tr> <td><input type="checkbox" name="check" value="{{ enrolled.id }}"></td> + <td>{{ forloop.counter }}.</td> <td> {{ enrolled.get_full_name|title }} </td> <td> {{enrolled.email}}</td> <td> {{enrolled.profile.roll_number}}</td> @@ -108,6 +112,7 @@ <div id="enroll"> <table class="table table-striped"> <th></th> + <th></th> <th>Full Name</th> <th>Email</th> <th>Roll Number</th> @@ -119,6 +124,7 @@ {% csrf_token %} <tr> <td><input type="checkbox" name="check" value="{{ rejected.id }}"></td> + <td>{{ forloop.counter }}.</td> <td>{{rejected.get_full_name|title}}</td> <td> {{rejected.email}}</td> <td> {{rejected.profile.roll_number}}</td> diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html index 1f6e67d..9dd0de5 100644 --- a/yaksh/templates/yaksh/question.html +++ b/yaksh/templates/yaksh/question.html @@ -135,13 +135,25 @@ function call_skip(url) {% block onload %} onload="updateTime();" {% endblock %} {% block main %} - <p id="status"></p> - <form id="code" action="{{URL_ROOT}}/exam/{{ question.id }}/check/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/" method="post" enctype="multipart/form-data"> - {% csrf_token %} - <input type=hidden name="question_id" id="question_id" value={{ question.id }}></input> - <div class="panel panel-default"> - <div class="panel-heading"> - <h4><u> {{ question.summary }} + <p id="status"></p> + {% if notification %} + {% if question.type == "code" %} + <div class="alert alert-success" role="alert"> + <strong>Note:</strong> {{ notification }} + </div> + {% else %} + <div class="alert alert-warning" role="alert"> + <strong>Note:</strong> {{ notification }} + </div> + {% endif %} + {% endif %} + <form id="code" action="{{URL_ROOT}}/exam/{{ question.id }}/check/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/" method="post" enctype="multipart/form-data"> + {% csrf_token %} + <input type=hidden name="question_id" id="question_id" value={{ question.id }}></input> + <div class="panel panel-default"> + <div class="panel-heading"> + <h4> + <u> {{ question.summary }} {% if question.type == "mcq" %} (Single Correct Choice Questions) {% elif question.type == "mcc" %} @@ -151,8 +163,8 @@ function call_skip(url) {% elif question.type == "upload" %} (ASSIGNMENT UPLOAD) {% endif %} - </u> - <font class=pull-right>(Marks : {{ question.points }}) </font> + </u> + <font class=pull-right>(Marks : {{ question.points }}) </font> </h4> <font size=3 face=arial> {{ question.description|safe }} </font> {% if files %} @@ -198,10 +210,15 @@ function call_skip(url) {% elif question.type == "upload" %} <br><button class="btn btn-primary" type="submit" name="check" id="check" onClick="return validate();">Upload</button> {% else %} - <button class="btn btn-primary" type="submit" name="check" id="check" onClick="submitCode();">Check Answer <span class="glyphicon glyphicon-cog"></span></button> + {% if question in paper.get_questions_unanswered %} + <button class="btn btn-primary" type="submit" name="check" id="check" onClick="submitCode();">Check Answer <span class="glyphicon glyphicon-cog"></span></button> + {% endif %} {% endif %} + {% if paper.question_paper.quiz.allow_skip and not paper.get_questions_unanswered|length_is:"1" %} + {% if question in paper.get_questions_unanswered %} <button class="btn btn-primary" onclick="call_skip('{{ URL_ROOT }}/exam/{{ question.id }}/skip/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/')" name="skip" id="skip">Attempt Later <span class="glyphicon glyphicon-arrow-right"></span></button> + {% endif %} </div> </div> </div> diff --git a/yaksh/templates/yaksh/quizzes_user.html b/yaksh/templates/yaksh/quizzes_user.html index 63f22a1..72fce30 100644 --- a/yaksh/templates/yaksh/quizzes_user.html +++ b/yaksh/templates/yaksh/quizzes_user.html @@ -6,7 +6,7 @@ <div class="col-md-12"> <div class="row"> <div class="col-md-4"> - <h4><b><u> {{ course.name }} by {{ course.creator }}</u></b></h4> + <h4><b><u> {{ course.name }} by {{ course.creator.get_full_name }}</u></b></h4> </div> <div class="col-md-4"> {% if user in course.requests.all %} <span class="label label-warning">Request Pending </span> diff --git a/yaksh/templates/yaksh/user_data.html b/yaksh/templates/yaksh/user_data.html index 9be28fb..16707b2 100644 --- a/yaksh/templates/yaksh/user_data.html +++ b/yaksh/templates/yaksh/user_data.html @@ -100,7 +100,7 @@ User IP address: {{ paper.user_ip }} {% else %} <h5>Student answer: </h5> {% for answer in answers %} - {% if not answer.skipped %} + {% if answer.answer.correct %} <div class="panel panel-success"> <div class="panel-heading">Correct answer @@ -116,7 +116,7 @@ User IP address: {{ paper.user_ip }} </div> <div class="panel-body"><pre><code>{{ answer.answer.answer.strip }}</code></pre></div> </div> - {% endif %} + {% endfor %} {% endif %} <hr> diff --git a/yaksh/test_models.py b/yaksh/test_models.py index 91d8806..cd66aed 100644 --- a/yaksh/test_models.py +++ b/yaksh/test_models.py @@ -662,7 +662,7 @@ class AnswerPaperTestCases(unittest.TestCase): current_question = self.answerpaper.current_question() self.assertEqual(current_question.id, 1) # Test completed_question() method of Answer Paper - question = self.answerpaper.completed_question(1) + question = self.answerpaper.add_completed_question(1) self.assertEqual(self.answerpaper.questions_left(), 2) # Test next_question() method of Answer Paper @@ -694,7 +694,7 @@ class AnswerPaperTestCases(unittest.TestCase): # Then self.assertTrue(next_question_id is not None) - self.assertEqual(next_question_id.id, 2) + self.assertEqual(next_question_id.id, 1) # Given, last question in the list current_question_id = 3 @@ -704,7 +704,7 @@ class AnswerPaperTestCases(unittest.TestCase): # Then self.assertTrue(next_question_id is not None) - self.assertEqual(next_question_id.id, 2) + self.assertEqual(next_question_id.id, 1) # Test get_questions_answered() method # When @@ -724,14 +724,14 @@ class AnswerPaperTestCases(unittest.TestCase): # Test completed_question and next_question # When all questions are answered - current_question = self.answerpaper.completed_question(2) + current_question = self.answerpaper.add_completed_question(2) # Then self.assertEqual(self.answerpaper.questions_left(), 1) self.assertEqual(current_question.id, 3) # When - current_question = self.answerpaper.completed_question(3) + current_question = self.answerpaper.add_completed_question(3) # Then self.assertEqual(self.answerpaper.questions_left(), 0) diff --git a/yaksh/views.py b/yaksh/views.py index 4c53d69..63653e6 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -400,7 +400,7 @@ def start(request, questionpaper_id=None, attempt_num=None): @login_required -def show_question(request, question, paper, error_message=None): +def show_question(request, question, paper, error_message=None, notification=None): """Show a question if possible.""" user = request.user if not question: @@ -412,10 +412,14 @@ def show_question(request, question, paper, error_message=None): if paper.time_left() <= 0: reason='Your time is up!' return complete(request, reason, paper.attempt_number, paper.question_paper.id) + if question in paper.questions_answered.all(): + notification = 'You have already attempted this question successfully' \ + if question.type == "code" else \ + 'You have already attempted this question' test_cases = question.get_test_cases() files = FileUpload.objects.filter(question_id=question.id, hide=False) context = {'question': question, 'paper': paper, 'error_message': error_message, - 'test_cases': test_cases, 'files': files, + 'test_cases': test_cases, 'files': files, 'notification': notification, 'last_attempt': question.snippet.encode('unicode-escape')} answers = paper.get_previous_answers(question) if answers: @@ -432,9 +436,6 @@ def skip(request, q_id, next_q=None, attempt_num=None, questionpaper_id=None): paper = get_object_or_404(AnswerPaper, user=request.user, attempt_number=attempt_num, question_paper=questionpaper_id) question = get_object_or_404(Question, pk=q_id) - if question in paper.questions_answered.all(): - next_q = paper.next_question(q_id) - return show_question(request, next_q, paper) if request.method == 'POST' and question.type == 'code': user_code = request.POST.get('answer') @@ -445,8 +446,6 @@ def skip(request, q_id, next_q=None, attempt_num=None, questionpaper_id=None): paper.answers.add(new_answer) if next_q is not None: next_q = get_object_or_404(Question, pk=next_q) - if next_q not in paper.questions_unanswered.all(): - return show_question(request, question, paper) else: next_q = paper.next_question(q_id) return show_question(request, next_q, paper) @@ -459,9 +458,6 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): paper = get_object_or_404(AnswerPaper, user=request.user, attempt_number=attempt_num, question_paper=questionpaper_id) current_question = get_object_or_404(Question, pk=q_id) - if current_question in paper.questions_answered.all(): - next_q = paper.next_question(q_id) - return show_question(request, next_q, paper) if request.method == 'POST': snippet_code = request.POST.get('snippet') @@ -480,14 +476,14 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): assign.assignmentFile = request.FILES['assignment'] assign.save() user_answer = 'ASSIGNMENT UPLOADED' - next_q = paper.completed_question(current_question.id) + next_q = paper.add_completed_question(current_question.id) return show_question(request, next_q, paper) else: user_code = request.POST.get('answer') user_answer = snippet_code + "\n" + user_code if snippet_code else user_code if not user_answer: msg = ["Please submit a valid option or code"] - return show_question(request, current_question, paper, msg) + return show_question(request, current_question, paper, notification=msg) new_answer = Answer(question=current_question, answer=user_answer, correct=False, error=json.dumps([])) new_answer.save() @@ -505,15 +501,16 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): new_answer.correct = result.get('success') error_message = None new_answer.error = json.dumps(result.get('error')) - next_question = paper.completed_question(current_question.id) + next_question = paper.add_completed_question(current_question.id) else: new_answer.marks = (current_question.points * result['weight'] / current_question.get_maximum_test_case_weight()) \ if current_question.partial_grading and current_question.type == 'code' else 0 - error_message = result.get('error') + error_message = result.get('error') if current_question.type == 'code' \ + else None new_answer.error = json.dumps(result.get('error')) next_question = current_question if current_question.type == 'code' \ - else paper.completed_question(current_question.id) + else paper.add_completed_question(current_question.id) new_answer.save() paper.update_marks('inprogress') paper.set_end_time(timezone.now()) |