diff options
-rwxr-xr-x[-rw-r--r--] | yaksh/code_server.py | 2 | ||||
-rw-r--r-- | yaksh/models.py | 11 | ||||
-rw-r--r-- | yaksh/static/yaksh/js/question.js | 12 | ||||
-rw-r--r-- | yaksh/static/yaksh/js/requesthandler.js | 90 | ||||
-rw-r--r-- | yaksh/templates/yaksh/question.html | 51 | ||||
-rw-r--r-- | yaksh/urls.py | 1 | ||||
-rw-r--r-- | yaksh/views.py | 79 |
7 files changed, 155 insertions, 91 deletions
diff --git a/yaksh/code_server.py b/yaksh/code_server.py index 7bacc7f..d74d35b 100644..100755 --- a/yaksh/code_server.py +++ b/yaksh/code_server.py @@ -208,7 +208,7 @@ def get_result(url, uid, block=False): ''' def _get_data(): r = requests.get(urllib.parse.urljoin(url, str(uid))) - return json.loads(r.content) + return json.loads(r.content.decode('utf-8')) data = _get_data() if block: while data.get('status') != 'done': diff --git a/yaksh/models.py b/yaksh/models.py index 87e6260..9b3cabe 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -24,7 +24,7 @@ import zipfile import tempfile from textwrap import dedent from .file_utils import extract_files, delete_files -from yaksh.xmlrpc_clients import code_server +from yaksh.code_server import submit, SERVER_POOL_PORT from django.conf import settings @@ -1266,7 +1266,7 @@ class AnswerPaper(models.Model): if question.type == 'code': return self.answers.filter(question=question).order_by('-id') - def validate_answer(self, user_answer, question, json_data=None): + def validate_answer(self, user_answer, question, json_data=None, uid=None): """ Checks whether the answer submitted by the user is right or wrong. If right then returns correct = True, success and @@ -1327,10 +1327,9 @@ class AnswerPaper(models.Model): elif question.type == 'code' or question.type == "upload": user_dir = self.user.profile.get_user_dir() - json_result = code_server.run_code( - question.language, json_data, user_dir - ) - result = json.loads(json_result) + url = 'http://localhost:%s' % SERVER_POOL_PORT + submit(url, uid, json_data, user_dir) + result = {'uid': uid, 'state': 'running'} return result def regrade(self, question_id): diff --git a/yaksh/static/yaksh/js/question.js b/yaksh/static/yaksh/js/question.js deleted file mode 100644 index 96ff3de..0000000 --- a/yaksh/static/yaksh/js/question.js +++ /dev/null @@ -1,12 +0,0 @@ -function submitCode() -{ - document.forms["code"].submit(); - var x = document.getElementById("status"); - x.innerHTML = "<strong>Checking answer ...</strong>"; - x = document.getElementById("check"); - x.disabled = true; - x.value = "Checking Answer ..."; - if (document.getElementById("skip")!=null) { - document.getElementById("skip").disabled = true; - } -} diff --git a/yaksh/static/yaksh/js/requesthandler.js b/yaksh/static/yaksh/js/requesthandler.js new file mode 100644 index 0000000..9d023cc --- /dev/null +++ b/yaksh/static/yaksh/js/requesthandler.js @@ -0,0 +1,90 @@ +request_status = "initial" +function submitRequest(){ + document.forms["code"].submit(); +} + +function check_state(state, uid, success) { + if (state == "running") { + setTimeout(get_result(uid), 2000); + } +} + +function get_result(uid){ + $.ajax({ + method: "GET", + url: "/exam/get_results/"+uid+"/", + dataType: "html", // Your server can response html, json, xml format. + success: function(data, status, xhr) { + content_type = xhr.getResponseHeader("content-type"); + if(content_type.includes("text/html")) { + request_status = "initial"; + document.open(); + document.write(data); + document.close(); + } else if(content_type.includes("application/json")) { + res = JSON.parse(data); + request_status = res.status; + check_state(request_status, uid, res.success); + } + } + }); +} + +var global_editor = {}; +$(document).ready(function(){ + // Codemirror object, language modes and initial content + // Get the textarea node + var textarea_node = document.querySelector('#answer'); + + var mode_dict = { + 'python': 'python', + 'c': 'text/x-csrc', + 'cpp': 'text/x-c++src', + 'java': 'text/x-java', + 'bash': 'text/x-sh', + 'scilab': 'text/x-csrc' + } + + // Code mirror Options + var options = { + mode: mode_dict[lang], + gutter: true, + lineNumbers: true, + onChange: function (instance, changes) { + render(); + } + }; + + // Initialize the codemirror editor + global_editor.editor = CodeMirror.fromTextArea(textarea_node, options); + + // Setting code editors initial content + global_editor.editor.setValue(init_val); + + function reset_editor() { + global_editor.editor.setValue(init_val); + global_editor.editor.clearHistory(); + } + $('#code').submit(function(e) { + $.ajax({ + type: 'POST', + url: $(this).attr("action"), + data: $(this).serializeArray(), + dataType: "html", // Your server can response html, json, xml format. + success: function(data, status, xhr) { + content_type = xhr.getResponseHeader("content-type"); + if(content_type.includes("text/html")) { + document.open(); + document.write(data); + document.close(); + } else if(content_type.includes("application/json")) { + res = JSON.parse(data); + var uid = res.uid; + request_status = res.state; + check_state(request_status, uid, false); + } + } + }); + e.preventDefault(); // To stop the default form submission. + }); +}); diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html index ee33523..9df2fef 100644 --- a/yaksh/templates/yaksh/question.html +++ b/yaksh/templates/yaksh/question.html @@ -15,7 +15,7 @@ {% endblock %} {% block script %} -<script src="{{ URL_ROOT }}/static/yaksh/js/question.js"></script> +<script src="{{ URL_ROOT }}/static/yaksh/js/requesthandler.js"></script> <script src="{{ URL_ROOT }}/static/yaksh/js/codemirror/lib/codemirror.js"></script> <script src="{{ URL_ROOT }}/static/yaksh/js/codemirror/mode/python/python.js"></script> <script src="{{ URL_ROOT }}/static/yaksh/js/codemirror/mode/clike/clike.js"></script> @@ -79,7 +79,7 @@ function validate(){ } else { - return true; + send_request(); } } @@ -89,47 +89,8 @@ function call_skip(url) form.action = url form.submit(); } -</script> -<script> - - var init_val = '{{ last_attempt|escape_quotes|safe }}'; - var global_editor = {}; - $(document).ready(function(){ - // Codemirror object, language modes and initial content - // Get the textarea node - var textarea_node = document.querySelector('#answer'); - - var lang = "{{ question.language }}" - var mode_dict = { - 'python': 'python', - 'c': 'text/x-csrc', - 'cpp': 'text/x-c++src', - 'java': 'text/x-java', - 'bash': 'text/x-sh', - 'scilab': 'text/x-csrc' - } - - // Code mirror Options - var options = { - mode: mode_dict[lang], - gutter: true, - lineNumbers: true, - onChange: function (instance, changes) { - render(); - } - }; - - // Initialize the codemirror editor - global_editor.editor = CodeMirror.fromTextArea(textarea_node, options); - - // Setting code editors initial content - global_editor.editor.setValue(init_val); - - function reset_editor() { - global_editor.editor.setValue(init_val); - global_editor.editor.clearHistory(); - } - }); +init_val = '{{ last_attempt|escape_quotes|safe }}'; +lang = "{{ question.language }}" </script> {% endblock script %} @@ -239,13 +200,13 @@ function call_skip(url) <div class="from-group"> {% if question.type == "mcq" or "mcc" or "integer" or "float" or "string" %} - <br><button class="btn btn-primary" type="submit" name="check" id="check">Submit Answer</button> <br/> + <br><button class="btn btn-primary" type="submit" name="check" id="check" >Submit Answer</button> <br/> {% elif question.type == "upload" %} <br><button class="btn btn-primary" type="submit" name="check" id="check" onClick="return validate();">Upload</button> {% else %} {% 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> + <button class="btn btn-primary" type="submit" name="check" id="check" >Check Answer <span class="glyphicon glyphicon-cog"></span></button> {% endif %} {% endif %} diff --git a/yaksh/urls.py b/yaksh/urls.py index 5270068..6daaf46 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -20,6 +20,7 @@ urlpatterns = [ views.complete), url(r'^register/$', views.user_register, name="register"), url(r'^(?P<q_id>\d+)/check/$', views.check), + url(r'^get_results/(?P<uid>\d+)/$', views.get_results), url(r'^(?P<q_id>\d+)/check/(?P<attempt_num>\d+)/(?P<questionpaper_id>\d+)/$',\ views.check), url(r'^(?P<q_id>\d+)/skip/(?P<attempt_num>\d+)/(?P<questionpaper_id>\d+)/$', diff --git a/yaksh/views.py b/yaksh/views.py index c10ba6a..f6243a7 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -4,7 +4,7 @@ import os from datetime import datetime, timedelta import collections import csv -from django.http import HttpResponse +from django.http import HttpResponse, JsonResponse from django.core.urlresolvers import reverse from django.contrib.auth import login, logout, authenticate from django.shortcuts import render_to_response, get_object_or_404, redirect @@ -30,6 +30,7 @@ try: except ImportError: from io import BytesIO as string_io # Local imports. +from yaksh.code_server import get_result, SERVER_POOL_PORT from yaksh.models import get_model_class, Quiz, Question, QuestionPaper, QuestionSet, Course from yaksh.models import Profile, Answer, AnswerPaper, User, TestCase, FileUpload,\ has_profile, StandardTestCase, McqTestCase,\ @@ -539,6 +540,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): new_answer = Answer(question=current_question, answer=user_answer, correct=False, error=json.dumps([])) new_answer.save() + uid = new_answer.id paper.answers.add(new_answer) # 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 @@ -547,38 +549,61 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): if current_question.type == 'code' or \ current_question.type == 'upload' else None result = paper.validate_answer(user_answer, current_question, - json_data + json_data, uid ) - if result.get('success'): - 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' or current_question.type == 'upload' \ - else current_question.points - new_answer.correct = result.get('success') - error_message = None - new_answer.error = json.dumps(result.get('error')) - next_question = paper.add_completed_question(current_question.id) + if current_question.type in ['code', 'upload']: + return JsonResponse(result) 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' or current_question.type == 'upload' \ - else 0 - error_message = result.get('error') if current_question.type == 'code' \ - or current_question.type == 'upload' else None - new_answer.error = json.dumps(result.get('error')) - next_question = current_question if current_question.type == 'code' \ - or current_question.type == 'upload' \ - else paper.add_completed_question(current_question.id) - new_answer.save() - paper.update_marks('inprogress') - paper.set_end_time(timezone.now()) - return show_question(request, next_question, paper, error_message) + next_question, error_message, paper = _update_paper(request, uid, result) + return show_question(request, next_question, paper, error_message) else: return show_question(request, current_question, paper) +@csrf_exempt +def get_results(request, uid): + url = 'http://localhost:%s' % SERVER_POOL_PORT + result_state = get_result(url, uid) + result = json.loads(result_state.get('result')) + next_question, error_message, paper = _update_paper(request, uid, result) + result['status'] = result_state.get('status') + if result['status']== 'done': + return show_question(request, next_question, paper, error_message) + return JsonResponse(result) + + +def _update_paper(request, uid, result): + new_answer = Answer.objects.get(id=uid) + current_question = new_answer.question + paper = new_answer.answerpaper_set.first() + + if result.get('success'): + 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' or current_question.type == 'upload' \ + else current_question.points + new_answer.correct = result.get('success') + error_message = None + new_answer.error = json.dumps(result.get('error')) + 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' or current_question.type == 'upload' \ + else 0 + error_message = result.get('error') if current_question.type == 'code' \ + or current_question.type == 'upload' else None + new_answer.error = json.dumps(result.get('error')) + next_question = current_question if current_question.type == 'code' \ + or current_question.type == 'upload' \ + else paper.add_completed_question(current_question.id) + new_answer.save() + paper.update_marks('inprogress') + paper.set_end_time(timezone.now()) + return next_question, error_message, paper + def quit(request, reason=None, attempt_num=None, questionpaper_id=None): """Show the quit page when the user logs out.""" |