Skip to content

Commit 93926d5

Browse files
committed
Update markus metadata validation and success message
1 parent 555b031 commit 93926d5

File tree

1 file changed

+78
-37
lines changed

1 file changed

+78
-37
lines changed

markus-jupyter-extension/static/extension.js

Lines changed: 78 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ define(["require", "base/js/namespace", "base/js/dialog"], function (
3333
logo.width = "16";
3434
new_group.firstChild.prepend(logo);
3535
}
36-
3736
/**
3837
* Submit the currently open file to MarkUs. Relies on the following notebook metadata:
3938
* "markus": {
@@ -44,22 +43,28 @@ define(["require", "base/js/namespace", "base/js/dialog"], function (
4443
*/
4544
function submitToMarkus() {
4645
// Construct URL from MarkUs metadata
47-
const markus_metadata = Jupyter.notebook.metadata.markus;
48-
let submitUrl;
46+
const metadata = Jupyter.notebook.metadata;
4947
try {
50-
submitUrl = getSubmitUrl(markus_metadata);
48+
validateMetadata(metadata);
5149
} catch (e) {
5250
report_error(e);
5351
return;
5452
}
5553

54+
const markusMetadata = metadata.markus;
55+
const submitUrl = getSubmitUrl(markusMetadata);
56+
5657
// Get API key from file
57-
const api_key_path = markus_metadata.api_key_path || DEFAULT_API_KEY_PATH;
58+
const api_key_path = markusMetadata.api_key_path || DEFAULT_API_KEY_PATH;
5859

59-
fetchApiKey(api_key_path).then(
60-
(apiKey) => submitFile(submitUrl, apiKey),
61-
report_error
62-
);
60+
fetchApiKey(api_key_path)
61+
.then((apiKey) => {
62+
return submitFile(submitUrl, apiKey);
63+
}, report_error)
64+
.then(
65+
(response) => handleMarkUsResponse(response, markusMetadata),
66+
(err) => handleNetworkError(submitUrl, err)
67+
);
6368
}
6469

6570
/**
@@ -108,17 +113,51 @@ define(["require", "base/js/namespace", "base/js/dialog"], function (
108113

109114
/**
110115
* Constructs the MarkUs URL to submit the notebook file to.
111-
* Raises an error if URL cannot be constructed from the metadata.
116+
* This assumes the metadata has already been validated.
112117
*
113118
* @param {object} markusMetadata
114119
* @returns {URL} the URL to submit to
115120
*/
116121
function getSubmitUrl(markusMetadata) {
117-
if (markusMetadata === undefined) {
122+
let { url, course_id, assessment_id } = markusMetadata;
123+
return new URL(
124+
url +
125+
"/api/courses/" +
126+
course_id +
127+
"/assignments/" +
128+
assessment_id +
129+
"/submit_file"
130+
);
131+
}
132+
133+
/**
134+
* Constructs the MarkUs URL to view an assessment (as a student).
135+
* This assumes the metadata has already been validated.
136+
*
137+
* @param {object} markusMetadata
138+
* @returns {URL} the assessment URL
139+
*/
140+
function getAssessmentUrl(markusMetadata) {
141+
let { url, course_id, assessment_id } = markusMetadata;
142+
return new URL(
143+
url + "/courses/" + course_id + "/assignments/" + assessment_id
144+
);
145+
}
146+
147+
/**
148+
* Validate whether the given notebook metadata has a valid MarkUs configuration.
149+
* A successful return means the notebook metadata has a valid configuration.
150+
* An error is raised if the configuration is invalid.
151+
*
152+
* @param {object} metadata
153+
* @returns {null}
154+
*/
155+
function validateMetadata(metadata) {
156+
if (metadata["markus"] === undefined) {
118157
throw 'Notebook metadata is missing the "markus" key.';
119158
}
120159

121-
let { url, course_id, assessment_id } = markusMetadata;
160+
let { url, course_id, assessment_id } = metadata["markus"];
122161

123162
if (!url || !course_id || !assessment_id) {
124163
throw 'Notebook metadata is missing one or more of the following keys under "markus": "url", "course_id", or "assessment_id".';
@@ -131,28 +170,11 @@ define(["require", "base/js/namespace", "base/js/dialog"], function (
131170
throw 'Notebook metadata "course_id" and "assessment_id" values must be numbers.';
132171
}
133172

134-
let submitUrl;
135173
try {
136-
submitUrl = new URL(
137-
url +
138-
"/api/courses/" +
139-
course_id +
140-
"/assignments/" +
141-
assessment_id +
142-
"/submit_file"
143-
);
174+
new URL(url);
144175
} catch {
145-
throw (
146-
"Notebook metatdata did not specify a valid MarkUs URL. Parameters: " +
147-
JSON.stringify({
148-
url: url,
149-
course_id: course_id,
150-
assessment_id: assessment_id,
151-
})
152-
);
176+
throw 'Notebook metatdata "url" value did not specify a valid URL.';
153177
}
154-
155-
return submitUrl;
156178
}
157179

158180
/**
@@ -174,22 +196,24 @@ define(["require", "base/js/namespace", "base/js/dialog"], function (
174196
console.info(
175197
`markus-jupyter-extension: Submitting file ${filename} to ${submitUrl}`
176198
);
177-
fetch(submitUrl, {
199+
200+
return fetch(submitUrl, {
178201
method: "POST",
179202
headers: {
180203
AUTHORIZATION: "MarkUsAuth " + key,
181204
Accept: "application/json",
182205
},
183206
body: formData,
184-
}).then(handleMarkUsResponse, (err) => handleNetworkError(submitUrl, err));
207+
});
185208
}
186209

187210
/**
188211
* Handle MarkUs response after submitting file.
189212
* @param {*} response
213+
* @param {object} MarkUs metadata
190214
* @returns
191215
*/
192-
function handleMarkUsResponse(response) {
216+
function handleMarkUsResponse(response, metadata) {
193217
if (!response.ok) {
194218
response.json().then((body) => {
195219
report_error(
@@ -200,10 +224,27 @@ define(["require", "base/js/namespace", "base/js/dialog"], function (
200224
return;
201225
}
202226

227+
const assessmentUrl = getAssessmentUrl(metadata);
228+
const assessmentLink = document.createElement("a");
229+
assessmentLink.setAttribute("href", assessmentUrl);
230+
assessmentLink.setAttribute("target", "_blank");
231+
assessmentLink.setAttribute("rel", "noopener noreferrer");
232+
assessmentLink.innerText = assessmentUrl;
233+
const body = document.createElement("p");
234+
body.appendChild(
235+
document.createTextNode("Your file has been submitted! Go to ")
236+
);
237+
body.appendChild(assessmentLink);
238+
body.appendChild(
239+
document.createTextNode(
240+
" to view your submission. Please check your assessment due date carefully, as only files submitted before the due date will be graded."
241+
)
242+
);
243+
203244
// Success dialog
204245
dialog.modal({
205-
title: "Submit to MarkUs",
206-
body: "Your file was submitted!",
246+
title: SUBMIT_LABEL,
247+
body: body,
207248
default_button: "Close",
208249
buttons: {
209250
Close: {},
@@ -233,7 +274,7 @@ define(["require", "base/js/namespace", "base/js/dialog"], function (
233274
function report_error(msg) {
234275
console.error(msg);
235276
dialog.modal({
236-
title: "Submit to MarkUs",
277+
title: SUBMIT_LABEL,
237278
body: "[ERROR] Could not submit file to MarkUs. Cause: " + msg,
238279
default_button: "Close",
239280
buttons: {

0 commit comments

Comments
 (0)