From 3fe840b52e4d780587f14d06a46fcab523ba23c3 Mon Sep 17 00:00:00 2001 From: maheshgudi Date: Mon, 20 Aug 2018 17:35:55 +0530 Subject: Add syntax highlight for code answers in answerpaper --- yaksh/templates/yaksh/grade_user.html | 5 ++++- yaksh/templates/yaksh/user_data.html | 4 +++- yaksh/templates/yaksh/view_answerpaper.html | 4 +++- yaksh/templatetags/custom_filters.py | 13 +++++++++++++ 4 files changed, 23 insertions(+), 3 deletions(-) (limited to 'yaksh') diff --git a/yaksh/templates/yaksh/grade_user.html b/yaksh/templates/yaksh/grade_user.html index 2e5a403..bc9ed87 100644 --- a/yaksh/templates/yaksh/grade_user.html +++ b/yaksh/templates/yaksh/grade_user.html @@ -298,7 +298,9 @@ Status : Failed
{% if question.type == "code" %} -
{{ ans.answer.answer.strip|safe }}
+ {% pygmentise_user_answer question.language ans.answer.answer.strip as user_answer %} + +
{{user_answer.0|safe}}
{% elif question.type == "mcc"%}
{% for testcases in question.get_test_cases %} @@ -327,6 +329,7 @@ Status : Failed
{% else %}
{{ ans.answer.answer.strip|safe }} +
{% endif %}
diff --git a/yaksh/templates/yaksh/user_data.html b/yaksh/templates/yaksh/user_data.html index 9449fcc..72397dc 100644 --- a/yaksh/templates/yaksh/user_data.html +++ b/yaksh/templates/yaksh/user_data.html @@ -233,7 +233,9 @@ User IP address: {{ paper.user_ip }} {{ answer.answer.answer.strip|safe }}
{% else %} -
{{ answer.answer.answer.strip|safe }}
+ {% pygmentise_user_answer question.language answer.answer.answer.strip as user_answer %} + +
{{user_answer.0|safe}}
{% endif %} diff --git a/yaksh/templates/yaksh/view_answerpaper.html b/yaksh/templates/yaksh/view_answerpaper.html index 8f3fad7..e3f7df4 100644 --- a/yaksh/templates/yaksh/view_answerpaper.html +++ b/yaksh/templates/yaksh/view_answerpaper.html @@ -229,7 +229,9 @@ {% endfor %} {% endwith %}
-
{{ answer.answer.answer.strip }}
+ {% pygmentise_user_answer question.language answer.answer.answer.strip as user_answer %} + +
{{user_answer.0|safe}}
{% endif %} diff --git a/yaksh/templatetags/custom_filters.py b/yaksh/templatetags/custom_filters.py index 056421f..2fdc1d2 100644 --- a/yaksh/templatetags/custom_filters.py +++ b/yaksh/templatetags/custom_filters.py @@ -6,6 +6,9 @@ try: from itertools import zip_longest except ImportError: from itertools import izip_longest as zip_longest +from pygments import highlight +from pygments.lexers import get_lexer_by_name +from pygments.formatters import HtmlFormatter register = template.Library() @@ -87,3 +90,13 @@ def get_answer_for_arrange_options(ans, question): @register.filter(name='replace_spaces') def replace_spaces(name): return name.replace(" ", "_") + +@register.simple_tag +def pygmentise_user_answer(language, answer): + lexer = get_lexer_by_name(language, stripall=True) + formatter = HtmlFormatter(linenos="inline", + cssclass="highlight", + style="colorful") + style = formatter.get_style_defs('.highlight') + result = highlight(answer, lexer, formatter) + return result, style -- cgit From 4def423d573beceee07780f21bcd55836d50b558 Mon Sep 17 00:00:00 2001 From: maheshgudi Date: Mon, 13 Aug 2018 15:36:48 +0530 Subject: Allow new line to be added in an stdio testcase --- yaksh/error_messages.py | 5 ++--- yaksh/python_stdio_evaluator.py | 3 ++- yaksh/templates/yaksh/add_question.html | 2 ++ yaksh/templates/yaksh/error_template.html | 11 +++++++++-- yaksh/templates/yaksh/moderator_dashboard.html | 6 +++++- yaksh/templatetags/custom_filters.py | 10 ++++++++++ yaksh/views.py | 16 ++++++++++++++-- 7 files changed, 44 insertions(+), 9 deletions(-) (limited to 'yaksh') diff --git a/yaksh/error_messages.py b/yaksh/error_messages.py index f34bf28..bf79ef8 100644 --- a/yaksh/error_messages.py +++ b/yaksh/error_messages.py @@ -33,9 +33,8 @@ def prettify_exceptions(exception, message, traceback=None, def _get_incorrect_user_lines(exp_lines, user_lines): err_line_numbers = [] for line_no, (expected_line, user_line) in \ - enumerate(zip_longest(exp_lines, user_lines)): - if (not user_line or not expected_line or - user_line.strip() != expected_line.strip()): + enumerate(zip_longest(exp_lines, user_lines)): + if user_line != expected_line: err_line_numbers.append(line_no) return err_line_numbers diff --git a/yaksh/python_stdio_evaluator.py b/yaksh/python_stdio_evaluator.py index 64a2809..a1e8f72 100644 --- a/yaksh/python_stdio_evaluator.py +++ b/yaksh/python_stdio_evaluator.py @@ -46,6 +46,7 @@ class PythonStdIOEvaluator(BaseEvaluator): if self.file_paths: self.files = copy_files(self.file_paths) submitted = compile(self.user_answer, '', mode='exec') + self.expected_output = self.expected_output.replace('\r', '') if self.expected_input: self.expected_input = self.expected_input.replace('\r', '') input_buffer = StringIO() @@ -55,7 +56,7 @@ class PythonStdIOEvaluator(BaseEvaluator): with redirect_stdout() as output_buffer: exec_scope = {} exec(submitted, exec_scope) - self.output_value = output_buffer.getvalue().rstrip("\n") + self.output_value = output_buffer.getvalue() return self.output_value def check_code(self): diff --git a/yaksh/templates/yaksh/add_question.html b/yaksh/templates/yaksh/add_question.html index 692af48..c4cd8a7 100644 --- a/yaksh/templates/yaksh/add_question.html +++ b/yaksh/templates/yaksh/add_question.html @@ -50,7 +50,9 @@ {% for form in formset %} {% endfor %} diff --git a/yaksh/templates/yaksh/error_template.html b/yaksh/templates/yaksh/error_template.html index 5530844..00fa306 100644 --- a/yaksh/templates/yaksh/error_template.html +++ b/yaksh/templates/yaksh/error_template.html @@ -1,4 +1,11 @@ +{% block css%} + +{% endblock %} +{% block script %} + +{% endblock %} + {% load custom_filters %} {% if error_message %}
@@ -64,8 +71,8 @@ {% for expected,user in error.expected_output|zip:error.user_output %} {{forloop.counter}} - {{expected|default:""}} - {{user|default:""}} + {{expected|default:""|highlight_spaces|safe}} + {{user|default:""|highlight_spaces|safe}} {% if forloop.counter0 in error.error_line_numbers or not expected or not user %} {% else %} diff --git a/yaksh/templates/yaksh/moderator_dashboard.html b/yaksh/templates/yaksh/moderator_dashboard.html index 503cc72..59dd123 100644 --- a/yaksh/templates/yaksh/moderator_dashboard.html +++ b/yaksh/templates/yaksh/moderator_dashboard.html @@ -102,7 +102,11 @@ {% endfor %} -
+
+
{% endif %} diff --git a/yaksh/templatetags/custom_filters.py b/yaksh/templatetags/custom_filters.py index b59d320..e51b8aa 100644 --- a/yaksh/templatetags/custom_filters.py +++ b/yaksh/templatetags/custom_filters.py @@ -101,3 +101,13 @@ def pygmentise_user_answer(language, answer): style = formatter.get_style_defs('.highlight') result = highlight(answer, lexer, formatter) return result, style + + +@register.simple_tag +def course_grade(course, user): + return course.get_grade(user) + + +@register.filter(name='highlight_spaces') +def highlight_spaces(text): + return text.replace(" ",' ') diff --git a/yaksh/views.py b/yaksh/views.py index 0bf91eb..440236c 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -6,10 +6,12 @@ from django.shortcuts import render, get_object_or_404, redirect from django.template import Context, Template from django.http import Http404 from django.db.models import Max, Q, F +from django.db import models from django.views.decorators.csrf import csrf_exempt from django.contrib.auth.decorators import login_required from django.contrib.auth.models import Group from django.forms.models import inlineformset_factory +from django.forms import fields from django.utils import timezone from django.core.exceptions import ( MultipleObjectsReturned, ObjectDoesNotExist @@ -96,6 +98,12 @@ def get_html_text(md_text): """Takes markdown text and converts it to html""" return Markdown().convert(md_text) +def formfield_callback(field): + if (isinstance(field, models.TextField) + and field.name == 'expected_output' + or field.name == 'expected_input'): + return fields.CharField(strip=False) + return field.formfield() @email_verified def index(request, next_url=None): @@ -243,8 +251,12 @@ def add_question(request, question_id=None): file.toggle_hide_status() formsets = [] for testcase in TestCase.__subclasses__(): - formset = inlineformset_factory(Question, testcase, extra=0, - fields='__all__') + + formset = inlineformset_factory( + Question, testcase, extra=0, + fields='__all__', + formfield_callback = formfield_callback + ) formsets.append(formset( request.POST, request.FILES, instance=question ) -- cgit From 3a0acf73017899067b75b23b7df0eac0ab888abf Mon Sep 17 00:00:00 2001 From: maheshgudi Date: Mon, 15 Oct 2018 16:08:54 +0530 Subject: Add testcase for highlight spaces --- yaksh/templatetags/test_custom_filters.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'yaksh') diff --git a/yaksh/templatetags/test_custom_filters.py b/yaksh/templatetags/test_custom_filters.py index eb1f0fb..9816dc8 100644 --- a/yaksh/templatetags/test_custom_filters.py +++ b/yaksh/templatetags/test_custom_filters.py @@ -11,7 +11,8 @@ from yaksh.models import (User, Profile, Question, Quiz, QuestionPaper, from yaksh.templatetags.custom_filters import (completed, inprogress, get_ordered_testcases, - get_answer_for_arrange_options + get_answer_for_arrange_options, + highlight_spaces ) @@ -148,3 +149,9 @@ class CustomFiltersTestCases(unittest.TestCase): self.assertSequenceEqual(testcases, ordered_testcases) new_answerpaper.delete() + + def test_highlight_spaces(self): + expected_output = "A " + highlighted_output = highlight_spaces(expected_output) + self.assertEqual(highlighted_output, + 'A ') -- cgit From 28c64f6f1771fe93385e3767d5bee9fe55af8c82 Mon Sep 17 00:00:00 2001 From: maheshgudi Date: Mon, 15 Oct 2018 17:23:43 +0530 Subject: Fix pep8 for stdio spacing error --- yaksh/error_messages.py | 2 +- yaksh/templatetags/custom_filters.py | 4 +++- yaksh/templatetags/test_custom_filters.py | 4 +++- yaksh/views.py | 16 ++++++++-------- 4 files changed, 15 insertions(+), 11 deletions(-) (limited to 'yaksh') diff --git a/yaksh/error_messages.py b/yaksh/error_messages.py index bf79ef8..2d27417 100644 --- a/yaksh/error_messages.py +++ b/yaksh/error_messages.py @@ -33,7 +33,7 @@ def prettify_exceptions(exception, message, traceback=None, def _get_incorrect_user_lines(exp_lines, user_lines): err_line_numbers = [] for line_no, (expected_line, user_line) in \ - enumerate(zip_longest(exp_lines, user_lines)): + enumerate(zip_longest(exp_lines, user_lines)): if user_line != expected_line: err_line_numbers.append(line_no) return err_line_numbers diff --git a/yaksh/templatetags/custom_filters.py b/yaksh/templatetags/custom_filters.py index e51b8aa..92a1169 100644 --- a/yaksh/templatetags/custom_filters.py +++ b/yaksh/templatetags/custom_filters.py @@ -110,4 +110,6 @@ def course_grade(course, user): @register.filter(name='highlight_spaces') def highlight_spaces(text): - return text.replace(" ",' ') + return text.replace( + " ", ' ' + ) diff --git a/yaksh/templatetags/test_custom_filters.py b/yaksh/templatetags/test_custom_filters.py index 9816dc8..9d7f246 100644 --- a/yaksh/templatetags/test_custom_filters.py +++ b/yaksh/templatetags/test_custom_filters.py @@ -58,6 +58,7 @@ def tearDownModule(): User.objects.get(username="teacher2000").delete() Group.objects.all().delete() + class CustomFiltersTestCases(unittest.TestCase): @classmethod @@ -154,4 +155,5 @@ class CustomFiltersTestCases(unittest.TestCase): expected_output = "A " highlighted_output = highlight_spaces(expected_output) self.assertEqual(highlighted_output, - 'A ') + 'A ' + ) diff --git a/yaksh/views.py b/yaksh/views.py index 440236c..56f1873 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -98,13 +98,14 @@ def get_html_text(md_text): """Takes markdown text and converts it to html""" return Markdown().convert(md_text) + def formfield_callback(field): - if (isinstance(field, models.TextField) - and field.name == 'expected_output' - or field.name == 'expected_input'): + if (isinstance(field, models.TextField) and field.name == 'expected_output' + or field.name == 'expected_input'): return fields.CharField(strip=False) return field.formfield() + @email_verified def index(request, next_url=None): """The start page. @@ -255,7 +256,7 @@ def add_question(request, question_id=None): formset = inlineformset_factory( Question, testcase, extra=0, fields='__all__', - formfield_callback = formfield_callback + formfield_callback=formfield_callback ) formsets.append(formset( request.POST, request.FILES, instance=question @@ -941,10 +942,9 @@ def complete(request, reason=None, attempt_num=None, questionpaper_id=None, """Show a page to inform user that the quiz has been completed.""" user = request.user if questionpaper_id is None: - message = ( - reason or "An Unexpected Error occurred." - " Please contact your instructor/administrator." - ) + message = reason or ("An Unexpected Error occurred. Please " + "contact your instructor/administrator." + ) context = {'message': message} return my_render_to_response(request, 'yaksh/complete.html', context) else: -- cgit From 6eca1dd646eff0c1f90c129e10e7959e79eb83a2 Mon Sep 17 00:00:00 2001 From: ankitjavalkar Date: Wed, 18 Dec 2019 14:49:16 +0530 Subject: Fix bug to only allow self created grading systems to show up in the course form --- yaksh/forms.py | 24 +++++++++++++++++------- yaksh/views.py | 4 ++-- 2 files changed, 19 insertions(+), 9 deletions(-) (limited to 'yaksh') diff --git a/yaksh/forms.py b/yaksh/forms.py index 57140bc..742212a 100644 --- a/yaksh/forms.py +++ b/yaksh/forms.py @@ -3,6 +3,7 @@ from yaksh.models import ( get_model_class, Profile, Quiz, Question, Course, QuestionPaper, Lesson, LearningModule ) +from grades.models import GradingSystem from django.contrib.auth import authenticate from django.contrib.auth.models import User from django.conf import settings @@ -268,6 +269,13 @@ class QuestionFilterForm(forms.Form): class CourseForm(forms.ModelForm): """ course form for moderators """ + class Meta: + model = Course + fields = [ + 'name', 'enrollment', 'active', 'code', 'instructions', + 'start_enroll_time', 'end_enroll_time', 'grading_system', + 'view_grade' + ] def save(self, commit=True, *args, **kwargs): instance = super(CourseForm, self).save(commit=False) @@ -280,13 +288,15 @@ class CourseForm(forms.ModelForm): instance.save() return instance - class Meta: - model = Course - fields = [ - 'name', 'enrollment', 'active', 'code', 'instructions', - 'start_enroll_time', 'end_enroll_time', 'grading_system', - 'view_grade' - ] + def __init__(self, user, *args, **kwargs): + super(CourseForm, self).__init__(*args, **kwargs) + if self.instance.id and self.instance.teachers.filter(id=user.id).exists(): + self.fields['grading_system'].widget.attrs['disabled'] = True + else: + grading_choices = GradingSystem.objects.filter( + creator=user + ) + self.fields['grading_system'].queryset = grading_choices class ProfileForm(forms.ModelForm): diff --git a/yaksh/views.py b/yaksh/views.py index 0bf91eb..afb69ac 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -970,7 +970,7 @@ def add_course(request, course_id=None): if not is_moderator(user): raise Http404('You are not allowed to view this page') if request.method == 'POST': - form = CourseForm(request.POST, instance=course) + form = CourseForm(user, request.POST, instance=course) if form.is_valid(): new_course = form.save(commit=False) if course_id is None: @@ -982,7 +982,7 @@ def add_course(request, course_id=None): request, 'yaksh/add_course.html', {'form': form} ) else: - form = CourseForm(instance=course) + form = CourseForm(user, instance=course) return my_render_to_response( request, 'yaksh/add_course.html', {'form': form} ) -- cgit From ca07207d515ecdc8466bb5dc4203bbcfab1a04b1 Mon Sep 17 00:00:00 2001 From: prathamesh Date: Sun, 12 Jan 2020 12:25:28 +0530 Subject: evaluator for r- Initial --- yaksh/models.py | 1 + yaksh/r_code_evaluator.py | 96 +++++++++++++++++++++++++++++++++++++++++++++++ yaksh/settings.py | 4 ++ 3 files changed, 101 insertions(+) create mode 100644 yaksh/r_code_evaluator.py (limited to 'yaksh') diff --git a/yaksh/models.py b/yaksh/models.py index 6881b4f..6edb9ea 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -46,6 +46,7 @@ languages = ( ("cpp", "C++ Language"), ("java", "Java Language"), ("scilab", "Scilab"), + ("r", "R"), ) question_types = ( diff --git a/yaksh/r_code_evaluator.py b/yaksh/r_code_evaluator.py new file mode 100644 index 0000000..ca4c94a --- /dev/null +++ b/yaksh/r_code_evaluator.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +from __future__ import unicode_literals +import os +import subprocess +import re + +# Local imports +from .base_evaluator import BaseEvaluator +from .file_utils import copy_files, delete_files + + +class RCodeEvaluator(BaseEvaluator): + """Tests the R code obtained from Code Server""" + def __init__(self, metadata, test_case_data): + self.files = [] + self.submit_code_path = "" + self.test_code_path = "" + # Set metadata values + self.user_answer = metadata.get('user_answer') + self.file_paths = metadata.get('file_paths') + self.partial_grading = metadata.get('partial_grading') + + # Set test case data values + self.test_case = test_case_data.get('test_case') + self.weight = test_case_data.get('weight') + + def teardown(self): + # Delete the created file. + if os.path.exists(self.submit_code_path): + os.remove(self.submit_code_path) + if os.path.exists(self.test_code_path): + os.remove(self.test_code_path) + if self.files: + delete_files(self.files) + + def check_code(self): + self.submit_code_path = self.create_submit_code_file('function.r') + self.test_code_path = self.create_submit_code_file('main.r') + if self.file_paths: + self.files = copy_files(self.file_paths) + clean_ref_path = self.test_code_path + self.user_answer, terminate_commands = \ + self._remove_r_quit(self.user_answer.lstrip()) + + success = False + mark_fraction = 0.0 + self.write_to_submit_code_file(self.submit_code_path, self.user_answer) + self.write_to_submit_code_file(self.test_code_path, self.test_case) + # Throw message if there are commmands that terminates scilab + add_err = "" + if terminate_commands: + add_err = "Please do not use quit() in your\ + code.\n Otherwise your code will not be evaluated\ + correctly.\n" + + cmd = 'Rscript main.r' + ret = self._run_command(cmd, shell=True, stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + proc, stdout, stderr = ret + + if stderr is '': + # Clean output + stdout = self._strip_output(stdout) + if proc.returncode == 31: + success, err = True, None + mark_fraction = 1.0 if self.partial_grading else 0.0 + else: + err = add_err + stdout + else: + err = add_err + stderr + + return success, err, mark_fraction + + def _remove_r_quit(self, string): + """ + Removes quit from the R code + """ + new_string = "" + terminate_commands = False + for line in string.splitlines(): + new_line = re.sub(r"quit.*$", "", line) + if line != new_line: + terminate_commands = True + new_string = new_string + '\n' + new_line + return new_string, terminate_commands + + def _strip_output(self, out): + """ + Cleans whitespace from the output + """ + strip_out = "Message" + for l in out.split('\n'): + if l.strip(): + strip_out = strip_out+"\n"+l.strip() + return strip_out + out diff --git a/yaksh/settings.py b/yaksh/settings.py index 9e9597d..7b42298 100644 --- a/yaksh/settings.py +++ b/yaksh/settings.py @@ -55,4 +55,8 @@ code_evaluators = { "standardtestcase": "yaksh.scilab_code_evaluator.ScilabCodeEvaluator", "hooktestcase": "yaksh.hook_evaluator.HookEvaluator" }, + "r": { + "standardtestcase": "yaksh.r_code_evaluator.RCodeEvaluator", + "hooktestcase": "yaksh.hook_evaluator.HookEvaluator" + }, } -- cgit