Skip to content

Commit a8766ec

Browse files
committed
Storage cleanup
1 parent 95e946c commit a8766ec

14 files changed

+1133
-657
lines changed

app/controllers/Assignment.java

+19-67
Original file line numberDiff line numberDiff line change
@@ -8,55 +8,10 @@
88
99
The "problem key" is normally the problem URL. However, for interactive or CodeCheck
1010
problems in the textbook repo, it is the qid of the single question in the problem.
11-
12-
CodeCheckWork is a map from problem keys to scores and states. It only stores the most recent version.
13-
CodeCheckSubmissions is an append-only log of all submissions of a single problem.
14-
15-
Tables:
16-
17-
CodeCheckAssignment
18-
assignmentID [primary key]
19-
deadline (an ISO 8601 string like "2020-12-01T23:59:59Z")
20-
editKey // LTI: tool consumer ID + user ID
21-
problems
22-
array of // One per group
23-
array of { URL, qid?, weight } // qid for book repo
24-
25-
CodeCheckLTIResources (Legacy)
26-
resourceID [primary key] // LTI tool consumer ID + course ID + resource ID
27-
assignmentID
28-
29-
CodeCheckWork
30-
assignmentID [partition key] // non-LTI: courseID? + assignmentID, LTI: toolConsumerID/courseID + assignment ID, Legacy tool consumer ID/course ID/resource ID
31-
workID [sort key] // non-LTI: ccid/editKey, LTI: userID
32-
problems
33-
map from URL/qids to { state, score }
34-
submittedAt
35-
tab
36-
37-
CodeCheckSubmissions
38-
submissionID [partition key] // non-LTI: courseID? + assignmentID + problemKey + ccid/editKey , LTI: toolConsumerID/courseID + assignmentID + problemID + userID
39-
// either way, that's resource ID + workID + problem key
40-
submittedAt [sort key]
41-
state: as string, not JSON
42-
score
43-
44-
with global secondary index (TODO: Not currently)
45-
problemID
46-
submitterID
47-
48-
CodeCheckLTICredentials
49-
oauth_consumer_key [primary key]
50-
shared_secret
5111
52-
CodeCheckComments
53-
assignmentID [partition key] // non-LTI: courseID? + assignmentID, LTI: toolConsumerID/courseID + assignment ID, Legacy tool consumer ID/course ID/resource ID
54-
workID [sort key] // non-LTI: ccid/editKey, LTI: userID
55-
comment
56-
57-
(This is a separate table from CodeCheckWork because we can't guarantee atomic updates if a student happens to update their work while the instructor updates a comment)
58-
59-
Assignment parsing format:
12+
assignmentID // non-LTI: courseID? + assignmentID, LTI: toolConsumerID/courseID + assignment ID, Legacy tool consumer ID/course ID/resource ID
13+
14+
Assignment parsing format:
6015
6116
Groups separated by 3 or more -
6217
Each line:
@@ -87,14 +42,14 @@ with global secondary index (TODO: Not currently)
8742
import com.fasterxml.jackson.databind.node.ObjectNode;
8843
import com.horstmann.codecheck.Util;
8944

90-
import models.AssignmentConnector;
45+
import models.StorageConnector;
9146
import play.Logger;
9247
import play.mvc.Controller;
9348
import play.mvc.Http;
9449
import play.mvc.Result;
9550

9651
public class Assignment extends Controller {
97-
@Inject private AssignmentConnector assignmentConn;
52+
@Inject private StorageConnector storageConn;
9853
private static Logger.ALogger logger = Logger.of("com.horstmann.codecheck");
9954

10055
public static ArrayNode parseAssignment(String assignment) {
@@ -205,7 +160,7 @@ public Result edit(Http.Request request, String assignmentID, String editKey) th
205160
if (assignmentID == null) {
206161
assignmentNode = JsonNodeFactory.instance.objectNode();
207162
} else {
208-
assignmentNode = assignmentConn.readJsonObjectFromDB("CodeCheckAssignments", "assignmentID", assignmentID);
163+
assignmentNode = storageConn.readAssignment(assignmentID);
209164
if (assignmentNode == null) return badRequest("Assignment not found");
210165

211166
if (editKey == null) { // Clone
@@ -237,7 +192,7 @@ public Result work(Http.Request request, String assignmentID, String ccid, Strin
237192
String workID = "";
238193
boolean editKeySaved = true;
239194

240-
ObjectNode assignmentNode = assignmentConn.readJsonObjectFromDB("CodeCheckAssignments", "assignmentID", assignmentID);
195+
ObjectNode assignmentNode = storageConn.readAssignment(assignmentID);
241196
if (assignmentNode == null) return badRequest("Assignment not found");
242197

243198
assignmentNode.put("isStudent", isStudent);
@@ -283,18 +238,14 @@ public Result work(Http.Request request, String assignmentID, String ccid, Strin
283238
// Start reading work and comments
284239
String work = null;
285240
ObjectNode commentObject = null;
286-
String comment = null;
241+
String comment = "";
287242
if (!workID.equals("")) {
288-
work = assignmentConn.readJsonStringFromDB("CodeCheckWork", "assignmentID", assignmentID, "workID", workID);
289-
commentObject = assignmentConn.readJsonObjectFromDB("CodeCheckComments", "assignmentID", assignmentID, "workID", workID);
243+
work = storageConn.readWorkString(assignmentID, workID);
244+
comment = storageConn.readComment(assignmentID, workID);
290245
}
291246
if (work == null)
292247
work = "{ assignmentID: \"" + assignmentID + "\", workID: \""
293248
+ workID + "\", problems: {} }";
294-
if (commentObject == null)
295-
comment = "";
296-
else
297-
comment = commentObject.get("comment").asText();
298249
assignmentNode.put("comment", comment);
299250

300251
String lti = "undefined";
@@ -330,15 +281,15 @@ public Result work(Http.Request request, String assignmentID, String ccid, Strin
330281

331282
public Result viewSubmissions(Http.Request request, String assignmentID, String editKey)
332283
throws IOException {
333-
ObjectNode assignmentNode = assignmentConn.readJsonObjectFromDB("CodeCheckAssignments", "assignmentID", assignmentID);
284+
ObjectNode assignmentNode = storageConn.readAssignment(assignmentID);
334285
if (assignmentNode == null) return badRequest("Assignment not found");
335286

336287
if (!editKeyValid(editKey, assignmentNode))
337288
throw new IllegalArgumentException("Edit key does not match");
338289

339290
ArrayNode submissions = JsonNodeFactory.instance.arrayNode();
340291

341-
Map<String, ObjectNode> itemMap = assignmentConn.readJsonObjectsFromDB("CodeCheckWork", "assignmentID", assignmentID, "workID");
292+
Map<String, ObjectNode> itemMap = storageConn.readAllWork(assignmentID);
342293

343294
for (String submissionKey : itemMap.keySet()) {
344295
String[] parts = submissionKey.split("/");
@@ -374,7 +325,7 @@ public Result saveAssignment(Http.Request request) throws IOException {
374325
ObjectNode assignmentNode;
375326
if (params.has("assignmentID")) {
376327
assignmentID = params.get("assignmentID").asText();
377-
assignmentNode = assignmentConn.readJsonObjectFromDB("CodeCheckAssignments", "assignmentID", assignmentID);
328+
assignmentNode = storageConn.readAssignment(assignmentID);
378329
if (assignmentNode == null) return badRequest("Assignment not found");
379330

380331
if (!params.has("editKey")) return badRequest("Missing edit key");
@@ -393,7 +344,7 @@ public Result saveAssignment(Http.Request request) throws IOException {
393344
}
394345
assignmentNode = null;
395346
}
396-
assignmentConn.writeJsonObjectToDB("CodeCheckAssignments", params);
347+
storageConn.writeAssignment(params);
397348

398349
String prefix = models.Util.prefix(request);
399350
String assignmentURL = prefix + "/private/assignment/" + assignmentID + "/" + editKey;
@@ -409,7 +360,7 @@ public Result saveWork(Http.Request request) throws IOException, NoSuchAlgorithm
409360

410361
Instant now = Instant.now();
411362
String assignmentID = requestNode.get("assignmentID").asText();
412-
ObjectNode assignmentNode = assignmentConn.readJsonObjectFromDB("CodeCheckAssignments", "assignmentID", assignmentID);
363+
ObjectNode assignmentNode = storageConn.readAssignment(assignmentID);
413364
if (assignmentNode == null) return badRequest("Assignment not found");
414365
String workID = requestNode.get("workID").asText();
415366
String problemID = requestNode.get("tab").asText();
@@ -422,7 +373,7 @@ public Result saveWork(Http.Request request) throws IOException, NoSuchAlgorithm
422373
// TODO: NPE in logs for the line below
423374
submissionNode.put("state", problemsNode.get(problemID).get("state").toString());
424375
submissionNode.put("score", problemsNode.get(problemID).get("score").asDouble());
425-
assignmentConn.writeJsonObjectToDB("CodeCheckSubmissions", submissionNode);
376+
storageConn.writeSubmission(submissionNode);
426377

427378
if (assignmentNode.has("deadline")) {
428379
try {
@@ -435,7 +386,8 @@ public Result saveWork(Http.Request request) throws IOException, NoSuchAlgorithm
435386
}
436387
result.put("submittedAt", now.toString());
437388

438-
assignmentConn.writeNewerJsonObjectToDB("CodeCheckWork", requestNode, "assignmentID", "submittedAt");
389+
storageConn.writeWork(requestNode);
390+
439391
return ok(result);
440392
} catch (Exception e) {
441393
logger.error(Util.getStackTrace(e));
@@ -455,7 +407,7 @@ public Result saveComment(Http.Request request) throws IOException {
455407
commentNode.put("assignmentID", assignmentID);
456408
commentNode.put("workID", workID);
457409
commentNode.put("comment", comment);
458-
assignmentConn.writeJsonObjectToDB("CodeCheckComments", commentNode);
410+
storageConn.writeComment(commentNode);
459411
result.put("comment", comment);
460412
result.put("refreshURL", "/private/submission/" + assignmentID + "/" + workID);
461413
return ok(result);

app/controllers/LTIAssignment.java

+21-21
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
import models.JWT;
2323
import models.LTI;
24-
import models.AssignmentConnector;
24+
import models.StorageConnector;
2525
import oauth.signpost.exception.OAuthCommunicationException;
2626
import oauth.signpost.exception.OAuthExpectationFailedException;
2727
import oauth.signpost.exception.OAuthMessageSignerException;
@@ -42,7 +42,7 @@ Session cookie (LTI Instructors only)
4242

4343

4444
public class LTIAssignment extends Controller {
45-
@Inject private AssignmentConnector assignmentConn;
45+
@Inject private StorageConnector assignmentConn;
4646
@Inject private LTI lti;
4747
@Inject private JWT jwt;
4848
private static Logger.ALogger logger = Logger.of("com.horstmann.codecheck");
@@ -63,9 +63,7 @@ private String assignmentOfResource(String resourceID) throws IOException {
6363
int i = resourceID.lastIndexOf(" ");
6464
return resourceID.substring(i + 1);
6565
} else {
66-
ObjectNode resourceNode = assignmentConn.readJsonObjectFromDB("CodeCheckLTIResources", "resourceID", resourceID);
67-
if (resourceNode == null) return null;
68-
return resourceNode.get("assignmentID").asText();
66+
return assignmentConn.readLegacyLTIResource(resourceID);
6967
}
7068
}
7169

@@ -117,13 +115,13 @@ public Result saveAssignment(Http.Request request) throws IOException {
117115
}
118116

119117
assignmentID = params.get("assignmentID").asText();
120-
ObjectNode assignmentNode = assignmentConn.readJsonObjectFromDB("CodeCheckAssignments", "assignmentID", assignmentID);
118+
ObjectNode assignmentNode = assignmentConn.readAssignment(assignmentID);
121119
String editKey = params.get("editKey").asText();
122120

123121
if (assignmentNode != null && !editKey.equals(assignmentNode.get("editKey").asText()))
124122
return badRequest("Edit keys do not match");
125123

126-
assignmentConn.writeJsonObjectToDB("CodeCheckAssignments", params);
124+
assignmentConn.writeAssignment(params);
127125
}
128126

129127
ObjectNode result = JsonNodeFactory.instance.objectNode();
@@ -138,10 +136,10 @@ public Result saveAssignment(Http.Request request) throws IOException {
138136
@Security.Authenticated(Secured.class) // Instructor
139137
public Result viewSubmissions(Http.Request request) throws IOException {
140138
String resourceID = request.queryString("resourceID").orElse(null);
141-
Map<String, ObjectNode> itemMap = assignmentConn.readJsonObjectsFromDB("CodeCheckWork", "assignmentID", resourceID, "workID");
139+
Map<String, ObjectNode> itemMap = assignmentConn.readAllWork(resourceID);
142140
String assignmentID = assignmentOfResource(resourceID);
143141

144-
ObjectNode assignmentNode = assignmentConn.readJsonObjectFromDB("CodeCheckAssignments", "assignmentID", assignmentID);
142+
ObjectNode assignmentNode = assignmentConn.readAssignment(assignmentID);
145143
if (assignmentNode == null) return badRequest("Assignment not found");
146144

147145
ArrayNode submissions = JsonNodeFactory.instance.arrayNode();
@@ -164,10 +162,10 @@ public Result viewSubmissions(Http.Request request) throws IOException {
164162
public Result viewSubmission(Http.Request request) throws IOException {
165163
String resourceID = request.queryString("resourceID").orElse(null);
166164
String workID = request.queryString("workID").orElse(null);
167-
String work = assignmentConn.readJsonStringFromDB("CodeCheckWork", "assignmentID", resourceID, "workID", workID);
165+
String work = assignmentConn.readWorkString(resourceID, workID);
168166
if (work == null) return badRequest("Work not found");
169167
String assignmentID = assignmentOfResource(resourceID);
170-
ObjectNode assignmentNode = assignmentConn.readJsonObjectFromDB("CodeCheckAssignments", "assignmentID", assignmentID);
168+
ObjectNode assignmentNode = assignmentConn.readAssignment(assignmentID);
171169
if (assignmentNode == null) return badRequest("Assignment not found");
172170
ArrayNode groups = (ArrayNode) assignmentNode.get("problems");
173171
assignmentNode.set("problems", groups.get(Math.abs(workID.hashCode()) % groups.size()));
@@ -178,7 +176,7 @@ public Result viewSubmission(Http.Request request) throws IOException {
178176
@Security.Authenticated(Secured.class) // Instructor
179177
public Result editAssignment(Http.Request request, String assignmentID) throws IOException {
180178
String editKey = request.session().get("user").get(); // TODO orElseThrow();
181-
ObjectNode assignmentNode = assignmentConn.readJsonObjectFromDB("CodeCheckAssignments", "assignmentID", assignmentID);
179+
ObjectNode assignmentNode = assignmentConn.readAssignment(assignmentID);
182180
if (assignmentNode == null) return badRequest("Assignment not found");
183181

184182
if (!editKey.equals(assignmentNode.get("editKey").asText()))
@@ -207,7 +205,7 @@ private static ObjectNode bridgeAssignment(String url) {
207205

208206
private ObjectNode getAssignmentNode(String assignmentID) throws IOException {
209207
if (isBridgeAssignment.matcher(assignmentID).matches()) return bridgeAssignment(assignmentID);
210-
else return assignmentConn.readJsonObjectFromDB("CodeCheckAssignments", "assignmentID", assignmentID);
208+
else return assignmentConn.readAssignment(assignmentID);
211209
}
212210

213211
public Result launch(Http.Request request, String assignmentID) throws IOException {
@@ -233,9 +231,11 @@ public Result launch(Http.Request request, String assignmentID) throws IOExcepti
233231
ObjectNode ltiNode = JsonNodeFactory.instance.objectNode();
234232
// TODO: In order to facilitate search by assignmentID, it would be better if this was the other way around
235233
String resourceID = toolConsumerID + "/" + contextID + " " + assignmentID;
234+
235+
// TODO: When can we drop this?
236236
String legacyResourceID = toolConsumerID + "/" + contextID + "/" + resourceLinkID;
237-
ObjectNode resourceNode = assignmentConn.readJsonObjectFromDB("CodeCheckLTIResources", "resourceID", legacyResourceID);
238-
if (resourceNode != null) resourceID = legacyResourceID;
237+
String legacy = assignmentConn.readLegacyLTIResource(legacyResourceID);
238+
if (legacy != null) resourceID = legacyResourceID;
239239

240240
if (assignmentID == null) {
241241
return badRequest("No assignment ID");
@@ -281,7 +281,7 @@ public Result launch(Http.Request request, String assignmentID) throws IOExcepti
281281
ltiNode.put("oauthConsumerKey", oauthConsumerKey);
282282
ltiNode.put("jwt", jwt.generate(Map.of("resourceID", resourceID, "userID", userID)));
283283

284-
ObjectNode workNode = assignmentConn.readJsonObjectFromDB("CodeCheckWork", "assignmentID", resourceID, "workID", userID);
284+
ObjectNode workNode = assignmentConn.readWork(resourceID, userID);
285285
String work = "";
286286
if (workNode == null)
287287
work = "{ problems: {} }";
@@ -304,10 +304,10 @@ public Result launch(Http.Request request, String assignmentID) throws IOExcepti
304304
public Result allSubmissions(Http.Request request) throws IOException {
305305
String resourceID = request.queryString("resourceID").orElse(null);
306306
if (resourceID == null) return badRequest("Assignment not found");
307-
Map<String, ObjectNode> itemMap = assignmentConn.readJsonObjectsFromDB("CodeCheckWork", "assignmentID", resourceID, "workID");
307+
Map<String, ObjectNode> itemMap = assignmentConn.readAllWork(resourceID);
308308
String assignmentID = assignmentOfResource(resourceID);
309309

310-
ObjectNode assignmentNode = assignmentConn.readJsonObjectFromDB("CodeCheckAssignments", "assignmentID", assignmentID);
310+
ObjectNode assignmentNode = assignmentConn.readAssignment(assignmentID);
311311
if (assignmentNode == null) return badRequest("Assignment not found");
312312

313313
ObjectNode submissions = JsonNodeFactory.instance.objectNode();
@@ -342,7 +342,7 @@ public Result saveWork(Http.Request request) throws IOException, NoSuchAlgorithm
342342
submissionNode.put("submittedAt", now.toString());
343343
submissionNode.put("state", problemsNode.get(problemID).get("state").toString());
344344
submissionNode.put("score", problemsNode.get(problemID).get("score").asDouble());
345-
assignmentConn.writeJsonObjectToDB("CodeCheckSubmissions", submissionNode);
345+
assignmentConn.writeSubmission(submissionNode);
346346

347347
ObjectNode assignmentNode = getAssignmentNode(assignmentID);
348348
if (assignmentNode.has("deadline")) {
@@ -355,7 +355,7 @@ public Result saveWork(Http.Request request) throws IOException, NoSuchAlgorithm
355355
}
356356
}
357357
result.put("submittedAt", now.toString());
358-
if (assignmentConn.writeNewerJsonObjectToDB("CodeCheckWork", workNode, "assignmentID", "submittedAt")) {
358+
if (assignmentConn.writeWork(workNode)) {
359359
// Don't submit grade if this is an older submission
360360
submitGradeToLMS(requestNode, (ObjectNode) requestNode.get("work"), result);
361361
}
@@ -380,7 +380,7 @@ public Result sendScore(Http.Request request) throws IOException, NoSuchAlgorith
380380
String userID = claims.get("userID").toString();
381381
String resourceID = claims.get("resourceID").toString();
382382

383-
ObjectNode workNode = assignmentConn.readJsonObjectFromDB("CodeCheckWork", "assignmentID", resourceID, "workID", userID);
383+
ObjectNode workNode = assignmentConn.readWork(resourceID, userID);
384384
if (workNode == null) return badRequest("Work not found");
385385
submitGradeToLMS(requestNode, workNode, result);
386386
String outcome = result.get("outcome").asText();

0 commit comments

Comments
 (0)