From 636b03ddbd5d5ccb92dcf1d48f6314776b0cce27 Mon Sep 17 00:00:00 2001
From: AlexandreDoneux <94830560+AlexandreDoneux@users.noreply.github.com>
Date: Tue, 17 Oct 2023 10:14:12 +0200
Subject: [PATCH 01/12] [frontend/task_problems] Adding line offset for
CodeMirror code blocks
A teacher could need to change the code lines offset in order to send back correct review when parsing student code into a template.
Problem : changing the first_line parameter in the template_helper.render() function is effective only after restarting the server.
---
inginious/frontend/app.py | 1 +
inginious/frontend/static/js/common.js | 5 +++--
inginious/frontend/task_problems.py | 2 +-
inginious/frontend/templates/tasks/code.html | 1 +
4 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/inginious/frontend/app.py b/inginious/frontend/app.py
index 5f4e8c522..f5f38236e 100644
--- a/inginious/frontend/app.py
+++ b/inginious/frontend/app.py
@@ -302,6 +302,7 @@ def flask_internalerror(e):
flask_app.privacy_page = config.get("privacy_page", None)
flask_app.static_directory = config.get("static_directory", "./static")
flask_app.webdav_host = config.get("webdav_host", None)
+ flask_app.jinja_env.auto_reload = True
# Init the mapping of the app
init_flask_mapping(flask_app)
diff --git a/inginious/frontend/static/js/common.js b/inginious/frontend/static/js/common.js
index 0382ffe75..f6efe38d9 100644
--- a/inginious/frontend/static/js/common.js
+++ b/inginious/frontend/static/js/common.js
@@ -10,7 +10,7 @@ function init_common()
colorizeStaticCode();
$('.code-editor').each(function(index, elem)
{
- registerCodeEditor(elem, $(elem).attr('data-x-language'), $(elem).attr('data-x-lines'));
+ registerCodeEditor(elem, $(elem).attr('data-x-language'), $(elem).attr('data-x-lines'), $(elem).attr('data-x-first-line'));
});
//Fix a bug with codemirror and bootstrap tabs
@@ -69,7 +69,7 @@ function colorizeStaticCode()
}
//Register and init a code editor (ace)
-function registerCodeEditor(textarea, lang, lines)
+function registerCodeEditor(textarea, lang, lines, firstline)
{
var mode = CodeMirror.findModeByName(lang);
if(mode == undefined)
@@ -79,6 +79,7 @@ function registerCodeEditor(textarea, lang, lines)
var editor = CodeMirror.fromTextArea(textarea, {
lineNumbers: true,
+ firstLineNumber: parseInt(firstline),
mode: mode["mime"],
foldGutter: true,
styleActiveLine: true,
diff --git a/inginious/frontend/task_problems.py b/inginious/frontend/task_problems.py
index 7c62eef39..fcb2bed09 100644
--- a/inginious/frontend/task_problems.py
+++ b/inginious/frontend/task_problems.py
@@ -82,7 +82,7 @@ def show_input(self, template_helper, language, seed):
header = ParsableText(self.gettext(language,self._header), "rst",
translation=self.get_translation_obj(language))
return template_helper.render("tasks/code.html", inputId=self.get_id(), header=header,
- lines=8, maxChars=0, language=self._language, optional=self._optional,
+ lines=8, first_line=1, maxChars=0, language=self._language, optional=self._optional,
default=self._default)
@classmethod
diff --git a/inginious/frontend/templates/tasks/code.html b/inginious/frontend/templates/tasks/code.html
index 8fef8f3a7..0dda84a57 100644
--- a/inginious/frontend/templates/tasks/code.html
+++ b/inginious/frontend/templates/tasks/code.html
@@ -9,4 +9,5 @@
class="code-editor form-control {% if '/' in inputId %} single {% endif %}"
data-x-language="{{language}}"
data-x-lines="{{lines}}"
+ data-x-first-line="{{first_line}}"
data-optional="{% if optional %}{{True}}{% else %}{{False}}{% endif %}">{{ default }}
From 17c56c29752845ec51e21d38f96f2d55fcbfc91f Mon Sep 17 00:00:00 2001
From: AlexandreDoneux <94830560+AlexandreDoneux@users.noreply.github.com>
Date: Tue, 17 Oct 2023 11:11:48 +0200
Subject: [PATCH 02/12] [frontend/task_problems] Displaying no line offset when
first_line parameter not defined
---
inginious/frontend/static/js/common.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/inginious/frontend/static/js/common.js b/inginious/frontend/static/js/common.js
index f6efe38d9..d00f6939b 100644
--- a/inginious/frontend/static/js/common.js
+++ b/inginious/frontend/static/js/common.js
@@ -10,7 +10,7 @@ function init_common()
colorizeStaticCode();
$('.code-editor').each(function(index, elem)
{
- registerCodeEditor(elem, $(elem).attr('data-x-language'), $(elem).attr('data-x-lines'), $(elem).attr('data-x-first-line'));
+ registerCodeEditor(elem, $(elem).attr('data-x-language'), $(elem).attr('data-x-lines'), $(elem).attr('data-x-first-line') !== undefined ? $(elem).attr('data-x-first-line') : "1");
});
//Fix a bug with codemirror and bootstrap tabs
From d1542aa6f372e6bac4032a5647dd2818d44cf741 Mon Sep 17 00:00:00 2001
From: AlexandreDoneux <94830560+AlexandreDoneux@users.noreply.github.com>
Date: Tue, 17 Oct 2023 16:14:49 +0200
Subject: [PATCH 03/12] [frontend/common] Fixing firstLineNumber param for
CodeMirror
Was checking the value of data-x-first-line only for textareas with code-editor class. Now doing it in registerCodeEditor() function for every case.
---
inginious/frontend/app.py | 1 -
inginious/frontend/static/js/common.js | 4 +++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/inginious/frontend/app.py b/inginious/frontend/app.py
index f5f38236e..5f4e8c522 100644
--- a/inginious/frontend/app.py
+++ b/inginious/frontend/app.py
@@ -302,7 +302,6 @@ def flask_internalerror(e):
flask_app.privacy_page = config.get("privacy_page", None)
flask_app.static_directory = config.get("static_directory", "./static")
flask_app.webdav_host = config.get("webdav_host", None)
- flask_app.jinja_env.auto_reload = True
# Init the mapping of the app
init_flask_mapping(flask_app)
diff --git a/inginious/frontend/static/js/common.js b/inginious/frontend/static/js/common.js
index d00f6939b..b2088995a 100644
--- a/inginious/frontend/static/js/common.js
+++ b/inginious/frontend/static/js/common.js
@@ -10,7 +10,7 @@ function init_common()
colorizeStaticCode();
$('.code-editor').each(function(index, elem)
{
- registerCodeEditor(elem, $(elem).attr('data-x-language'), $(elem).attr('data-x-lines'), $(elem).attr('data-x-first-line') !== undefined ? $(elem).attr('data-x-first-line') : "1");
+ registerCodeEditor(elem, $(elem).attr('data-x-language'), $(elem).attr('data-x-lines'), $(elem).attr('data-x-first-line'));
});
//Fix a bug with codemirror and bootstrap tabs
@@ -76,6 +76,8 @@ function registerCodeEditor(textarea, lang, lines, firstline)
mode = {"mode": "plain", "mime": "text/plain"};
var is_single = $(textarea).hasClass('single');
+ // if firstline not given, set to "1"
+ var firstline = firstline ?? "1";
var editor = CodeMirror.fromTextArea(textarea, {
lineNumbers: true,
From 57d6af4496dbe86f9e26b698211045362e2905e9 Mon Sep 17 00:00:00 2001
From: AlexandreDoneux <94830560+AlexandreDoneux@users.noreply.github.com>
Date: Tue, 17 Oct 2023 16:22:37 +0200
Subject: [PATCH 04/12] Adding form field for line offset in subproblem edit
---
.../templates/course_admin/subproblems/code.html | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/inginious/frontend/templates/course_admin/subproblems/code.html b/inginious/frontend/templates/course_admin/subproblems/code.html
index 49228f4d7..0b18dc9bb 100644
--- a/inginious/frontend/templates/course_admin/subproblems/code.html
+++ b/inginious/frontend/templates/course_admin/subproblems/code.html
@@ -47,3 +47,12 @@
{% endif %}
+
+{% if multiline %}
+
+{% endif %}
From f6958428ec249c06e160a9445c7cc30bd4f36707 Mon Sep 17 00:00:00 2001
From: AlexandreDoneux <94830560+AlexandreDoneux@users.noreply.github.com>
Date: Thu, 19 Oct 2023 08:50:26 +0200
Subject: [PATCH 05/12] Retrieving line offset from subproblem form, checking
it's value at submit and apply it to the problem's code block
---
inginious/frontend/pages/course_admin/task_edit.py | 14 ++++++++++++++
inginious/frontend/static/js/studio.js | 2 ++
inginious/frontend/task_problems.py | 3 ++-
3 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/inginious/frontend/pages/course_admin/task_edit.py b/inginious/frontend/pages/course_admin/task_edit.py
index f68da25e6..c4eaa8ff8 100644
--- a/inginious/frontend/pages/course_admin/task_edit.py
+++ b/inginious/frontend/pages/course_admin/task_edit.py
@@ -89,6 +89,7 @@ def POST_AUTH(self, courseid, taskid): # pylint: disable=arguments-differ
__, __ = self.get_course_and_check_rights(courseid, allow_all_staff=False)
data = flask.request.form.copy()
data["task_file"] = flask.request.files.get("task_file")
+ print(data)
# Else, parse content
try:
@@ -142,6 +143,19 @@ def POST_AUTH(self, courseid, taskid): # pylint: disable=arguments-differ
# Network grading
data["network_grading"] = "network_grading" in data
+
+ # Checking problem inputs
+ print(problems)
+ for problem, values in problems.items():
+ try:
+ offset = int(values["offset"])
+ if offset < 1:
+ return json.dumps(
+ {"status": "error", "message": _("The line offset for a problem must be positive!")})
+ except ValueError :
+ return json.dumps(
+ {"status": "error", "message": _("The line offset for a problem must be an integer!")})
+
except Exception as message:
return json.dumps({"status": "error", "message": _("Your browser returned an invalid form ({})").format(message)})
diff --git a/inginious/frontend/static/js/studio.js b/inginious/frontend/static/js/studio.js
index d309f8dca..40bf86fb7 100644
--- a/inginious/frontend/static/js/studio.js
+++ b/inginious/frontend/static/js/studio.js
@@ -411,6 +411,8 @@ function studio_init_template_code(well, pid, problem)
var default_editor = registerCodeEditor(default_tag, 'text', default_tag.tagName === "INPUT" ? 1 : 10);
if("default" in problem)
default_editor.setValue(problem["default"]);
+ if("offset" in problem)
+ $('#offset-' + pid, well).val(problem["offset"]);
}
/**
diff --git a/inginious/frontend/task_problems.py b/inginious/frontend/task_problems.py
index fcb2bed09..5aaec80e4 100644
--- a/inginious/frontend/task_problems.py
+++ b/inginious/frontend/task_problems.py
@@ -69,6 +69,7 @@ class DisplayableCodeProblem(CodeProblem, DisplayableProblem):
def __init__(self, problemid, content, translations, taskfs):
super(DisplayableCodeProblem, self).__init__(problemid, content, translations, taskfs)
+ self._first_line = content.get("offset", 1)
@classmethod
def get_type_name(cls, language):
@@ -82,7 +83,7 @@ def show_input(self, template_helper, language, seed):
header = ParsableText(self.gettext(language,self._header), "rst",
translation=self.get_translation_obj(language))
return template_helper.render("tasks/code.html", inputId=self.get_id(), header=header,
- lines=8, first_line=1, maxChars=0, language=self._language, optional=self._optional,
+ lines=8, first_line=self._first_line, maxChars=0, language=self._language, optional=self._optional,
default=self._default)
@classmethod
From 26fcaf93a0d88abc6bffca08292132c686ecec74 Mon Sep 17 00:00:00 2001
From: AlexandreDoneux <94830560+AlexandreDoneux@users.noreply.github.com>
Date: Thu, 19 Oct 2023 08:51:50 +0200
Subject: [PATCH 06/12] [static/common] fixing first line transformation to int
for CodeMirror
Was changing the firstline value into a integer or set it to 1 if it was not given. Was oing it outside the registerCodeEditor() function. But as it is used in other places this action wasn't performed each time. Doing this transformation inside the function now.
---
inginious/frontend/static/js/common.js | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/inginious/frontend/static/js/common.js b/inginious/frontend/static/js/common.js
index b2088995a..44a66975b 100644
--- a/inginious/frontend/static/js/common.js
+++ b/inginious/frontend/static/js/common.js
@@ -76,12 +76,16 @@ function registerCodeEditor(textarea, lang, lines, firstline)
mode = {"mode": "plain", "mime": "text/plain"};
var is_single = $(textarea).hasClass('single');
- // if firstline not given, set to "1"
- var firstline = firstline ?? "1";
+ // if firstline null or undefined, set to "1"
+ firstline = parseInt(firstline)?? "1";
+ if (isNaN(firstline))
+ firstline = 1;
+
+
var editor = CodeMirror.fromTextArea(textarea, {
lineNumbers: true,
- firstLineNumber: parseInt(firstline),
+ firstLineNumber: firstline,
mode: mode["mime"],
foldGutter: true,
styleActiveLine: true,
From f36d81906d394b1b3976ccb408dfa47257c05c02 Mon Sep 17 00:00:00 2001
From: AlexandreDoneux <94830560+AlexandreDoneux@users.noreply.github.com>
Date: Thu, 19 Oct 2023 09:06:38 +0200
Subject: [PATCH 07/12] [pages/task_edit] Allowing the line offset field to be
empty
When the line offset was empty it was retrieved as an empty string. It couldn't be transformed into an integer so an error message appeared. Fixed that.
---
.../frontend/pages/course_admin/task_edit.py | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/inginious/frontend/pages/course_admin/task_edit.py b/inginious/frontend/pages/course_admin/task_edit.py
index c4eaa8ff8..d96bfc44a 100644
--- a/inginious/frontend/pages/course_admin/task_edit.py
+++ b/inginious/frontend/pages/course_admin/task_edit.py
@@ -145,16 +145,16 @@ def POST_AUTH(self, courseid, taskid): # pylint: disable=arguments-differ
data["network_grading"] = "network_grading" in data
# Checking problem inputs
- print(problems)
for problem, values in problems.items():
- try:
- offset = int(values["offset"])
- if offset < 1:
+ if len(values["offset"]) != 0:
+ try:
+ offset = int(values["offset"])
+ if offset < 1:
+ return json.dumps(
+ {"status": "error", "message": _("The line offset for a problem must be positive!")})
+ except ValueError :
return json.dumps(
- {"status": "error", "message": _("The line offset for a problem must be positive!")})
- except ValueError :
- return json.dumps(
- {"status": "error", "message": _("The line offset for a problem must be an integer!")})
+ {"status": "error", "message": _("The line offset for a problem must be an integer!")})
except Exception as message:
return json.dumps({"status": "error", "message": _("Your browser returned an invalid form ({})").format(message)})
From 59596f83164cea117e89461009c0a8996d98d75e Mon Sep 17 00:00:00 2001
From: AlexandreDoneux <94830560+AlexandreDoneux@users.noreply.github.com>
Date: Thu, 19 Oct 2023 12:03:22 +0200
Subject: [PATCH 08/12] [Fronted/task_edit] Raising error in
CodeProblem.parse_problem() instead of task_edit.py
---
inginious/common/tasks_problems.py | 8 ++++++++
inginious/frontend/pages/course_admin/task_edit.py | 12 ------------
2 files changed, 8 insertions(+), 12 deletions(-)
diff --git a/inginious/common/tasks_problems.py b/inginious/common/tasks_problems.py
index ad451f221..8a4d18ba2 100644
--- a/inginious/common/tasks_problems.py
+++ b/inginious/common/tasks_problems.py
@@ -181,6 +181,14 @@ def input_is_consistent(self, task_input, default_allowed_extension, default_max
@classmethod
def parse_problem(self, problem_content):
+ # Checking problem edit inputs
+ if len(problem_content["offset"]) != 0:
+ try:
+ offset = int(problem_content["offset"])
+ if offset < 1:
+ raise Exception("Line offset must be positive!")
+ except ValueError:
+ raise Exception("Line offset must be an integer!")
return Problem.parse_problem(problem_content)
@classmethod
diff --git a/inginious/frontend/pages/course_admin/task_edit.py b/inginious/frontend/pages/course_admin/task_edit.py
index d96bfc44a..032b78a90 100644
--- a/inginious/frontend/pages/course_admin/task_edit.py
+++ b/inginious/frontend/pages/course_admin/task_edit.py
@@ -89,7 +89,6 @@ def POST_AUTH(self, courseid, taskid): # pylint: disable=arguments-differ
__, __ = self.get_course_and_check_rights(courseid, allow_all_staff=False)
data = flask.request.form.copy()
data["task_file"] = flask.request.files.get("task_file")
- print(data)
# Else, parse content
try:
@@ -144,17 +143,6 @@ def POST_AUTH(self, courseid, taskid): # pylint: disable=arguments-differ
# Network grading
data["network_grading"] = "network_grading" in data
- # Checking problem inputs
- for problem, values in problems.items():
- if len(values["offset"]) != 0:
- try:
- offset = int(values["offset"])
- if offset < 1:
- return json.dumps(
- {"status": "error", "message": _("The line offset for a problem must be positive!")})
- except ValueError :
- return json.dumps(
- {"status": "error", "message": _("The line offset for a problem must be an integer!")})
except Exception as message:
return json.dumps({"status": "error", "message": _("Your browser returned an invalid form ({})").format(message)})
From 692ab98a0b9a69e26f9cb4d954cd574a5a5a88c1 Mon Sep 17 00:00:00 2001
From: AlexandreDoneux <94830560+AlexandreDoneux@users.noreply.github.com>
Date: Thu, 19 Oct 2023 12:06:14 +0200
Subject: [PATCH 09/12] [static/common] Replacing parameter check in
registerCodeEditor() with default parameter
---
inginious/frontend/static/js/common.js | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/inginious/frontend/static/js/common.js b/inginious/frontend/static/js/common.js
index 44a66975b..c88b74582 100644
--- a/inginious/frontend/static/js/common.js
+++ b/inginious/frontend/static/js/common.js
@@ -69,17 +69,13 @@ function colorizeStaticCode()
}
//Register and init a code editor (ace)
-function registerCodeEditor(textarea, lang, lines, firstline)
+function registerCodeEditor(textarea, lang, lines, firstline=1)
{
var mode = CodeMirror.findModeByName(lang);
if(mode == undefined)
mode = {"mode": "plain", "mime": "text/plain"};
var is_single = $(textarea).hasClass('single');
- // if firstline null or undefined, set to "1"
- firstline = parseInt(firstline)?? "1";
- if (isNaN(firstline))
- firstline = 1;
From 8ed921dec1c84d5e10632e0eb9e7cca64f579a44 Mon Sep 17 00:00:00 2001
From: AlexandreDoneux <94830560+AlexandreDoneux@users.noreply.github.com>
Date: Fri, 20 Oct 2023 09:50:36 +0200
Subject: [PATCH 10/12] [common/tasks_problems] storing offset as integer in
task descriptor
---
inginious/common/tasks_problems.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/inginious/common/tasks_problems.py b/inginious/common/tasks_problems.py
index 8a4d18ba2..e41d0330f 100644
--- a/inginious/common/tasks_problems.py
+++ b/inginious/common/tasks_problems.py
@@ -187,6 +187,7 @@ def parse_problem(self, problem_content):
offset = int(problem_content["offset"])
if offset < 1:
raise Exception("Line offset must be positive!")
+ problem_content["offset"] = offset
except ValueError:
raise Exception("Line offset must be an integer!")
return Problem.parse_problem(problem_content)
From b35cb775bff9949b795f8368f935185655663088 Mon Sep 17 00:00:00 2001
From: Alexandre Doneux <94830560+AlexandreDoneux@users.noreply.github.com>
Date: Thu, 7 Dec 2023 13:08:48 +0100
Subject: [PATCH 11/12] [common/tasks_problems] Fixing empty input leading to
code block starting at 0
---
inginious/common/tasks_problems.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/inginious/common/tasks_problems.py b/inginious/common/tasks_problems.py
index e41d0330f..c9935240d 100644
--- a/inginious/common/tasks_problems.py
+++ b/inginious/common/tasks_problems.py
@@ -182,7 +182,9 @@ def input_is_consistent(self, task_input, default_allowed_extension, default_max
@classmethod
def parse_problem(self, problem_content):
# Checking problem edit inputs
- if len(problem_content["offset"]) != 0:
+ if len(problem_content["offset"]) == 0:
+ problem_content["offset"] = 1
+ else:
try:
offset = int(problem_content["offset"])
if offset < 1:
@@ -190,6 +192,7 @@ def parse_problem(self, problem_content):
problem_content["offset"] = offset
except ValueError:
raise Exception("Line offset must be an integer!")
+
return Problem.parse_problem(problem_content)
@classmethod
From 207af6ff26036013ac5f57906ca387e9a960188a Mon Sep 17 00:00:00 2001
From: AlexandreDoneux <94830560+AlexandreDoneux@users.noreply.github.com>
Date: Thu, 14 Dec 2023 10:31:19 +0100
Subject: [PATCH 12/12] [common/tasks_problems] not storing offset when no
offset given
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Instead of setting it to 1 (default value) we do not store it
Co-authored-by: Anthony Gégo
---
inginious/common/tasks_problems.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/inginious/common/tasks_problems.py b/inginious/common/tasks_problems.py
index c9935240d..ca0ae88a8 100644
--- a/inginious/common/tasks_problems.py
+++ b/inginious/common/tasks_problems.py
@@ -183,7 +183,7 @@ def input_is_consistent(self, task_input, default_allowed_extension, default_max
def parse_problem(self, problem_content):
# Checking problem edit inputs
if len(problem_content["offset"]) == 0:
- problem_content["offset"] = 1
+ del problem_content["offset"]
else:
try:
offset = int(problem_content["offset"])