Skip to content

Commit 70c3aa8

Browse files
committed
Use JSON responses with active elements
Also fixes an issue where the submission isn't visible if messages are present (e.g. "Staff can submit assignments without enrolling.")
1 parent e62f611 commit 70c3aa8

3 files changed

Lines changed: 50 additions & 23 deletions

File tree

exercise/static/exercise/chapter.js

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -453,14 +453,16 @@
453453
},
454454

455455
// Submit the formData to given url and then execute the callback.
456-
submitAjax: function(url, formData, callback) {
456+
submitAjax: function(url, formData, ajaxParams, callback) {
457+
ajaxParams = ajaxParams ?? {};
458+
ajaxParams.dataType = ajaxParams.dataType ?? "html";
457459
var exercise = this;
458460
$.ajax(url, {
459461
type: "POST",
460462
data: formData,
461463
contentType: false,
462464
processData: false,
463-
dataType: "html"
465+
...ajaxParams,
464466
}).fail(function(xhr, textStatus, errorThrown) {
465467
//$(form_element).find(":input").prop("disabled", false);
466468
//exercise.showLoader("error");
@@ -596,24 +598,24 @@
596598
out_content.html("<p>Evaluating</p>");
597599

598600
var url = exercise.url;
599-
exercise.submitAjax(url, formData, function(data) {
600-
const content = $(data);
601-
// Look for error alerts in the feedback, but skip the hidden element
602-
// that is always included: <div class="quiz-submit-error alert alert-danger hide">
601+
exercise.submitAjax(url, formData, {dataType: "json"}, function(data) {
602+
output.find(data.messages.selector).replaceWith(data.messages.html);
603+
604+
// Look for error alerts in the feedback
605+
const content = $(data.page.content);
603606
const alerts = content.find('.alert-danger:not(.hide)');
604-
if (!alerts.length) {
605-
var poll_url = content.find(".exercise-wait").attr("data-poll-url");
606-
output.attr('data-poll-url', poll_url);
607-
608-
exercise.updateSubmission(content);
609-
} else if (alerts.contents().text()
610-
.indexOf("The grading queue is not configured.") >= 0) {
611-
output.find(exercise.settings.ae_result_selector)
612-
.html(content.find(".alert:not(.hide)").text());
613-
output.find(exercise.settings.ae_result_selector).append(content.find(".grading-task").text());
607+
output.find(data.messages.selector).append(alerts);
608+
609+
if (data.submission) {
610+
output.attr('data-poll-url', data.submission.poll_url);
611+
output.attr('data-ready-url', data.submission.ready_url);
612+
613+
exercise.updateSubmission();
614614
} else {
615-
output.find(exercise.settings.ae_result_selector)
616-
.html(alerts.contents());
615+
out_content.html("<p>Failed to submit:</p>")
616+
for(const err of data.errors) {
617+
out_content.append("<p>" + err + "</p>")
618+
}
617619
}
618620
});
619621
});
@@ -636,7 +638,7 @@
636638
} else {
637639
formData.set('__aplus__', JSON.stringify({hash: hash}));
638640
}
639-
exercise.submitAjax(url, formData, function(data) {
641+
exercise.submitAjax(url, formData, {}, function(data) {
640642
//$(form_element).find(":input").prop("disabled", false);
641643
//exercise.hideLoader();
642644
var input = $(data);

exercise/views.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from course.models import CourseModule
1717
from course.viewbase import CourseInstanceBaseView, EnrollableViewMixin
1818
from lib.helpers import query_dict_to_list_of_tuples, safe_file_name
19+
from lib.json import json_response_with_messages
20+
from lib.mime_request import accepts_mimes, MIMERequest
1921
from lib.remote_page import RemotePageNotFound, request_for_response
2022
from lib.viewbase import BaseRedirectMixin, BaseView
2123
from userprofile.models import UserProfile
@@ -141,15 +143,16 @@ def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
141143
latest_enrollment_submission_data=all_enroll_data,
142144
**kwargs)
143145

144-
def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
146+
@accepts_mimes(["text/html", "application/json"])
147+
def post(self, request: MIMERequest, *args: Any, **kwargs: Any) -> HttpResponse:
145148
# Stop submit trials for e.g. chapters.
146149
# However, allow posts from exercises switched to maintenance status.
147150
if not self.exercise.is_submittable:
148151
return self.http_method_not_allowed(request, *args, **kwargs)
149152

150153
new_submission = None
151154
page = ExercisePage(self.exercise)
152-
_submission_status, submission_allowed, _issues, students = (
155+
_submission_status, submission_allowed, issues, students = (
153156
self.submission_check(True, request)
154157
)
155158
if submission_allowed:
@@ -212,7 +215,17 @@ def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
212215

213216
# Redirect non AJAX content page request back.
214217
if not request.is_ajax() and "__r" in request.GET:
215-
return self.redirect(request.GET["__r"], backup=self.exercise);
218+
return self.redirect(request.GET["__r"], backup=self.exercise)
219+
220+
if request.expected_mime == "application/json":
221+
return json_response_with_messages(
222+
request,
223+
{
224+
"page": page,
225+
"submission": new_submission,
226+
"errors": issues,
227+
}
228+
)
216229

217230
self.get_summary_submissions()
218231
return self.render_to_response(self.get_context_data(

lib/json.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,19 @@ class AJAXJSONEncoder(DjangoJSONEncoder):
1616
javascript, instead of just serializing the object.
1717
"""
1818
def default(self, obj: Any) -> Any:
19-
# Any custom types come here `if isinstance(obj, ...):`
19+
if isinstance(obj, Submission):
20+
return {
21+
"id": obj.id,
22+
"poll_url": obj.get_url("submission-poll"),
23+
"ready_url": obj.get_url(
24+
getattr(self, "submission_poll_ready_url_name", "submission")
25+
),
26+
}
27+
elif isinstance(obj, ExercisePage):
28+
return {
29+
"content": obj.content,
30+
"errors": obj.errors,
31+
}
2032

2133
return super().default(obj)
2234

0 commit comments

Comments
 (0)