Skip to content
This repository was archived by the owner on Jun 7, 2023. It is now read-only.

Commit ba5b95e

Browse files
authored
Merge pull request #1207 from bjones1/fixes
Fixes
2 parents 56856b2 + 52e52eb commit ba5b95e

File tree

6 files changed

+81
-14
lines changed

6 files changed

+81
-14
lines changed

runestone/clickableArea/js/clickable.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,9 +400,11 @@ export default class ClickableArea extends RunestoneBase {
400400
}
401401

402402
logCurrentAnswer() {
403+
const answer = this.givenIndexArray.join(";");
403404
this.logBookEvent({
404405
event: "clickableArea",
405-
act: this.givenIndexArray.join(";"),
406+
answer: answer,
407+
act: answer,
406408
div_id: this.divid,
407409
correct: this.correct ? "T" : "F",
408410
});

runestone/common/css/runestone-custom-sphinx-bootstrap.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -863,7 +863,7 @@ ul.dropdown-menu.globaltoc {
863863

864864

865865
/* Style lp textareas. */
866-
textarea#lp-result {
866+
textarea.lp-result {
867867
width: 100%;
868868
height: 10em;
869869
font-family: monospace;

runestone/fitb/js/fitb.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ export default class FITB extends RunestoneBase {
217217
correct: this.correct ? "T" : "F",
218218
div_id: this.divid,
219219
});
220+
data = data.detail;
220221
if (!this.feedbackArray) {
221222
// On success, update the feedback from the server's grade.
222223
this.setLocalStorage({

runestone/lp/js/lp.js

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ class LP extends RunestoneBase {
4848
this.containerDiv = this.element;
4949
this.divid = this.element.id;
5050
// Store the DOM element (the textarea) where compile results will be displayed.
51-
this.resultElement = $(this.element).siblings("textarea");
52-
// Store the DOM element (a span) where feedback will be displayed.
53-
this.feedbackElement = $(this.element).siblings("div");
51+
this.resultElement = $(this.element).siblings(".lp-result");
52+
// Store the DOM element (a div) where feedback will be displayed.
53+
this.feedbackElement = $(this.element).siblings(".lp-feedback").children("div");
5454
// Use a nice editor.
5555
let that = this;
5656
this.textAreas = [];
@@ -78,22 +78,72 @@ class LP extends RunestoneBase {
7878
this.checkServer("lp_build", true);
7979
}
8080

81+
// Data structures:
82+
//
83+
// Format of data stored locally and on the server*::
84+
//
85+
// - answer: JSON-encoded string containing {
86+
// code_snippets: [
87+
// str, snippet 1, ...
88+
// ],
89+
// (optional) resultString: str, output from build.
90+
// }
91+
// - correct: (optional) float, a percentage from 0 to 100.
92+
// - timestamp: date/time in UTC.
93+
//
94+
// Format of data sent to the server*::
95+
//
96+
// - answer: JSON-encoded string containing only the code_snippets
97+
// array, not resultString. There's no point in sending the
98+
// previous resultString, since the server will compute a new
99+
// one.
100+
// - event: "lp_build"
101+
// - act: "", since the useinfo table requires it. It's not
102+
// otherwise used.
103+
// - path: str, giving the relative path to this web page. Used
104+
// to find the source code which produced this page in order
105+
// to do snippet replacement.
106+
// - div_id: str, the div_id of this component.
107+
//
108+
// Format of data received from the server::
109+
//
110+
// If there was an error:
111+
// - errors: [
112+
// str, error message 1, ...
113+
// ]
114+
//
115+
// Otherwise:
116+
// - answer: JSON-encoded string containing {
117+
// resultString: str, output from build.
118+
// Note that the code_snippets aren't sent back, to save
119+
// bandwidth.
120+
// }
121+
// - correct: float, a percentage from 0 to 100.
122+
// - timestamp: str, the server's timestamp.
123+
//
124+
// * For simplicity, I omitted the common fields (course, etc.) and discussed only fields unique to this component.
125+
81126
async onSaveAndRun(_eventObject) {
127+
// Prevent multiple click while the build is running.
128+
$(this.element).attr("disabled", true);
82129
$(this.resultElement).val("Building...");
83130
$(this.feedbackElement).text("").attr("");
84131
// Since the Save and run button was clicked, we assume the code snippets have been changed; therefore, don't store ``correct`` or ``answer.resultString`` because they are out of date.
85-
let answer = { code_snippets: this.textareasToData() };
132+
//
133+
// Store the answer as a string, since this is what goes in to / comes out from the database. We have to translate this back to a data structure when restoring from the db or local storage.
134+
let code_snippets = this.textareasToData();
86135
this.setLocalStorage({
87-
answer: answer,
136+
answer: JSON.stringify({code_snippets: code_snippets}),
88137
timestamp: new Date(),
89138
});
90139
// Store the answer that the server returns, which includes additional data (correct/incorrect, feedback from the build, etc.).
91140
let serverAnswer;
92141
try {
93142
serverAnswer = await this.logBookEvent({
94143
event: "lp_build",
95-
// All values must be strings, or the resulting values on the server side come out confused.
96-
answer: JSON.stringify(answer),
144+
answer: JSON.stringify(code_snippets),
145+
// This is required by useinfo, but not used.
146+
act: "",
97147
// Find the relative path to this web page. Slice off the leading ``/``.
98148
path: window.location.href
99149
.replace(eBookConfig.app, "")
@@ -105,13 +155,19 @@ class LP extends RunestoneBase {
105155
.val(`Error contacting server: {err}.`)
106156
.attr("class", "alert alert-danger");
107157
return;
158+
} finally {
159+
// Always re-enable the button after the server responds.
160+
$(this.element).attr("disabled", false);
108161
}
162+
serverAnswer = serverAnswer.detail;
109163
// The server doesn't return the ``code_snippets``, for efficiency. Include those. If an error was returned, note that there is no ``answer`` yet.
110164
if (!("answer" in serverAnswer)) {
111-
serverAnswer["answer"] = {};
165+
serverAnswer.answer = {};
112166
}
113-
serverAnswer["answer"]["code_snippets"] = this.textareasToData();
167+
serverAnswer.answer.code_snippets = code_snippets;
114168
this.displayAnswer(serverAnswer);
169+
// JSON-encode the answer for storage.
170+
serverAnswer.answer = JSON.stringify(serverAnswer.answer);
115171
this.setLocalStorage(serverAnswer);
116172
}
117173

@@ -142,13 +198,15 @@ class LP extends RunestoneBase {
142198
$(this.resultElement).scrollTop(this.resultElement[0].scrollHeight);
143199
}
144200
}
201+
145202
// Store the contents of each textarea into an array of strings.
146203
textareasToData() {
147204
return $.map(this.textAreas, function (obj, index) {
148205
// See https://codemirror.net/doc/manual.html#api.
149206
return obj.getValue();
150207
});
151208
}
209+
152210
// Store an array of strings in ``data.code_snippets`` into each textarea.
153211
dataToTextareas(data) {
154212
// Find all code snippet textareas.
@@ -157,11 +215,15 @@ class LP extends RunestoneBase {
157215
value.setValue((data.answer.code_snippets || "")[index] || "");
158216
});
159217
}
218+
160219
// Restore answers from storage retrieval done in RunestoneBase.
161220
restoreAnswers(data) {
221+
// We store the answer as a JSON-encoded string in the db / local storage. Restore the actual data structure from it.
222+
data.answer = JSON.parse(data.answer);
162223
this.dataToTextareas(data);
163224
this.displayAnswer(data);
164225
}
226+
165227
checkLocalStorage() {
166228
// Loads previous answers from local storage if they exist.
167229
var storedData;
@@ -181,6 +243,7 @@ class LP extends RunestoneBase {
181243
}
182244
}
183245
}
246+
184247
setLocalStorage(data) {
185248
localStorage.setItem(this.localStorageKey(), JSON.stringify(data));
186249
}
@@ -192,7 +255,7 @@ class LP extends RunestoneBase {
192255
$(document).bind("runestone:login-complete", function () {
193256
$("[data-component=lp_build]").each(function (index) {
194257
try {
195-
LPList[this.id] = new LP({
258+
window.LPList[this.id] = new LP({
196259
orig: this,
197260
useRunestoneServices: eBookConfig.useRunestoneServices,
198261
});

runestone/lp/lp.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,9 @@ def depart_lp_node(self, node):
164164
{}
165165
<input type="button" value="Save and run" class="btn btn-success" data-component="lp_build" data-question_label="%(question_label)s" data-lang="{}" id="{}" />
166166
<br />
167-
<textarea readonly id="lp-result"></textarea>
167+
<textarea readonly class="lp-result"></textarea>
168168
<br />
169-
<div></div>
169+
<div class="lp-feedback"><div></div></div>
170170
</div>""".format(
171171
"".join(self.body), node.runestone_options["language"], id_
172172
)

runestone/shortanswer/js/shortanswer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ export default class ShortAnswer extends RunestoneBase {
152152
this.logBookEvent({
153153
event: "shortanswer",
154154
act: value,
155+
answer: value,
155156
div_id: this.divid,
156157
});
157158
}

0 commit comments

Comments
 (0)