From a618a87df68a0e10f7b042bce5aaaa56db6f10bf Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Thu, 17 Mar 2022 07:44:39 +0100 Subject: [PATCH 001/217] Deploy staging play-codecheck --- app/models/JWT.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/JWT.java b/app/models/JWT.java index 488dd128..9f7aaf49 100644 --- a/app/models/JWT.java +++ b/app/models/JWT.java @@ -1,5 +1,6 @@ package models; +import java.time.Duration; import java.time.Instant; import java.util.Date; import java.util.Map; @@ -24,7 +25,7 @@ public String generate(Map claims) { .setIssuer("codecheck.io") .addClaims(claims) .setIssuedAt(Date.from(Instant.now())) - .setExpiration(Date.from(Instant.now().plusSeconds(60 * 60 * 3))) + .setExpiration(Date.from(Instant.now().plus(Duration.ofDays(7)))) .signWith(SignatureAlgorithm.HS256, key) .compact(); return jwt; From f619ffea14141550b21a0fa56a9a4fe0bc2fd01f Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Thu, 17 Mar 2022 11:48:31 +0100 Subject: [PATCH 002/217] Deploy release play-codecheck --- app/com/horstmann/codecheck/HaskellLanguage.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app/com/horstmann/codecheck/HaskellLanguage.java b/app/com/horstmann/codecheck/HaskellLanguage.java index 9b126c5b..01a22bef 100644 --- a/app/com/horstmann/codecheck/HaskellLanguage.java +++ b/app/com/horstmann/codecheck/HaskellLanguage.java @@ -64,19 +64,20 @@ public String functionName(String declaration) { out.append(" args <- getArgs\n"); for (int k = 0; k < calls.size(); k++) { Calls.Call call = calls.get(k); - if (k == 0) out.append(" if (head args) == \"1\" then "); + if (k == 0 && calls.size() > 1) out.append(" if (head args) == \"1\" then "); else if (k < calls.size() - 1) out.append(" else if (head args) == \"" + (k + 1) + "\" then "); - else out.append(" else "); - out.append(moduleName + "." + call.name + " " + call.args + " `comp` CodeCheckSolution." - + call.name + " " + call.args + "\n"); + else if (calls.size() > 1) out.append(" else "); + else out.append(" "); + out.append("CodeCheckSolution." + call.name + " " + call.args + " `comp` " + + moduleName + "." + call.name + " " + call.args + "\n"); } out.append(" where\n"); out.append(" exec expr = (do x <- evaluate expr ; return $ Just x)\n"); out.append(" `catch` (\\(SomeException x) -> return Nothing)\n"); out.append(" comp expr1 expr2 = do\n"); - out.append(" actual <- exec expr1\n"); - out.append(" expected <- exec expr2\n"); - out.append(" case (actual, expected) of\n"); + out.append(" expected <- exec expr1\n"); + out.append(" actual <- exec expr2\n"); + out.append(" case (expected, actual) of\n"); out.append(" (Nothing, Nothing) -> putStrLn \"error\\nerror\\ntrue\"\n"); out.append(" (Just a, Just b) -> putStrLn$ (show a) ++ \"\\n\" ++ (show b) ++ (if a==b then \"\\ntrue\" else \"\\nfalse\")\n"); out.append(" (Just a, Nothing) -> putStrLn $ (show a) ++ \"\\nerror\\nfalse\"\n"); From 211836b56b4f9bb8556f167f2b487b5e4ab508be Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Mon, 21 Mar 2022 16:22:48 +0100 Subject: [PATCH 003/217] Deploy staging play-codecheck --- .../horstmann/codecheck/CompareImages.java | 21 ++++++++----- app/com/horstmann/codecheck/HTMLReport.java | 28 +++++++++-------- app/com/horstmann/codecheck/JSONReport.java | 20 ++++++++----- app/com/horstmann/codecheck/Main.java | 30 +++++++------------ app/com/horstmann/codecheck/Report.java | 4 +-- 5 files changed, 54 insertions(+), 49 deletions(-) diff --git a/app/com/horstmann/codecheck/CompareImages.java b/app/com/horstmann/codecheck/CompareImages.java index d68d3bc0..82ba396b 100644 --- a/app/com/horstmann/codecheck/CompareImages.java +++ b/app/com/horstmann/codecheck/CompareImages.java @@ -17,23 +17,28 @@ public static boolean isImage(String name) { return Arrays.asList(ImageIO.getReaderFileSuffixes()).contains(extension); } - public CompareImages(byte[] firstImage) throws IOException { - image1 = readImage(firstImage); + public CompareImages(byte[] firstImage) { + try { + image1 = readImage(firstImage); + } catch (IOException ex) { + image1 = null; + } } public void setOtherImage(byte[] p) throws IOException { - image2 = readImage(p); + try { + image2 = readImage(p); + } catch (IOException ex) { + image2 = null; + } } public BufferedImage first() { return image1; } public BufferedImage other() { return image2; } private static BufferedImage readImage(byte[] bytes) throws IOException { - try { - return ImageIO.read(new ByteArrayInputStream(bytes)); - } catch (Exception ex) { - throw new IOException("Image not readable"); - } + if (bytes == null) throw new IOException("null data"); + return ImageIO.read(new ByteArrayInputStream(bytes)); } public boolean getOutcome() { diff --git a/app/com/horstmann/codecheck/HTMLReport.java b/app/com/horstmann/codecheck/HTMLReport.java index 87501756..dc658397 100644 --- a/app/com/horstmann/codecheck/HTMLReport.java +++ b/app/com/horstmann/codecheck/HTMLReport.java @@ -166,7 +166,7 @@ public HTMLReport systemError(Throwable t) { * @see com.horstmann.codecheck.Report#image(java.lang.String, byte[]) */ @Override - public HTMLReport image(String captionText, BufferedImage img) throws IOException { + public HTMLReport image(String captionText, BufferedImage img) { if (img == null) return this; caption(captionText); @@ -180,19 +180,23 @@ public HTMLReport image(String captionText, BufferedImage img) throws IOExceptio * @see com.horstmann.codecheck.Report#image(byte[]) */ @Override - public HTMLReport image(BufferedImage img) throws IOException { + public HTMLReport image(BufferedImage img) { if (img == null) return this; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ImageIO.write(img, "PNG", out); - out.close(); - byte[] pngBytes = out.toByteArray(); - String data = Base64.getEncoder().encodeToString(pngBytes); - builder.append("

"); - builder.append("\"screen"); - builder.append("

\n"); + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ImageIO.write(img, "PNG", out); + out.close(); + byte[] pngBytes = out.toByteArray(); + String data = Base64.getEncoder().encodeToString(pngBytes); + builder.append("

"); + builder.append("\"screen"); + builder.append("

\n"); + } catch (IOException ex) { + builder.append("

Cannot display image

"); + } return this; } diff --git a/app/com/horstmann/codecheck/JSONReport.java b/app/com/horstmann/codecheck/JSONReport.java index 27b68da1..871878df 100644 --- a/app/com/horstmann/codecheck/JSONReport.java +++ b/app/com/horstmann/codecheck/JSONReport.java @@ -165,19 +165,23 @@ public JSONReport input(String input) { } @Override - public JSONReport image(String caption, BufferedImage image) throws IOException { + public JSONReport image(String caption, BufferedImage image) { if (image == null) return this; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ImageIO.write(image, "PNG", out); - out.close(); - byte[] pngBytes = out.toByteArray(); - String data = Base64.getEncoder().encodeToString(pngBytes); - run.images.add(new Item(caption, data)); + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ImageIO.write(image, "PNG", out); + out.close(); + byte[] pngBytes = out.toByteArray(); + String data = Base64.getEncoder().encodeToString(pngBytes); + run.images.add(new Item(caption, data)); + } catch (Exception ex) { + run.images.add(new Item(caption, null)); + } return this; } @Override - public JSONReport image(BufferedImage image) throws IOException { + public JSONReport image(BufferedImage image) { image("", image); return this; } diff --git a/app/com/horstmann/codecheck/Main.java b/app/com/horstmann/codecheck/Main.java index 28e1e82f..f4271bc3 100644 --- a/app/com/horstmann/codecheck/Main.java +++ b/app/com/horstmann/codecheck/Main.java @@ -11,6 +11,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; @@ -273,34 +274,25 @@ private void testInput(Path mainFile, if (!interleaveio && !test.equals("Input")) report.input(input); - List contents = new ArrayList<>(); - List imageComp = new ArrayList<>(); + Map contents = new HashMap<>(); + Map imageComp = new HashMap<>(); String outerr = plan.outerr(submissionRunID); for (String f : outFiles) { - if (CompareImages.isImage(f)) { - try { - imageComp.add(new CompareImages(plan.getOutputBytes(submissionRunID, f))); - } catch (IOException ex) { - report.output(outerr); - report.error(ex.getMessage()); - } - } + if (CompareImages.isImage(f)) + imageComp.put(f, new CompareImages(plan.getOutputBytes(submissionRunID, f))); else - contents.add(plan.getOutputString(submissionRunID, f)); + contents.put(f, plan.getOutputString(submissionRunID, f)); } if (!runSolution) { report.output(outerr); for (String f : outFiles) { if (CompareImages.isImage(f)) { - try { - report.image("Image", imageComp.remove(0).first()); - } catch (IOException ex) { - report.error(ex.getMessage()); - } + CompareImages ci = imageComp.get(f); + report.image("Image", ci.first()); } else - report.file(f, contents.remove(0)); + report.file(f, contents.get(f)); } // No score return; @@ -320,7 +312,7 @@ private void testInput(Path mainFile, for (String f : outFiles) { if (CompareImages.isImage(f)) { - CompareImages ic = imageComp.remove(0); + CompareImages ic = imageComp.get(f); try { ic.setOtherImage(plan.getOutputBytes(solutionRunID, f)); boolean outcome = ic.getOutcome(); @@ -335,7 +327,7 @@ private void testInput(Path mainFile, } } else { String expectedContents = plan.getOutputString(solutionRunID, f); - boolean outcome = comp.execute(input, contents.remove(0), + boolean outcome = comp.execute(input, contents.get(f), expectedContents, report, f); score.pass(outcome, report); } diff --git a/app/com/horstmann/codecheck/Report.java b/app/com/horstmann/codecheck/Report.java index 387711f6..b980470c 100644 --- a/app/com/horstmann/codecheck/Report.java +++ b/app/com/horstmann/codecheck/Report.java @@ -28,9 +28,9 @@ static class Match Report systemError(Throwable t); - Report image(String caption, BufferedImage image) throws IOException; + Report image(String caption, BufferedImage image); - Report image(BufferedImage image) throws IOException; + Report image(BufferedImage image); Report file(String file, String contents); From f8295e27e34ce72986b44ed01b6ea8aadb6e6932 Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Sun, 27 Mar 2022 14:52:28 +0200 Subject: [PATCH 004/217] Deploy staging comrun --- comrun/Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/comrun/Dockerfile b/comrun/Dockerfile index 677940e1..e06fe933 100644 --- a/comrun/Dockerfile +++ b/comrun/Dockerfile @@ -45,11 +45,11 @@ ENV COMRUN_USER=comrunner RUN echo "Defaults:root !syslog, !pam_session" >> /etc/sudoers RUN echo "Defaults:comrunner !syslog, !pam_session" >> /etc/sudoers -COPY bin /opt/codecheck -RUN chmod +x /opt/codecheck/comrun +COPY --chmod +x bin /opt/codecheck +#RUN chmod +x /opt/codecheck/comrun -RUN mkdir /tmp/codecheck -RUN chmod 733 /tmp/codecheck +RUN --chmod 733 mkdir /tmp/codecheck +#RUN chmod 733 /tmp/codecheck # To avoid Unable to open env file: /etc/default/locale: No such file or directory RUN echo 'LANG="en_US.UTF-8"' >> /etc/default/locale From bb04a45bb85bf8a4abe754564271b086ce698f3b Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Thu, 31 Mar 2022 14:10:56 +0200 Subject: [PATCH 005/217] Deploy staging play-codecheck --- app/controllers/LTIAssignment.java | 7 ++++--- app/models/S3Connection.java | 27 +++++++++++++++++++-------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/app/controllers/LTIAssignment.java b/app/controllers/LTIAssignment.java index 2abb93cd..66d9ccf8 100644 --- a/app/controllers/LTIAssignment.java +++ b/app/controllers/LTIAssignment.java @@ -332,9 +332,10 @@ public Result saveWork(Http.Request request) throws IOException, NoSuchAlgorithm } } result.put("submittedAt", now.toString()); - - s3conn.writeNewerJsonObjectToDynamoDB("CodeCheckWork", workNode, "assignmentID", "submittedAt"); - submitGradeToLMS(requestNode, (ObjectNode) requestNode.get("work"), result); + if (s3conn.writeNewerJsonObjectToDynamoDB("CodeCheckWork", workNode, "assignmentID", "submittedAt")) { + // Don't submit grade if this is an older submission + submitGradeToLMS(requestNode, (ObjectNode) requestNode.get("work"), result); + } return ok(result); } catch (Exception e) { logger.error("saveWork: " + requestNode + " " + e.getMessage()); diff --git a/app/models/S3Connection.java b/app/models/S3Connection.java index 5bb8406f..77569abd 100644 --- a/app/models/S3Connection.java +++ b/app/models/S3Connection.java @@ -29,7 +29,10 @@ import com.amazonaws.services.dynamodbv2.document.spec.PutItemSpec; import com.amazonaws.services.dynamodbv2.document.spec.QuerySpec; import com.amazonaws.services.dynamodbv2.document.utils.ValueMap; +import com.amazonaws.services.dynamodbv2.model.ConditionalCheckFailedException; +import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughputExceededException; import com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException; +import com.amazonaws.services.dynamodbv2.model.TransactionConflictException; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3ClientBuilder; import com.amazonaws.services.s3.model.AmazonS3Exception; @@ -52,6 +55,8 @@ public class S3Connection { private AmazonDynamoDB amazonDynamoDB; private static Logger.ALogger logger = Logger.of("com.horstmann.codecheck"); + public static class OutOfOrderException extends RuntimeException {} + @Inject public S3Connection(Config config) { this.config = config; // For local testing only--TODO: What exactly should work in this situation? @@ -273,7 +278,7 @@ public void writeJsonObjectToDynamoDB(String tableName, ObjectNode obj) { ); } - public void writeNewerJsonObjectToDynamoDB(String tableName, ObjectNode obj, String primaryKeyName, String timeStampKeyName) { + public boolean writeNewerJsonObjectToDynamoDB(String tableName, ObjectNode obj, String primaryKeyName, String timeStampKeyName) { DynamoDB dynamoDB = new DynamoDB(amazonDynamoDB); Table table = dynamoDB.getTable(tableName); /* @@ -283,11 +288,17 @@ public void writeNewerJsonObjectToDynamoDB(String tableName, ObjectNode obj, Str Apparently, the simpler putItem(item, conditionalExpression, nameMap, valueMap) swallows the ConditionalCheckFailedException */ String conditionalExpression = "attribute_not_exists(" + primaryKeyName + ") OR " + timeStampKeyName + " < :" + timeStampKeyName; - table.putItem( - new PutItemSpec() - .withItem(Item.fromJSON(obj.toString())) - .withConditionExpression(conditionalExpression) - .withValueMap(Collections.singletonMap(":" + timeStampKeyName, obj.get(timeStampKeyName).asText())) - ); - } + try { + table.putItem( + new PutItemSpec() + .withItem(Item.fromJSON(obj.toString())) + .withConditionExpression(conditionalExpression) + .withValueMap(Collections.singletonMap(":" + timeStampKeyName, obj.get(timeStampKeyName).asText()))); + return true; + } catch(ConditionalCheckFailedException e) { + // https://github.com/aws/aws-sdk-java/issues/1945 + logger.warn("writeNewerJsonObjectToDynamoDB: " + e.getMessage() + " " + obj); + return false; + } + } } \ No newline at end of file From dda4ba55978fd314a70c34243e6a10e2a0621563 Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Thu, 31 Mar 2022 14:39:07 +0200 Subject: [PATCH 006/217] Deploy staging play-codecheck --- app/controllers/Assignment.java | 4 ++-- app/controllers/Check.java | 4 ++-- app/controllers/Files.java | 8 ++++---- app/controllers/LTIProblem.java | 4 ++-- app/models/Util.java | 10 ++++++++++ 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/app/controllers/Assignment.java b/app/controllers/Assignment.java index d1c1f4e9..b0c29955 100644 --- a/app/controllers/Assignment.java +++ b/app/controllers/Assignment.java @@ -274,8 +274,8 @@ public Result work(Http.Request request, String assignmentID, String ccid, Strin assignmentNode.put("returnToWorkURL", returnToWorkURL); assignmentNode.put("editKeySaved", editKeySaved); assignmentNode.put("sentAt", Instant.now().toString()); - Http.Cookie newCookie1 = Http.Cookie.builder("ccid", ccid).withPath("/").withMaxAge(Duration.ofDays(180)).build(); - Http.Cookie newCookie2 = Http.Cookie.builder("cckey", editKey).withPath("/").withMaxAge(Duration.ofDays(180)).build(); + Http.Cookie newCookie1 = Http.Cookie.builder("ccid", ccid).withPath("/").withMaxAge(Duration.ofDays(180)).withSameSite(Http.Cookie.SameSite.STRICT).build(); + Http.Cookie newCookie2 = Http.Cookie.builder("cckey", editKey).withPath("/").withMaxAge(Duration.ofDays(180)).withSameSite(Http.Cookie.SameSite.STRICT).build(); return ok(views.html.workAssignment.render(assignmentNode.toString(), work, ccid, lti)).withCookies(newCookie1, newCookie2); } else { // Instructor diff --git a/app/controllers/Check.java b/app/controllers/Check.java index 129ae249..ac4c02f9 100644 --- a/app/controllers/Check.java +++ b/app/controllers/Check.java @@ -67,7 +67,7 @@ else if (key.equals("ccid")) // TODO: For testing of randomization? report = String.format("Timed out after %5.0f seconds\n", elapsed); } - Http.Cookie newCookie = Http.Cookie.builder("ccid", ccid).withMaxAge(Duration.ofDays(180)).build(); + Http.Cookie newCookie = models.Util.buildCookie("ccid", ccid); return ok(report).withCookies(newCookie).as("text/html"); } catch (Exception ex) { @@ -160,7 +160,7 @@ else if ("application/json".equals(request.contentType().orElse(""))) { } result.put("zip", reportZip); - Http.Cookie newCookie = Http.Cookie.builder("ccid", ccid).withMaxAge(Duration.ofDays(180)).build(); + Http.Cookie newCookie = models.Util.buildCookie("ccid", ccid); return ok(result).withCookies(newCookie).as("application/json"); } catch (Exception ex) { return internalServerError(Util.getStackTrace(ex)); diff --git a/app/controllers/Files.java b/app/controllers/Files.java index 4ae57e80..cd3dabed 100644 --- a/app/controllers/Files.java +++ b/app/controllers/Files.java @@ -78,7 +78,7 @@ public Result filesHTML2(Http.Request request, String repo, String problemName, result.append(data.toString()); result.append(end2); wakeupChecker(); - Http.Cookie newCookie = Http.Cookie.builder("ccid", ccid).withMaxAge(Duration.ofDays(180)).build(); + Http.Cookie newCookie = models.Util.buildCookie("ccid", ccid); return ok(result.toString()).withCookies(newCookie).as("text/html"); } @@ -136,7 +136,7 @@ public Result tracer(Http.Request request, String repo, String problemName, Stri result.append(Util.getString(problemFiles, Path.of("tracer.js"))); result.append(tracerEnd); - Http.Cookie newCookie = Http.Cookie.builder("ccid", ccid).withMaxAge(Duration.ofDays(180)).build(); + Http.Cookie newCookie = models.Util.buildCookie("ccid", ccid); return ok(result.toString()).withCookies(newCookie).as("text/html"); } @@ -157,7 +157,7 @@ public Result fileData(Http.Request request, String repo, String problemName, St } Problem problem = new Problem(problemFiles); - Http.Cookie newCookie = Http.Cookie.builder("ccid", ccid).withMaxAge(Duration.ofDays(180)).build(); + Http.Cookie newCookie = models.Util.buildCookie("ccid", ccid); return ok(models.Util.toJson(problem.getProblemData())).withCookies(newCookie); } @@ -304,7 +304,7 @@ public Result filesHTML(Http.Request request, String repo, String problemName, S // result.append(jsonpAjaxSubmissionScript); result.append(bodyEnd); - Http.Cookie newCookie = Http.Cookie.builder("ccid", ccid).withMaxAge(Duration.ofDays(180)).build(); + Http.Cookie newCookie = models.Util.buildCookie("ccid", ccid); return ok(result.toString()).withCookies(newCookie).as("text/html"); } } diff --git a/app/controllers/LTIProblem.java b/app/controllers/LTIProblem.java index 05c04c28..90d512cd 100644 --- a/app/controllers/LTIProblem.java +++ b/app/controllers/LTIProblem.java @@ -177,7 +177,7 @@ public Result launchCodeCheck(Http.Request request, String repo, String problemN " " + " " + ""; - Http.Cookie newCookie = Http.Cookie.builder("ccid", ccid).withMaxAge(Duration.ofDays(180)).build(); + Http.Cookie newCookie = models.Util.buildCookie("ccid", ccid); return ok(document).withCookies(newCookie).as("text/html"); } catch (Exception ex) { logger.error("launchCodeCheck: Cannot load problem " + repo + "/" + problemName + " " + ex.getMessage()); @@ -221,7 +221,7 @@ public Result launchTracer(Http.Request request, String repo, String problemName result.append("horstmann_config.lti = " + ltiNode.toString() + "\n"); result.append(Util.getString(problemFiles, Path.of("tracer.js"))); result.append(tracerEnd); - Http.Cookie newCookie = Http.Cookie.builder("ccid", ccid).withMaxAge(Duration.ofDays(180)).build(); + Http.Cookie newCookie = models.Util.buildCookie("ccid", ccid); return ok(result.toString()).withCookies(newCookie).as("text/html"); } catch (Exception ex) { logger.error("launchTracer: Cannot load problem " + repo + "/" + problemName + " " + ex.getMessage()); diff --git a/app/models/Util.java b/app/models/Util.java index 7cb9e60f..24c19007 100644 --- a/app/models/Util.java +++ b/app/models/Util.java @@ -1,5 +1,7 @@ package models; +import java.time.Duration; + import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -24,4 +26,12 @@ public static ObjectNode toJson(Object obj) { mapper.setSerializationInclusion(Include.NON_DEFAULT); return (ObjectNode) mapper.convertValue(obj, JsonNode.class); } + + public static Http.Cookie buildCookie(String name, String value) { + return Http.Cookie.builder(name, value) + .withPath("/") + .withMaxAge(Duration.ofDays(180)) + .withSameSite(Http.Cookie.SameSite.STRICT) + .build(); + } } From e7a1ed9d7d216d658880091b1703d85441a25bd5 Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Thu, 31 Mar 2022 14:46:35 +0200 Subject: [PATCH 007/217] Deploy staging play-codecheck --- app/controllers/Assignment.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/controllers/Assignment.java b/app/controllers/Assignment.java index b0c29955..6cb68e49 100644 --- a/app/controllers/Assignment.java +++ b/app/controllers/Assignment.java @@ -274,9 +274,10 @@ public Result work(Http.Request request, String assignmentID, String ccid, Strin assignmentNode.put("returnToWorkURL", returnToWorkURL); assignmentNode.put("editKeySaved", editKeySaved); assignmentNode.put("sentAt", Instant.now().toString()); - Http.Cookie newCookie1 = Http.Cookie.builder("ccid", ccid).withPath("/").withMaxAge(Duration.ofDays(180)).withSameSite(Http.Cookie.SameSite.STRICT).build(); - Http.Cookie newCookie2 = Http.Cookie.builder("cckey", editKey).withPath("/").withMaxAge(Duration.ofDays(180)).withSameSite(Http.Cookie.SameSite.STRICT).build(); - return ok(views.html.workAssignment.render(assignmentNode.toString(), work, ccid, lti)).withCookies(newCookie1, newCookie2); + Http.Cookie newCookie1 = models.Util.buildCookie("ccid", ccid); + Http.Cookie newCookie2 = models.Util.buildCookie("cckey", editKey); + return ok(views.html.workAssignment.render(assignmentNode.toString(), work, ccid, lti)) + .withCookies(newCookie1, newCookie2); } else { // Instructor if (ccid == null) { From e03fb005239b99aeb93caa41a88129cf250e36f1 Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Fri, 1 Apr 2022 21:47:19 +0200 Subject: [PATCH 008/217] Deploy release play-codecheck --- app/controllers/LTIAssignment.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/LTIAssignment.java b/app/controllers/LTIAssignment.java index 66d9ccf8..84458e3e 100644 --- a/app/controllers/LTIAssignment.java +++ b/app/controllers/LTIAssignment.java @@ -338,7 +338,8 @@ public Result saveWork(Http.Request request) throws IOException, NoSuchAlgorithm } return ok(result); } catch (Exception e) { - logger.error("saveWork: " + requestNode + " " + e.getMessage()); + logger.error("saveWork: " + requestNode); + logger.error(Util.getStackTrace(e)); return badRequest("saveWork: " + requestNode); } } From 9f3d486dd12a111bfc5062974cd01c4e1818ea5c Mon Sep 17 00:00:00 2001 From: Sergio Rojas <48057303+hisergiorojas@users.noreply.github.com> Date: Wed, 20 Apr 2022 10:27:20 -0700 Subject: [PATCH 009/217] Update Installing Codecheck dependencies --- build-instructions.md | 115 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 108 insertions(+), 7 deletions(-) diff --git a/build-instructions.md b/build-instructions.md index 537245fa..76ddba44 100644 --- a/build-instructions.md +++ b/build-instructions.md @@ -30,15 +30,119 @@ This tool uses only the part of `play-codecheck` that deals with checking a problem (in the `com.horstmann.codecheck` package). The tool is called `codecheck`. It is created by the `cli/build.xml` Ant script. +## Dependencies + +* openjdk-11-jdk https://openjdk.java.net/projects/jdk/11 +* git https://git-scm.com +* ant https://ant.apache.org +* curl https://curl.se +* unzip +* sbt https://www.scala-sbt.org +* docker https://www.docker.com +* gcloud CLI SDK https://cloud.google.com +* AWS CLI https://aws.amazon.com/ + + +## Install Codecheck dependencies +Open a terminal and install the dependencies +``` +sudo apt-get update +sudo apt install openjdk-11-jdk git ant curl unzip +``` + +Install sbt for Linux (deb) or [follow the instruction for your environment](https://www.scala-sbt.org/download.html) +``` +echo "deb https://repo.scala-sbt.org/scalasbt/debian all main" | sudo tee /etc/apt/sources.list.d/sbt.list +echo "deb https://repo.scala-sbt.org/scalasbt/debian /" | sudo tee /etc/apt/sources.list.d/sbt_old.list +curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823" | sudo apt-key add +sudo apt-get update +sudo apt-get install sbt +``` +Install docker for Linux (deb) or [follow the instruction for your environment](https://docs.docker.com/engine/install/) +``` + sudo apt-get update + + sudo apt-get install \ + ca-certificates \ + curl \ + gnupg \ + lsb-release +``` +Add Docker’s official GPG key +``` + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg +``` +Use the following command to set up the stable repository. +``` +echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null +``` +Update the apt package index, and install the latest version of Docker Engine and containerd +``` + sudo apt-get update + sudo apt-get install docker-ce docker-ce-cli containerd.io +``` + +Install Google Cloud CLI for linux or [follow the instruction for your environment](https://cloud.google.com/sdk/docs/install#linux) + +Open a terminal and download Google Cloud SDK +``` +curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-373.0.0-linux-x86_64.tar.gz +``` + +Extract the contents of the file to any location on your file system (preferably your Home directory). To replace an existing installation, remove the existing google-cloud-sdk directory and then extract the archive to the same location. +``` +tar -xf google-cloud-sdk-373.0.0-linux-x86.tar.gz +``` + +Run the script (from the root of the folder you extracted to) using the following command +``` +./google-cloud-sdk/install.sh +``` +To initialize the gcloud CLI, run gcloud init +``` +./google-cloud-sdk/bin/gcloud init +``` +Install AWS CLI for linux or [follow the instruction for your environment](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) + +Open a terminal and download the AWS CLI installation file + +``` +curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" +``` + +Unzip the installer +``` +unzip awscliv2.zip +``` + +Run the install program +``` +sudo ./aws/install +``` +Confirm the installation with the following command +``` +aws --version +``` +Configure AWS CLI [instruction](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html) +* Access key ID + +* Secret access key + +* AWS Region + +* Output format +``` +aws configure +``` + + Building the Command Line Tool ------------------------------ These instructions are for Ubuntu 20.04LTS. -Install the following software: - - sudo apt install openjdk-11-jdk git ant curl unzip - Make a directory `/opt/codecheck` and a subdirectory `ext` that you own: sudo mkdir -p /opt/codecheck/ext @@ -124,7 +228,6 @@ Building the Web Application Install, following the instructions of the providers, -- [SBT](https://www.scala-sbt.org/download.html) - [Eclipse](https://www.eclipse.org/eclipseide/) Run the `play-codecheck` server: @@ -167,8 +270,6 @@ needed. Docker Deployment ----------------- -Install [Docker](https://docs.docker.com/engine/install/ubuntu/). - Build and run the Docker container for the `comrun` service: docker build --tag codecheck:1.0-SNAPSHOT comrun From 7ae050f9776836fa6d36b734c3b2d379cbe30a8a Mon Sep 17 00:00:00 2001 From: cayhorstmann Date: Thu, 21 Apr 2022 06:24:24 +0200 Subject: [PATCH 010/217] Update build-instructions.md --- build-instructions.md | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/build-instructions.md b/build-instructions.md index 76ddba44..4b0a41a3 100644 --- a/build-instructions.md +++ b/build-instructions.md @@ -1,4 +1,4 @@ -CodeCheck^®^ Build Instructions +CodeCheck® Build Instructions =============================== Program Structure @@ -44,6 +44,9 @@ is called `codecheck`. It is created by the `cli/build.xml` Ant script. ## Install Codecheck dependencies + +These instructions are for Ubuntu 20.04LTS. + Open a terminal and install the dependencies ``` sudo apt-get update @@ -78,7 +81,7 @@ echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null ``` -Update the apt package index, and install the latest version of Docker Engine and containerd +Update the apt package index, and install the latest version of Docker Engine and `containerd` ``` sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io @@ -86,12 +89,12 @@ Update the apt package index, and install the latest version of Docker Engine an Install Google Cloud CLI for linux or [follow the instruction for your environment](https://cloud.google.com/sdk/docs/install#linux) -Open a terminal and download Google Cloud SDK +Open a terminal and download the Google Cloud SDK ``` curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-373.0.0-linux-x86_64.tar.gz ``` -Extract the contents of the file to any location on your file system (preferably your Home directory). To replace an existing installation, remove the existing google-cloud-sdk directory and then extract the archive to the same location. +Extract the contents of the file to any location on your file system (preferably your home directory). To replace an existing installation, remove the existing google-cloud-sdk directory and then extract the archive to the same location. ``` tar -xf google-cloud-sdk-373.0.0-linux-x86.tar.gz ``` @@ -100,11 +103,11 @@ Run the script (from the root of the folder you extracted to) using the followin ``` ./google-cloud-sdk/install.sh ``` -To initialize the gcloud CLI, run gcloud init +To initialize the gcloud CLI, run `gcloud init` ``` ./google-cloud-sdk/bin/gcloud init ``` -Install AWS CLI for linux or [follow the instruction for your environment](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) +Install the AWS CLI for Linux or [follow the instruction for your environment](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) Open a terminal and download the AWS CLI installation file @@ -125,13 +128,10 @@ Confirm the installation with the following command ``` aws --version ``` -Configure AWS CLI [instruction](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html) +Configure the AWS CLI [instructions](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html) * Access key ID - * Secret access key - * AWS Region - * Output format ``` aws configure @@ -141,8 +141,6 @@ aws configure Building the Command Line Tool ------------------------------ -These instructions are for Ubuntu 20.04LTS. - Make a directory `/opt/codecheck` and a subdirectory `ext` that you own: sudo mkdir -p /opt/codecheck/ext From 905f594e76a44e7e04dc66c9c8a5834454a3c488 Mon Sep 17 00:00:00 2001 From: cayhorstmann Date: Thu, 21 Apr 2022 06:24:50 +0200 Subject: [PATCH 011/217] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3fd1811..3a9d5e35 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -CodeCheck^®^ +CodeCheck® ============ * [Description](https://codecheck.io) From f69ef5171542715acbee6ead2979e1a862fed13e Mon Sep 17 00:00:00 2001 From: cayhorstmann Date: Fri, 29 Apr 2022 20:00:46 +0200 Subject: [PATCH 012/217] Update build-instructions.md --- build-instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-instructions.md b/build-instructions.md index 4b0a41a3..e91e6d85 100644 --- a/build-instructions.md +++ b/build-instructions.md @@ -230,7 +230,7 @@ Install, following the instructions of the providers, Run the `play-codecheck` server: - sbt run + COMRUN_USER=$(whoami) sbt run Point the browser to . Upload a problem and test it. From ea8d28f4a2f6b4e968c5fa63791a8f6e3b4ba827 Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Tue, 10 May 2022 17:47:13 +0200 Subject: [PATCH 013/217] ... --- app/controllers/Files.java | 2 +- build-instructions.md | 16 +++++++++++++++- receiveMessage.js | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/controllers/Files.java b/app/controllers/Files.java index cd3dabed..77b1abad 100644 --- a/app/controllers/Files.java +++ b/app/controllers/Files.java @@ -304,7 +304,7 @@ public Result filesHTML(Http.Request request, String repo, String problemName, S // result.append(jsonpAjaxSubmissionScript); result.append(bodyEnd); - Http.Cookie newCookie = models.Util.buildCookie("ccid", ccid); + Http.Cookie newCookie = models.Util.buildCookie("ccid", ccid); return ok(result.toString()).withCookies(newCookie).as("text/html"); } } diff --git a/build-instructions.md b/build-instructions.md index 537245fa..99fa2c7f 100644 --- a/build-instructions.md +++ b/build-instructions.md @@ -242,12 +242,26 @@ Alternatively, you can test with the locally running web app. In com.horstmann.codecheck.comrun.remote= the URL of the comrun service -Play Server Deployment {#server-deployment} +Play Server Deployment ---------------------- In Amazon S3, create a bucket whose name starts with the four characters `ext.` and an arbitrary suffix, such as `ext.mydomain.com` to hold the uploaded CodeCheck problems. Set the ACL so that the bucket owner has all access rights and nobody else has any. +If you use CodeCheck with LTI, you need to set up an Amazon Dynamo database. Create the following tables: + +| Name | Partition key | Sort key | +| ------------------------- | ------------------ | ----------- | +| CodeCheckAssignments | assignmentID | | +| CodeCheckLTICredentials | oauth_consumer_key | | +| CodeCheckLTIResources | resourceID | | +| CodeCheckSubmissions | submissionID | submittedAt | +| CodeCheckWork | assignmentID | workID | + +The first three tables have no sort key. All types are `String`. + +You need to populate the `CodeCheckLTICredentials` table with at least one pair `oauth_consumer_key` and `shared_secret` (both of type `String`). These can be any values. I recommend to use the admin's email for `oauth_consumer_key` and a random password for `shared_secret`. + In your Google Cloud Run project, add another service `play-codecheck`. Add the following to `conf/production.conf`: diff --git a/receiveMessage.js b/receiveMessage.js index 0d4bfa2b..e75bc76e 100644 --- a/receiveMessage.js +++ b/receiveMessage.js @@ -33,7 +33,7 @@ Always from child to parent Score between 0 and 1 No response -{ query: 'retrieve', param: { qid: ... } } +{ query: 'retrieve', nonce: ..., param: { qid: ... } } Response: { request: ..., param: state } TODO: Why not param: { state: ..., score: ... } From 0fdfdf11f74c069809676e66763f420570e3d14a Mon Sep 17 00:00:00 2001 From: cayhorstmann Date: Mon, 23 May 2022 18:08:31 +0200 Subject: [PATCH 014/217] Update build-instructions.md --- build-instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-instructions.md b/build-instructions.md index 2e55a307..4faceaf2 100644 --- a/build-instructions.md +++ b/build-instructions.md @@ -96,7 +96,7 @@ curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud- Extract the contents of the file to any location on your file system (preferably your home directory). To replace an existing installation, remove the existing google-cloud-sdk directory and then extract the archive to the same location. ``` -tar -xf google-cloud-sdk-373.0.0-linux-x86.tar.gz +tar -xf google-cloud-sdk-373.0.0-linux-x86_64.tar.gz ``` Run the script (from the root of the folder you extracted to) using the following command From 6d11078519b1059f8c3b7eb7d1f3aac284cd75f5 Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Fri, 3 Jun 2022 07:21:31 +0200 Subject: [PATCH 015/217] ... --- app/com/horstmann/codecheck/Util.java | 1 + build-instructions.md | 249 +++++++++++++++++--------- build.sbt | 2 +- 3 files changed, 163 insertions(+), 89 deletions(-) diff --git a/app/com/horstmann/codecheck/Util.java b/app/com/horstmann/codecheck/Util.java index 6d34f8f0..61027391 100644 --- a/app/com/horstmann/codecheck/Util.java +++ b/app/com/horstmann/codecheck/Util.java @@ -538,6 +538,7 @@ public static boolean exists(String url) { // UIDs + // TODO Consider using https://github.com/scru128/spec public static String createPrivateUID() { return new BigInteger(128, generator).toString(36).toUpperCase(); } diff --git a/build-instructions.md b/build-instructions.md index 2e55a307..d8df96c9 100644 --- a/build-instructions.md +++ b/build-instructions.md @@ -1,8 +1,6 @@ -CodeCheck® Build Instructions -=============================== +# CodeCheck® Build Instructions -Program Structure ------------------ +## Program Structure CodeCheck has two parts: @@ -30,24 +28,17 @@ This tool uses only the part of `play-codecheck` that deals with checking a problem (in the `com.horstmann.codecheck` package). The tool is called `codecheck`. It is created by the `cli/build.xml` Ant script. -## Dependencies +## Install Codecheck dependencies * openjdk-11-jdk https://openjdk.java.net/projects/jdk/11 -* git https://git-scm.com * ant https://ant.apache.org -* curl https://curl.se -* unzip * sbt https://www.scala-sbt.org * docker https://www.docker.com -* gcloud CLI SDK https://cloud.google.com -* AWS CLI https://aws.amazon.com/ - - -## Install Codecheck dependencies These instructions are for Ubuntu 20.04LTS. Open a terminal and install the dependencies + ``` sudo apt-get update sudo apt install openjdk-11-jdk git ant curl unzip @@ -87,56 +78,22 @@ Update the apt package index, and install the latest version of Docker Engine an sudo apt-get install docker-ce docker-ce-cli containerd.io ``` -Install Google Cloud CLI for linux or [follow the instruction for your environment](https://cloud.google.com/sdk/docs/install#linux) +## Special Steps for Github Codespaces -Open a terminal and download the Google Cloud SDK -``` -curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-373.0.0-linux-x86_64.tar.gz -``` +Make a new Codespace by cloning the repository `cayhorstmann/codecheck2` -Extract the contents of the file to any location on your file system (preferably your home directory). To replace an existing installation, remove the existing google-cloud-sdk directory and then extract the archive to the same location. -``` -tar -xf google-cloud-sdk-373.0.0-linux-x86.tar.gz -``` +Open a terminal. Run -Run the script (from the root of the folder you extracted to) using the following command ``` -./google-cloud-sdk/install.sh +sed -i -e 's/root/ALL/' /etc/sudoers.d/codespace +cat /etc/sudoers.d/codespace ``` -To initialize the gcloud CLI, run `gcloud init` -``` -./google-cloud-sdk/bin/gcloud init -``` -Install the AWS CLI for Linux or [follow the instruction for your environment](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) -Open a terminal and download the AWS CLI installation file - -``` -curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" -``` +and verify that the contents is -Unzip the installer -``` -unzip awscliv2.zip -``` - -Run the install program -``` -sudo ./aws/install -``` -Confirm the installation with the following command -``` -aws --version ``` -Configure the AWS CLI [instructions](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html) -* Access key ID -* Secret access key -* AWS Region -* Output format +codespace ALL=(ALL) NOPASSWD:ALL ``` -aws configure -``` - Building the Command Line Tool ------------------------------ @@ -146,7 +103,7 @@ Make a directory `/opt/codecheck` and a subdirectory `ext` that you own: sudo mkdir -p /opt/codecheck/ext export ME=$(whoami) ; sudo -E chown $ME /opt/codecheck /opt/codecheck/ext -Clone the repo: +Clone the repo (unless you are in Codespaces, where it is already cloned) git clone https://github.com/cayhorstmann/codecheck2 @@ -178,27 +135,42 @@ Test that it works: If you omit the `-t`, you get a report with your default browser instead of the text report. -Debugging the Command Line Tool -------------------------------- +## Eclipse -If you are making changes to the part of CodeCheck that does the actual -code checking, such as adding a new language, and you need to run a -debugger, it is easiest to debug the command line tool. +If you work on your own machine, I recommend Eclipse as the IDE. If you use Codespaces, skip this section and read about the Visual Studio Code configuration instead. -Make directories for the submission and problem files, and populate them -with samples. +Install [Eclipse](https://www.eclipse.org/eclipseide/), following the instructions of the provider. + +Run -In your debug configuration, set: + sbt eclipse + sbt compile + +Then open Eclipse and import the created project. + +Make two debugger configurations. Select Run → Debug Configurations, +right-click on Remote Java Application, and select New Configuration. -- The main class to +For the first configuration, specify: + +- Name: Debug (Attach) +- Project: `play-codecheck` +- Connection type: Standard +- Host: `localhost` +- Port: 9999 + +For the second debug configuration, set: + +- Name: Launch Main +- Main class: com.horstmann.codecheck.Main -- Program arguments to +- Program arguments: - /path/to/submissiondir /path/to/problemdir + /tmp/submission /tmp/problem -- VM arguments to +- VM arguments: -Duser.language=en -Duser.country=US @@ -206,7 +178,73 @@ In your debug configuration, set: -Dcom.horstmann.codecheck.report=HTML -Dcom.horstmann.codecheck.debug -- The environment variable `COMRUN_USER` to your username +- Environment variable `COMRUN_USER`: your username + +## Visual Studio Code + +If you use Codespaces, you need to use Visual Studio Code as your IDE. If not, skip this section and follow the section about configuring Eclipse instead. + +Run + + sbt eclipse + sbt compile + +Then open the base directory in Visual Studio Code. Visual Studio Code will read the project configuration from the Eclipse configuration. + +In Visual Studio Code, click on the Run and Debug (triangle and bug) icon on the left. Select Run → Add Configuration from the menu. The file `.vscode/launch.json` is opened up. Set it to the following contents: + +``` +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Debug (Attach)", + "request": "attach", + "hostName": "localhost", + "port": 9999, + "projectName": "play-codecheck" + }, + { + "type": "java", + "name": "Launch Main", + "request": "launch", + "mainClass": "com.horstmann.codecheck.Main", + "projectName": "play-codecheck" + "args": "/tmp/submission /tmp/problem", + "vmArgs": [ + "-Duser.language=en", + "-Duser.country=US", + "-Dcom.horstmann.codecheck.comrun.local=/opt/codecheck/comrun", + "-Dcom.horstmann.codecheck.report=HTML", + "-Dcom.horstmann.codecheck.debug" + ], + "env": { "COMRUN_USER": "codespace" } + } + ] +} +``` + +Debugging the Command Line Tool +------------------------------- + +If you are making changes to the part of CodeCheck that does the actual +code checking, such as adding a new language, and you need to run a +debugger, it is easiest to debug the command line tool. + +Make directories for the submission and problem files, and populate them +with samples. For example, + +``` +rm -rf /tmp/submission /tmp/problem +mkdir /tmp/submission +cp samples/java/example1/*.java /tmp/submission +cp -R samples/java/example1 /tmp/problem +``` +Set a breakpoint in app/com/horstmann/codecheck/Main.java and launch the debugger with the Launch Main configuration. To debug on Windows or MacOS, you have to use the Docker container for compilation and execution. @@ -217,17 +255,13 @@ compilation and execution. Point your browser to to check that the container is running. -When debugging, add the VM argument +Add the VM argument -Dcom.horstmann.codecheck.comrun.remote=http://localhost:8080/api/upload Building the Web Application ---------------------------- -Install, following the instructions of the providers, - -- [Eclipse](https://www.eclipse.org/eclipseide/) - Run the `play-codecheck` server: COMRUN_USER=$(whoami) sbt run @@ -241,21 +275,6 @@ directory. Debugging the Server -------------------- -Import the project into Eclipse. Run - - sbt eclipse - -Then open Eclipse and import the created project. - -Make a debugger configuration. Select Run → Debug Configurations, -right-click on Remote Java Application, and select New Configuration. -Specify: - -- Project: `play-codecheck` -- Connection type: Standard -- Host: `localhost` -- Port: 9999 - Run the `play-codecheck` server in debug mode: COMRUN_USER=$(whoami) sbt -jvm-debug 9999 run @@ -297,6 +316,59 @@ Kill both containers by running this command in another terminal: docker container kill $(docker ps -q) +Cloud Provider Tools +-------------------- + +Install Google Cloud CLI for linux or [follow the instruction for your environment](https://cloud.google.com/sdk/docs/install#linux) + +Open a terminal and download the Google Cloud SDK +``` +curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-373.0.0-linux-x86_64.tar.gz +``` + +Extract the contents of the file to any location on your file system (preferably your home directory). To replace an existing installation, remove the existing google-cloud-sdk directory and then extract the archive to the same location. +``` +tar -xf google-cloud-sdk-373.0.0-linux-x86.tar.gz +``` + +Run the script (from the root of the folder you extracted to) using the following command +``` +./google-cloud-sdk/install.sh +``` +To initialize the gcloud CLI, run `gcloud init` +``` +./google-cloud-sdk/bin/gcloud init +``` +Install the AWS CLI for Linux or [follow the instruction for your environment](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) + +Open a terminal and download the AWS CLI installation file + +``` +curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" +``` + +Unzip the installer +``` +unzip awscliv2.zip +``` + +Run the install program +``` +sudo ./aws/install +``` +Confirm the installation with the following command +``` +aws --version +``` +Configure the AWS CLI [instructions](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html) +* Access key ID +* Secret access key +* AWS Region +* Output format +``` +aws configure +``` + Comrun Service Deployment {#service-deployment} ------------------------- @@ -341,6 +413,7 @@ Alternatively, you can test with the locally running web app. In com.horstmann.codecheck.comrun.remote= the URL of the comrun service + Play Server Deployment ---------------------- diff --git a/build.sbt b/build.sbt index f1dd0df6..72b0f4ea 100644 --- a/build.sbt +++ b/build.sbt @@ -4,7 +4,7 @@ version := "1.0-SNAPSHOT" maintainer := "cay@horstmann.com" -scalaVersion := "2.12.8" +scalaVersion := "2.12.15" javacOptions ++= Seq("-source", "11", "-target", "11") From 3a89f5f49691b35cf0ce502901ce5c07c4ef6874 Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Wed, 8 Jun 2022 19:31:42 +0200 Subject: [PATCH 016/217] Updated build instructions --- build-instructions.md | 80 +++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 48 deletions(-) diff --git a/build-instructions.md b/build-instructions.md index d8df96c9..c2a654c1 100644 --- a/build-instructions.md +++ b/build-instructions.md @@ -30,12 +30,7 @@ is called `codecheck`. It is created by the `cli/build.xml` Ant script. ## Install Codecheck dependencies -* openjdk-11-jdk https://openjdk.java.net/projects/jdk/11 -* ant https://ant.apache.org -* sbt https://www.scala-sbt.org -* docker https://www.docker.com - -These instructions are for Ubuntu 20.04LTS. +These instructions are for Ubuntu 20.04LTS. If you are not running Ubuntu natively, run it in a virtual machine. If you were asked to use Github Codespaces, that should be set up for you. Otherwise, you need to set up your own virtual machine. These instructions should be helpful: https://horstmann.com/pfh/2021/vm.html Open a terminal and install the dependencies @@ -52,32 +47,6 @@ curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89 sudo apt-get update sudo apt-get install sbt ``` -Install docker for Linux (deb) or [follow the instruction for your environment](https://docs.docker.com/engine/install/) -``` - sudo apt-get update - - sudo apt-get install \ - ca-certificates \ - curl \ - gnupg \ - lsb-release -``` -Add Docker’s official GPG key -``` - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg -``` -Use the following command to set up the stable repository. -``` -echo \ - "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ - $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null -``` -Update the apt package index, and install the latest version of Docker Engine and `containerd` -``` - sudo apt-get update - sudo apt-get install docker-ce docker-ce-cli containerd.io -``` - ## Special Steps for Github Codespaces Make a new Codespace by cloning the repository `cayhorstmann/codecheck2` @@ -246,19 +215,6 @@ cp -R samples/java/example1 /tmp/problem ``` Set a breakpoint in app/com/horstmann/codecheck/Main.java and launch the debugger with the Launch Main configuration. -To debug on Windows or MacOS, you have to use the Docker container for -compilation and execution. - - docker build --tag comrun:1.0-SNAPSHOT comrun - docker run -p 8080:8080 -it comrun:1.0-SNAPSHOT - -Point your browser to to check that -the container is running. - -Add the VM argument - - -Dcom.horstmann.codecheck.comrun.remote=http://localhost:8080/api/upload - Building the Web Application ---------------------------- @@ -284,8 +240,36 @@ you created, and select Debug. Point the browser to a URL such as . Set breakpoints as needed. -Docker Deployment ------------------ +## Docker Installation + +Install Docker for Linux (deb) or [follow the instruction for your environment](https://docs.docker.com/engine/install/) +``` + sudo apt-get update + + sudo apt-get install \ + ca-certificates \ + curl \ + gnupg \ + lsb-release +``` +Add Docker’s official GPG key +``` + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg +``` +Use the following command to set up the stable repository. +``` +echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null +``` +Update the apt package index, and install the latest version of Docker Engine and `containerd` +``` + sudo apt-get update + sudo apt-get install docker-ce docker-ce-cli containerd.io +``` + +Docker Local Testing +-------------------- Build and run the Docker container for the `comrun` service: @@ -314,7 +298,7 @@ Test that it works by pointing your browser to Kill both containers by running this command in another terminal: - docker container kill $(docker ps -q) + docker container kill $(docker ps -q) Cloud Provider Tools -------------------- From 217b45c1ed2e0fd27269a540293c3373b00c87c7 Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Thu, 16 Jun 2022 08:00:01 +0200 Subject: [PATCH 017/217] AWS CLI build instructions --- build-instructions.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/build-instructions.md b/build-instructions.md index c2a654c1..be2356e5 100644 --- a/build-instructions.md +++ b/build-instructions.md @@ -416,8 +416,46 @@ If you use CodeCheck with LTI, you need to set up an Amazon Dynamo database. Cre The first three tables have no sort key. All types are `String`. +``` +aws --region $REGION dynamodb create-table \ + --table-name CodeCheckAssignments \ + --attribute-definitions AttributeName=assignmentID,AttributeType=S \ + --key-schema AttributeName=assignmentID,KeyType=HASH \ + --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 + +aws --region $REGION dynamodb create-table \ + --table-name CodeCheckLTICredentials \ + --attribute-definitions AttributeName=oauth_consumer_key,AttributeType=S \ + --key-schema AttributeName=oauth_consumer_key,KeyType=HASH \ + --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 + +aws --region $REGION dynamodb create-table \ + --table-name CodeCheckLTIResources \ + --attribute-definitions AttributeName=resourceID,AttributeType=S \ + --key-schema AttributeName=resourceID,KeyType=HASH \ + --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 + +aws --region $REGION dynamodb create-table \ + --table-name CodeCheckSubmissions \ + --attribute-definitions AttributeName=submissionID,AttributeType=S AttributeName=submittedAt,AttributeType=S \ + --key-schema AttributeName=submissionID,KeyType=HASH AttributeName=submittedAt,KeyType=RANGE \ + --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 + +aws --region $REGION dynamodb create-table \ + --table-name CodeCheckWork \ + --attribute-definitions AttributeName=assignmentID,AttributeType=S AttributeName=workID,AttributeType=S \ + --key-schema AttributeName=assignmentID,KeyType=HASH AttributeName=workID,KeyType=RANGE \ + --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 +``` + You need to populate the `CodeCheckLTICredentials` table with at least one pair `oauth_consumer_key` and `shared_secret` (both of type `String`). These can be any values. I recommend to use the admin's email for `oauth_consumer_key` and a random password for `shared_secret`. +``` +USERNAME=... +PASSWORD=... +aws dynamodb put-item --table-name CodeCheckLTICredentials --item '{"oauth_consumer_key":{"S":"'${USERNAME}'"},"shared_secret":{"S":"'${PASSWORD}'"}}' +``` + In your Google Cloud Run project, add another service `play-codecheck`. Add the following to `conf/production.conf`: From eb86d1d7c5e0c194c22579e1712ec275d2449d95 Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Thu, 16 Jun 2022 08:05:19 +0200 Subject: [PATCH 018/217] AWS CLI build instructions --- build-instructions.md | 79 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/build-instructions.md b/build-instructions.md index be2356e5..9c988762 100644 --- a/build-instructions.md +++ b/build-instructions.md @@ -404,6 +404,46 @@ Play Server Deployment In Amazon S3, create a bucket whose name starts with the four characters `ext.` and an arbitrary suffix, such as `ext.mydomain.com` to hold the uploaded CodeCheck problems. Set the ACL so that the bucket owner has all access rights and nobody else has any. +``` +SUFFIX=mydomain.com +aws s3 mb s3://ext.$SUFFIX + +USERNAME=codecheck + +aws iam create-user --user-name $USERNAME +aws iam create-access-key --user-name $USERNAME + +# IMPORTANT: Record AccessKeyId and SecretAccessKey + +cat < policy1.json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "s3:*" + ], + "Resource": [ + "arn:aws:s3:::ext.codecheck-test.org" + ] + }, + { + "Effect": "Allow", + "Action": [ + "s3:*" + ], + "Resource": [ + "arn:aws:s3:::ext.codecheck-test.org/*" + ] + } + ] +} +EOF + +aws iam create-policy --policy-name CodeCheckTestS3 --policy-document file://./policy1.json +``` + If you use CodeCheck with LTI, you need to set up an Amazon Dynamo database. Create the following tables: | Name | Partition key | Sort key | @@ -446,6 +486,45 @@ aws --region $REGION dynamodb create-table \ --attribute-definitions AttributeName=assignmentID,AttributeType=S AttributeName=workID,AttributeType=S \ --key-schema AttributeName=assignmentID,KeyType=HASH AttributeName=workID,KeyType=RANGE \ --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 + + +ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) + +aws iam attach-user-policy --user-name $USERNAME \ + --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/CodeCheckTestS3 + +cat < policy2.json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:DeleteItem", + "dynamodb:BatchWriteItem", + "dynamodb:GetItem", + "dynamodb:BatchGetItem", + "dynamodb:Scan", + "dynamodb:Query", + "dynamodb:ConditionCheckItem" + ], + "Resource": [ + "arn:aws:dynamodb:us-west-1:$ACCOUNT_ID:table/CodeCheck*", + "arn:aws:dynamodb:us-west-1:$ACCOUNT_ID:table/CodeCheck*/index/*" + ] + } + ] +} +EOF + +aws iam create-policy --policy-name CodeCheckTestDynamo --policy-document file://./policy2.json + +aws iam attach-user-policy --user-name $USERNAME \ + --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/CodeCheckTestDynamo + +aws iam list-attached-user-policies --user-name $USERNAME ``` You need to populate the `CodeCheckLTICredentials` table with at least one pair `oauth_consumer_key` and `shared_secret` (both of type `String`). These can be any values. I recommend to use the admin's email for `oauth_consumer_key` and a random password for `shared_secret`. From 8bcf1f13e3443d063a6d5ef2b56eda68a5d74c21 Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Thu, 16 Jun 2022 11:45:19 +0200 Subject: [PATCH 019/217] AWS CLI build instructions --- build-instructions.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/build-instructions.md b/build-instructions.md index 9c988762..0b8b7ea7 100644 --- a/build-instructions.md +++ b/build-instructions.md @@ -303,7 +303,7 @@ Kill both containers by running this command in another terminal: Cloud Provider Tools -------------------- -Install Google Cloud CLI for linux or [follow the instruction for your environment](https://cloud.google.com/sdk/docs/install#linux) +Install Google Cloud CLI for Linux or [follow the instruction for your environment](https://cloud.google.com/sdk/docs/install#linux) Open a terminal and download the Google Cloud SDK ``` @@ -415,7 +415,7 @@ aws iam create-access-key --user-name $USERNAME # IMPORTANT: Record AccessKeyId and SecretAccessKey -cat < policy1.json +cat < CodeCheckS3.json { "Version": "2012-10-17", "Statement": [ @@ -425,7 +425,7 @@ cat < policy1.json "s3:*" ], "Resource": [ - "arn:aws:s3:::ext.codecheck-test.org" + "arn:aws:s3:::ext.$SUFFIX" ] }, { @@ -434,14 +434,14 @@ cat < policy1.json "s3:*" ], "Resource": [ - "arn:aws:s3:::ext.codecheck-test.org/*" + "arn:aws:s3:::ext.$SUFFIX/*" ] } ] } EOF -aws iam create-policy --policy-name CodeCheckTestS3 --policy-document file://./policy1.json +aws iam create-policy --policy-name CodeCheckS3 --policy-document file://./CodeCheckS3.json ``` If you use CodeCheck with LTI, you need to set up an Amazon Dynamo database. Create the following tables: @@ -491,9 +491,9 @@ aws --region $REGION dynamodb create-table \ ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) aws iam attach-user-policy --user-name $USERNAME \ - --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/CodeCheckTestS3 + --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/CodeCheckS3 -cat < policy2.json +cat < CodeCheckDynamo.json { "Version": "2012-10-17", "Statement": [ @@ -519,10 +519,10 @@ cat < policy2.json } EOF -aws iam create-policy --policy-name CodeCheckTestDynamo --policy-document file://./policy2.json +aws iam create-policy --policy-name CodeCheckDynamo --policy-document file://./CodeCheckDynamo.json aws iam attach-user-policy --user-name $USERNAME \ - --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/CodeCheckTestDynamo + --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/CodeCheckDynamo aws iam list-attached-user-policies --user-name $USERNAME ``` From 1b6d31302c624148b7f207a08b8d73313feb7eb9 Mon Sep 17 00:00:00 2001 From: Sergio Rojas <48057303+hisergiorojas@users.noreply.github.com> Date: Thu, 16 Jun 2022 18:32:38 +0000 Subject: [PATCH 020/217] Change prefix base on reqest --- app/controllers/Upload.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/controllers/Upload.java b/app/controllers/Upload.java index 272fe895..f9fd5bca 100644 --- a/app/controllers/Upload.java +++ b/app/controllers/Upload.java @@ -156,7 +156,15 @@ private String checkProblem(Http.Request request, String problem, Map"); response.append(""); - String prefix = (request.secure() ? "https://" : "http://") + request.host() + "/"; + + String prefix; + if(request.host().equals("localhost")) { + prefix = "../"; + } + else { + prefix = (request.secure() ? "https://" : "http://") + request.host() + "/"; + } + String problemUrl = prefix + type + "/" + problem; response.append("Public URL (for your students): "); response.append("" + problemUrl + ""); From 6e07f3d30cb30ba437fa63f530dae0f0b62b8441 Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Wed, 29 Jun 2022 21:35:52 +0200 Subject: [PATCH 021/217] ... --- build-instructions.md | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/build-instructions.md b/build-instructions.md index 0b8b7ea7..37b5c4e6 100644 --- a/build-instructions.md +++ b/build-instructions.md @@ -242,6 +242,8 @@ needed. ## Docker Installation +Skip this step if you are on Codespaces. Codespaces already has Docker installed. + Install Docker for Linux (deb) or [follow the instruction for your environment](https://docs.docker.com/engine/install/) ``` sudo apt-get update @@ -274,11 +276,11 @@ Docker Local Testing Build and run the Docker container for the `comrun` service: docker build --tag codecheck:1.0-SNAPSHOT comrun - docker run -p 8080:8080 -it codecheck:1.0-SNAPSHOT + docker run -p 8080:8080 -it codecheck:1.0-SNAPSHOT & Test that it works: - /opt/codecheck/codecheck -l samples/java/example1 & + /opt/codecheck/codecheck -lt samples/java/example1 Create a file `conf/production.conf` holding an [application secret](https://www.playframework.com/documentation/2.8.x/ApplicationSecret): @@ -401,12 +403,13 @@ Alternatively, you can test with the locally running web app. In Play Server Deployment ---------------------- -In Amazon S3, create a bucket whose name starts with the four characters `ext.` and an arbitrary suffix, such as `ext.mydomain.com` to hold -the uploaded CodeCheck problems. Set the ACL so that the bucket owner has all access rights and nobody else has any. +Set environment variables and create a user in your Amazon AWS account: ``` -SUFFIX=mydomain.com -aws s3 mb s3://ext.$SUFFIX +ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) +echo Account ID: $ACCOUNT_ID +REGION=$(aws configure get region) +echo Region: $REGION USERNAME=codecheck @@ -414,6 +417,16 @@ aws iam create-user --user-name $USERNAME aws iam create-access-key --user-name $USERNAME # IMPORTANT: Record AccessKeyId and SecretAccessKey +``` + +In Amazon S3, create a bucket whose name starts with the four characters `ext.` and an arbitrary suffix, such as `ext.mydomain.com` to hold +the uploaded CodeCheck problems. Set the ACL so that the bucket owner has all access rights and nobody else has any. + +``` +# Change the suffix below +SUFFIX=mydomain.com + +aws s3 mb s3://ext.$SUFFIX cat < CodeCheckS3.json { @@ -442,6 +455,9 @@ cat < CodeCheckS3.json EOF aws iam create-policy --policy-name CodeCheckS3 --policy-document file://./CodeCheckS3.json + +aws iam attach-user-policy --user-name $USERNAME \ + --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/CodeCheckS3 ``` If you use CodeCheck with LTI, you need to set up an Amazon Dynamo database. Create the following tables: @@ -488,11 +504,6 @@ aws --region $REGION dynamodb create-table \ --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 -ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) - -aws iam attach-user-policy --user-name $USERNAME \ - --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/CodeCheckS3 - cat < CodeCheckDynamo.json { "Version": "2012-10-17", @@ -530,8 +541,9 @@ aws iam list-attached-user-policies --user-name $USERNAME You need to populate the `CodeCheckLTICredentials` table with at least one pair `oauth_consumer_key` and `shared_secret` (both of type `String`). These can be any values. I recommend to use the admin's email for `oauth_consumer_key` and a random password for `shared_secret`. ``` -USERNAME=... -PASSWORD=... +USERNAME=codecheck +PASSWORD=$(strings /dev/urandom | grep -E '[^ ]{8}' | head -1) +echo Password: $PASSWORD aws dynamodb put-item --table-name CodeCheckLTICredentials --item '{"oauth_consumer_key":{"S":"'${USERNAME}'"},"shared_secret":{"S":"'${PASSWORD}'"}}' ``` From 513aa210506a12942131739c5d694cdc59efc5ba Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Fri, 1 Jul 2022 07:17:24 +0200 Subject: [PATCH 022/217] build instructions for docker on codespaces --- build-instructions.md | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/build-instructions.md b/build-instructions.md index 37b5c4e6..b848c3ea 100644 --- a/build-instructions.md +++ b/build-instructions.md @@ -293,12 +293,33 @@ Do not check this file into version control! Build and run the Docker container for the `play-codecheck` server: sbt docker:publishLocal - docker run -p 9090:9000 -it --add-host host.docker.internal:host-gateway play-codecheck:1.0-SNAPSHOT + docker run -p 9090:9000 -it --add-host host.docker.internal:host-gateway play-codecheck:1.0-SNAPSHOT & + +(Ignore the `[error]` labels during the Docker build. They aren't actually errors.) Test that it works by pointing your browser to -. Upload a problem. +. Or if you use CodeSpaces, locate the Ports tab and open the local address for port 9090. Ignore the nginx error and paste `/assets/uploadProblem.html` after the URL. + +Upload a problem: File name `Numbers.java`, file contents: + +``` +public class Numbers +{ +//CALL 3, 4 +//CALL -3, 3 +//CALL 3, 0 + public double average(int x, int y) + { + //HIDE + return 0.5 * (x + y); + //SHOW // Compute the average of x and y + } +} +``` + +Click the Submit Files button. You should see three passing test cases. -Kill both containers by running this command in another terminal: +Kill both containers by running this command in the terminal: docker container kill $(docker ps -q) From 16c8f1df0f6000a9d4cda8fb19c2331bc517bfe4 Mon Sep 17 00:00:00 2001 From: Oscar Diaz Vega Date: Wed, 6 Jul 2022 17:48:20 +0000 Subject: [PATCH 023/217] Added checkUIDForBadWords and slightly adjusted createPronouncableUID. --- app/com/horstmann/codecheck/Util.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/app/com/horstmann/codecheck/Util.java b/app/com/horstmann/codecheck/Util.java index 61027391..bdd43b5e 100644 --- a/app/com/horstmann/codecheck/Util.java +++ b/app/com/horstmann/codecheck/Util.java @@ -554,6 +554,8 @@ private static String datePrefix() { private static String consonants = "bcdfghjklmnpqrstvwxz"; public static String createPronouncableUID() { + String UID = ""; + do{ StringBuilder result = new StringBuilder(); int len = 16; int b = Util.generator.nextInt(2); @@ -566,7 +568,22 @@ public static String createPronouncableUID() { b = Util.generator.nextInt(2); } } - return result.toString(); + UID = result.toString(); + } while(checkUIDForBadWords(UID)); // loop will run again if the UID contains any bad words in it + return UID; + } + private static boolean checkUIDForBadWords(String s){ + //This function returns true if any bad word is present in the generated UID passed in as a parameter + String [] filteredOutWords = {"anal", "anus", "anil", "anes", "anis", "babe", "bozo", "coky", "dick", "dike", "dyke", "homo", "lube", "nude", "oral", "rape", "sexy", "titi", "wily"}; + String[] isolateIDWords = s.split("-"); + for(String word : isolateIDWords){ + //if the current word is in the array of bad words, then bad word is present in ID + boolean badWordPresent = Arrays.asList(filteredOutWords).contains(word); + if(badWordPresent){ + return true; + } + } + return false; } public static boolean isPronouncableUID(String s) { From 0fb264d21e624e4d8eea54e8a96819fefc9daa97 Mon Sep 17 00:00:00 2001 From: Oscar Diaz Vega Date: Mon, 11 Jul 2022 19:46:00 +0000 Subject: [PATCH 024/217] Made improvements to createPronouncableUID() and created another helper function named generateNewWord(). --- app/com/horstmann/codecheck/Util.java | 54 +++++++++++++++++++-------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/app/com/horstmann/codecheck/Util.java b/app/com/horstmann/codecheck/Util.java index bdd43b5e..039917f5 100644 --- a/app/com/horstmann/codecheck/Util.java +++ b/app/com/horstmann/codecheck/Util.java @@ -554,36 +554,58 @@ private static String datePrefix() { private static String consonants = "bcdfghjklmnpqrstvwxz"; public static String createPronouncableUID() { - String UID = ""; - do{ StringBuilder result = new StringBuilder(); int len = 16; int b = Util.generator.nextInt(2); + int startWordIndex = 0; for (int i = 0; i < len; i++) { String s = i % 2 == b ? Util.consonants : vowels; int n = Util.generator.nextInt(s.length()); result.append(s.charAt(n)); if (i % 4 == 3 && i < len - 1) { result.append('-'); + String word = result.substring(startWordIndex, result.indexOf("-", startWordIndex)); // create a substring for each word in the id + if(checkUIDForBadWords(word)){ // if the word is a bad word then we need to generate a new word for the id + while (true){ // continue to generate new word until we get a non-bad word + word = generateNewWord(); + if(!checkUIDForBadWords(word)) { // if the new generated word isn't a bad word, then we put the new word in the id + result.replace(startWordIndex, result.indexOf("-", startWordIndex), word); // replace the bad word with a new word + break; + } + } + } + startWordIndex = result.indexOf("-",startWordIndex) + 1; // update start index for next word b = Util.generator.nextInt(2); } } - UID = result.toString(); - } while(checkUIDForBadWords(UID)); // loop will run again if the UID contains any bad words in it - return UID; - } - private static boolean checkUIDForBadWords(String s){ - //This function returns true if any bad word is present in the generated UID passed in as a parameter - String [] filteredOutWords = {"anal", "anus", "anil", "anes", "anis", "babe", "bozo", "coky", "dick", "dike", "dyke", "homo", "lube", "nude", "oral", "rape", "sexy", "titi", "wily"}; - String[] isolateIDWords = s.split("-"); - for(String word : isolateIDWords){ - //if the current word is in the array of bad words, then bad word is present in ID - boolean badWordPresent = Arrays.asList(filteredOutWords).contains(word); - if(badWordPresent){ - return true; + String finalWord = result.substring(result.length() - 4); // we need to manually check the last word outside of the loop + if(checkUIDForBadWords(finalWord)){ // if the final word is a bad word then we need to generate a new word for the id + while (true){ // continue to generate new word until we get a non-bad word + finalWord = generateNewWord(); + if(!checkUIDForBadWords(finalWord)) { // if the new generated word isn't a bad word, then we put the new word in the id + result.replace(result.length() - 4, result.length(), finalWord); // replace the bad word with a new word + break; + } } } - return false; + return result.toString(); + } + private static String generateNewWord(){ // this function generates a four-letter word for the UID + //THIS FUNCTION SHOULD ONLY BE CALLED WHEN A BAD WORD EXISTS IN THE UID + StringBuilder word = new StringBuilder(); + int len = 4; + int b = Util.generator.nextInt(2); + for (int i = 0; i < len; i++) { + String s = i % 2 == b ? Util.consonants : vowels; + int n = Util.generator.nextInt(s.length()); + word.append(s.charAt(n)); + } + return word.toString(); + } + private static boolean checkUIDForBadWords(String idWord){ + //This function returns true if any bad word is present in the generated UID passed in as a parameter + String[] filteredOutWords = {"anal", "anus", "anil", "anes", "anis", "babe", "bozo", "coky", "dick", "dike", "dyke", "homo", "lube", "nude", "oral", "rape", "sexy", "titi", "wily"}; + return Arrays.asList(filteredOutWords).contains(idWord); } public static boolean isPronouncableUID(String s) { From 0a32d132fa051f78a218488da99a5b5e7f414405 Mon Sep 17 00:00:00 2001 From: Oscar Diaz Vega Date: Tue, 12 Jul 2022 19:09:14 +0000 Subject: [PATCH 025/217] Updated generateNewWord() so that the checking for bad words occurs in this helper function, thus making createPronouncableUID() simpler. I also made it so that createPronouncableUID() calls generateNewWord() four times to generate the UID. --- app/com/horstmann/codecheck/Util.java | 50 +++++++++------------------ 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/app/com/horstmann/codecheck/Util.java b/app/com/horstmann/codecheck/Util.java index 039917f5..3f0ac4ef 100644 --- a/app/com/horstmann/codecheck/Util.java +++ b/app/com/horstmann/codecheck/Util.java @@ -555,52 +555,34 @@ private static String datePrefix() { public static String createPronouncableUID() { StringBuilder result = new StringBuilder(); - int len = 16; - int b = Util.generator.nextInt(2); - int startWordIndex = 0; - for (int i = 0; i < len; i++) { - String s = i % 2 == b ? Util.consonants : vowels; - int n = Util.generator.nextInt(s.length()); - result.append(s.charAt(n)); - if (i % 4 == 3 && i < len - 1) { - result.append('-'); - String word = result.substring(startWordIndex, result.indexOf("-", startWordIndex)); // create a substring for each word in the id - if(checkUIDForBadWords(word)){ // if the word is a bad word then we need to generate a new word for the id - while (true){ // continue to generate new word until we get a non-bad word - word = generateNewWord(); - if(!checkUIDForBadWords(word)) { // if the new generated word isn't a bad word, then we put the new word in the id - result.replace(startWordIndex, result.indexOf("-", startWordIndex), word); // replace the bad word with a new word - break; - } - } - } - startWordIndex = result.indexOf("-",startWordIndex) + 1; // update start index for next word - b = Util.generator.nextInt(2); - } - } - String finalWord = result.substring(result.length() - 4); // we need to manually check the last word outside of the loop - if(checkUIDForBadWords(finalWord)){ // if the final word is a bad word then we need to generate a new word for the id - while (true){ // continue to generate new word until we get a non-bad word - finalWord = generateNewWord(); - if(!checkUIDForBadWords(finalWord)) { // if the new generated word isn't a bad word, then we put the new word in the id - result.replace(result.length() - 4, result.length(), finalWord); // replace the bad word with a new word - break; - } + int len = 4; + for(int i = 0; i < len; i++){ + result.append(generateNewWord()); + if(i != 3){ // we only want three dashes in the UID + result.append("-"); } } return result.toString(); } private static String generateNewWord(){ // this function generates a four-letter word for the UID - //THIS FUNCTION SHOULD ONLY BE CALLED WHEN A BAD WORD EXISTS IN THE UID StringBuilder word = new StringBuilder(); int len = 4; int b = Util.generator.nextInt(2); - for (int i = 0; i < len; i++) { + for(int i = 0; i < len; i++) { String s = i % 2 == b ? Util.consonants : vowels; int n = Util.generator.nextInt(s.length()); word.append(s.charAt(n)); } - return word.toString(); + //Check to see if the generated word is a bad word + if(checkUIDForBadWords(word.toString())) { // if the word is a bad word then we need to generate a new word for the id + while(true) { // continue to generate new word until we get a non-bad word + word = generateNewWord(); + if(!checkUIDForBadWords(word.toString())) { // if the new generated word isn't a bad word, then we have a valid word + break; + } + } + } + return word; } private static boolean checkUIDForBadWords(String idWord){ //This function returns true if any bad word is present in the generated UID passed in as a parameter From fafed82a5cf560fb4a10bddb6c6ade1af5c12d97 Mon Sep 17 00:00:00 2001 From: Oscar Diaz Vega Date: Tue, 12 Jul 2022 19:17:02 +0000 Subject: [PATCH 026/217] Changed generateNewWord() to return a StringBuilder rather than a String. --- app/com/horstmann/codecheck/Util.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/com/horstmann/codecheck/Util.java b/app/com/horstmann/codecheck/Util.java index 3f0ac4ef..8cc8fe84 100644 --- a/app/com/horstmann/codecheck/Util.java +++ b/app/com/horstmann/codecheck/Util.java @@ -564,7 +564,7 @@ public static String createPronouncableUID() { } return result.toString(); } - private static String generateNewWord(){ // this function generates a four-letter word for the UID + private static StringBuilder generateNewWord(){ // this function generates a four-letter word for the UID StringBuilder word = new StringBuilder(); int len = 4; int b = Util.generator.nextInt(2); From 071f5847af8b92715b6a5443c428ba215b34fc8c Mon Sep 17 00:00:00 2001 From: Oscar Diaz Vega Date: Wed, 13 Jul 2022 18:04:21 +0000 Subject: [PATCH 027/217] Made generateNewWord() simpler by removing revursive call and only calling isBadWord() once per word generation. --- app/com/horstmann/codecheck/Util.java | 28 +++++++++++---------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/app/com/horstmann/codecheck/Util.java b/app/com/horstmann/codecheck/Util.java index 8cc8fe84..89c9bacd 100644 --- a/app/com/horstmann/codecheck/Util.java +++ b/app/com/horstmann/codecheck/Util.java @@ -565,29 +565,23 @@ public static String createPronouncableUID() { return result.toString(); } private static StringBuilder generateNewWord(){ // this function generates a four-letter word for the UID - StringBuilder word = new StringBuilder(); + StringBuilder word; int len = 4; int b = Util.generator.nextInt(2); - for(int i = 0; i < len; i++) { - String s = i % 2 == b ? Util.consonants : vowels; - int n = Util.generator.nextInt(s.length()); - word.append(s.charAt(n)); - } - //Check to see if the generated word is a bad word - if(checkUIDForBadWords(word.toString())) { // if the word is a bad word then we need to generate a new word for the id - while(true) { // continue to generate new word until we get a non-bad word - word = generateNewWord(); - if(!checkUIDForBadWords(word.toString())) { // if the new generated word isn't a bad word, then we have a valid word - break; - } + do{ + word = new StringBuilder(); + for(int i = 0; i < len; i++) { // this loop generates a four-letter word for the id + String s = i % 2 == b ? Util.consonants : vowels; + int n = Util.generator.nextInt(s.length()); + word.append(s.charAt(n)); } - } + } while(isBadWord(word.toString())); // generate a word until we get a non bad word + return word; } - private static boolean checkUIDForBadWords(String idWord){ - //This function returns true if any bad word is present in the generated UID passed in as a parameter + private static boolean isBadWord(String word){ String[] filteredOutWords = {"anal", "anus", "anil", "anes", "anis", "babe", "bozo", "coky", "dick", "dike", "dyke", "homo", "lube", "nude", "oral", "rape", "sexy", "titi", "wily"}; - return Arrays.asList(filteredOutWords).contains(idWord); + return Arrays.asList(filteredOutWords).contains(word); } public static boolean isPronouncableUID(String s) { From 6588b24d4d5c7b36d880e2dbdde256978ee63ced Mon Sep 17 00:00:00 2001 From: thinhngocnguyen <59405079+thinh241819@users.noreply.github.com> Date: Thu, 14 Jul 2022 11:17:45 -0700 Subject: [PATCH 028/217] Modified readDescription() to remove relative links from problem statements --- app/com/horstmann/codecheck/Problem.java | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/app/com/horstmann/codecheck/Problem.java b/app/com/horstmann/codecheck/Problem.java index dc908c55..16f9aafc 100644 --- a/app/com/horstmann/codecheck/Problem.java +++ b/app/com/horstmann/codecheck/Problem.java @@ -201,6 +201,38 @@ private String readDescription(String descriptionFile) { if (start != -1) result.replace(0, start, ""); + // Check if links are relative or not, if relative links, change it to normal text + Pattern linkPattern = Pattern.compile("[<]\\s*[aA].*[aA]\\s*[>]"); + Pattern hrefPattern = Pattern.compile("[hH][rR][eE][fF]\\s*[=]\\s*['\"]"); + Pattern linkText = Pattern.compile(">.*<"); + Matcher linkMatcher = linkPattern.matcher(result); + int startLink = 0; + int endLink = 0; + while(linkMatcher.find(startLink)) { + startLink = linkMatcher.start(); + endLink = linkMatcher.end(); + String theLink = result.substring(startLink, endLink); + // Find Href and check if HTTP or HTTPS + Matcher hrefMatcher = hrefPattern.matcher(theLink); + int endHref = 0; + if(hrefMatcher.find()) + endHref = hrefMatcher.end(); + String hrefLink = theLink.substring(endHref); + if(!(hrefLink.startsWith("http://") || hrefLink.startsWith("https://"))) { + Matcher contentMatcher = linkText.matcher(theLink); + int startContent = 0; + int endContent = 0; + if(contentMatcher.find()) + startContent = contentMatcher.start(); + endContent = contentMatcher.end(); + String contentOfLink = theLink.substring(startContent + 1, endContent - 1); + result.replace(startLink, endLink, contentOfLink); + startLink += contentOfLink.length(); + } + else + startLink += theLink.length(); + } + Matcher matcher = IMG_PATTERN.matcher(result); start = 0; while (matcher.find(start)) { From 88cfea717309e78d5e3bd8c685688a0e9ddf11c7 Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Sat, 16 Jul 2022 08:17:01 +0200 Subject: [PATCH 029/217] ... --- build-instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-instructions.md b/build-instructions.md index b848c3ea..7083a01b 100644 --- a/build-instructions.md +++ b/build-instructions.md @@ -376,7 +376,7 @@ Configure the AWS CLI [instructions](https://docs.aws.amazon.com/cli/latest/user aws configure ``` -Comrun Service Deployment {#service-deployment} +Comrun Service Deployment ------------------------- There are two parts to the CodeCheck server. We\'ll take them up one at From a59def60a411d42a491d2331290d3ac8b90f5e78 Mon Sep 17 00:00:00 2001 From: thinhngocnguyen <59405079+thinh241819@users.noreply.github.com> Date: Sat, 16 Jul 2022 22:07:37 -0700 Subject: [PATCH 030/217] update the algorithm to replace relative links to be more accurate --- app/com/horstmann/codecheck/Problem.java | 35 ++++++++++-------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/app/com/horstmann/codecheck/Problem.java b/app/com/horstmann/codecheck/Problem.java index 16f9aafc..74f324ff 100644 --- a/app/com/horstmann/codecheck/Problem.java +++ b/app/com/horstmann/codecheck/Problem.java @@ -79,6 +79,9 @@ public static class DisplayData { private static final Pattern IMG_PATTERN = Pattern .compile("[<]\\s*[iI][mM][gG]\\s*[sS][rR][cC]\\s*[=]\\s*['\"]([^'\"]*)['\"][^>]*[>]"); + private static final Pattern LINK_START = Pattern + .compile("<\\s*[aA]\\s+[^>]*[hH][rR][eE][fF]\\s*=\\s*['\"]([^'\"]+)['\"][^>]*>"); + private static final Pattern LINK_END = Pattern.compile("<\\s*/\\s*[aA]\\s*>"); public Problem(Map problemFiles) throws IOException { this.problemFiles = problemFiles; @@ -202,35 +205,25 @@ private String readDescription(String descriptionFile) { result.replace(0, start, ""); // Check if links are relative or not, if relative links, change it to normal text - Pattern linkPattern = Pattern.compile("[<]\\s*[aA].*[aA]\\s*[>]"); - Pattern hrefPattern = Pattern.compile("[hH][rR][eE][fF]\\s*[=]\\s*['\"]"); - Pattern linkText = Pattern.compile(">.*<"); - Matcher linkMatcher = linkPattern.matcher(result); + //Pattern linkPattern = Pattern.compile("[<]\\s*[aA].*[aA]\\s*[>]"); + Matcher linkMatcherStart = LINK_START.matcher(result); + Matcher linkMatcherEnd = LINK_END.matcher(result); int startLink = 0; int endLink = 0; - while(linkMatcher.find(startLink)) { - startLink = linkMatcher.start(); - endLink = linkMatcher.end(); - String theLink = result.substring(startLink, endLink); + while(linkMatcherStart.find(startLink) && linkMatcherEnd.find(startLink)) { + startLink = linkMatcherStart.start(); + endLink = linkMatcherEnd.end(); // Find Href and check if HTTP or HTTPS - Matcher hrefMatcher = hrefPattern.matcher(theLink); - int endHref = 0; - if(hrefMatcher.find()) - endHref = hrefMatcher.end(); - String hrefLink = theLink.substring(endHref); + String hrefLink = result.substring(linkMatcherStart.start(1), linkMatcherStart.end(1)).toLowerCase(); if(!(hrefLink.startsWith("http://") || hrefLink.startsWith("https://"))) { - Matcher contentMatcher = linkText.matcher(theLink); - int startContent = 0; - int endContent = 0; - if(contentMatcher.find()) - startContent = contentMatcher.start(); - endContent = contentMatcher.end(); - String contentOfLink = theLink.substring(startContent + 1, endContent - 1); + int startContent = linkMatcherStart.end(); + int endContent = linkMatcherEnd.start(); + String contentOfLink = result.substring(startContent, endContent); result.replace(startLink, endLink, contentOfLink); startLink += contentOfLink.length(); } else - startLink += theLink.length(); + startLink += endLink - startLink; } Matcher matcher = IMG_PATTERN.matcher(result); From 856231218e41fcb501e24fdde86c70c101a22e30 Mon Sep 17 00:00:00 2001 From: thinhngocnguyen <59405079+thinh241819@users.noreply.github.com> Date: Sat, 16 Jul 2022 22:15:44 -0700 Subject: [PATCH 031/217] removing unnecessary comments --- app/com/horstmann/codecheck/Problem.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/com/horstmann/codecheck/Problem.java b/app/com/horstmann/codecheck/Problem.java index 74f324ff..1ea4c51e 100644 --- a/app/com/horstmann/codecheck/Problem.java +++ b/app/com/horstmann/codecheck/Problem.java @@ -205,7 +205,6 @@ private String readDescription(String descriptionFile) { result.replace(0, start, ""); // Check if links are relative or not, if relative links, change it to normal text - //Pattern linkPattern = Pattern.compile("[<]\\s*[aA].*[aA]\\s*[>]"); Matcher linkMatcherStart = LINK_START.matcher(result); Matcher linkMatcherEnd = LINK_END.matcher(result); int startLink = 0; @@ -213,7 +212,6 @@ private String readDescription(String descriptionFile) { while(linkMatcherStart.find(startLink) && linkMatcherEnd.find(startLink)) { startLink = linkMatcherStart.start(); endLink = linkMatcherEnd.end(); - // Find Href and check if HTTP or HTTPS String hrefLink = result.substring(linkMatcherStart.start(1), linkMatcherStart.end(1)).toLowerCase(); if(!(hrefLink.startsWith("http://") || hrefLink.startsWith("https://"))) { int startContent = linkMatcherStart.end(); From 1e7a51b39b557bfad03cc21469856f09abf5ac80 Mon Sep 17 00:00:00 2001 From: Anmol Gill Date: Mon, 18 Jul 2022 19:09:31 +0000 Subject: [PATCH 032/217] Tester/X-Unit, SUB, and CALL work with HIDDEN --- app/com/horstmann/codecheck/Annotations.java | 13 ++++++++++++- app/com/horstmann/codecheck/AsExpected.java | 13 ++++++++++++- app/com/horstmann/codecheck/Calls.java | 10 ++++++++++ app/com/horstmann/codecheck/Language.java | 5 +++-- app/com/horstmann/codecheck/Main.java | 17 +++++++++++++---- app/com/horstmann/codecheck/Substitution.java | 10 ++++++++++ 6 files changed, 60 insertions(+), 8 deletions(-) diff --git a/app/com/horstmann/codecheck/Annotations.java b/app/com/horstmann/codecheck/Annotations.java index e4887a89..34b7ad61 100644 --- a/app/com/horstmann/codecheck/Annotations.java +++ b/app/com/horstmann/codecheck/Annotations.java @@ -13,7 +13,7 @@ public class Annotations { public static final Set VALID_ANNOTATIONS = Set.of( - "HIDE", "SHOW", "EDIT", "SOLUTION", "CALL", "SUB", "ID", "SAMPLE", "ARGS", + "HIDDEN", "HIDE", "SHOW", "EDIT", "SOLUTION", "CALL", "SUB", "ID", "SAMPLE", "ARGS", "IN", "OUT", "TIMEOUT", "TOLERANCE", "IGNORECASE", "IGNORESPACE", "MAXOUTPUTLEN", "REQUIRED", "FORBIDDEN", "SCORING", "INTERLEAVE", "TILE", "FIXED", "OR", "PSEUDO"); public static final Set NON_BLANK_BEFORE_OK = Set.of("SUB", "PSEUDO"); @@ -56,6 +56,7 @@ public static Annotation parse(String line, String start, String end) { private Set keys = new HashSet<>(); private Set solutions = new TreeSet<>(); private Set hidden = new TreeSet<>(); + private Set hiddenTests = new TreeSet<>(); public Annotations(Language language) { this.language = language; @@ -88,6 +89,8 @@ private void read(Path p, byte[] contents) { solutions.add(p); if (a.key.equals("HIDE")) hidden.add(p); + if (a.key.equals("HIDDEN")) + hiddenTests.add(p); a.path = p; annotations.add(a); } @@ -102,6 +105,10 @@ public Set getHidden() { return Collections.unmodifiableSet(hidden); } + public Set getHiddenTests() { + return Collections.unmodifiableSet(hiddenTests); + } + public String findUniqueKey(String key) { Annotation match = null; for (Annotation a : annotations) { @@ -187,6 +194,8 @@ public Substitution findSubstitution() { for (Annotation a : annotations) { if (a.key.equals("SUB")) sub.addVariable(a.path, a.before, a.args); + if (getHiddenTests().contains(a.path)) // OR (a.key.equals("HIDDEN")) + sub.setHidden(true); } return sub; } @@ -208,6 +217,8 @@ public Calls findCalls() { for (Annotation a : annotations) { if (a.key.equals("CALL")) calls.addCall(a.path, a.args, a.next); + if (getHiddenTests().contains(a.path)) // OR (a.key.equals("HIDDEN")) + calls.setHidden(true); } return calls; } diff --git a/app/com/horstmann/codecheck/AsExpected.java b/app/com/horstmann/codecheck/AsExpected.java index 95ca4fa3..a084739e 100644 --- a/app/com/horstmann/codecheck/AsExpected.java +++ b/app/com/horstmann/codecheck/AsExpected.java @@ -10,6 +10,7 @@ public class AsExpected { private Comparison comp; + private boolean hidden = false; public AsExpected(Comparison comp) { this.comp = comp; @@ -19,6 +20,15 @@ public AsExpected(Comparison comp) { // test cases have not occurred. // Need to count the number of expected cases in the file + public boolean isHidden() { + return hidden; + } + + public void setHidden(boolean value) + { + hidden = value; + } + public static int expectedTests(String contents) { // TODO: Shouldn't this be in Language? Pattern expecteds = Pattern.compile("Expected: "); // TODO: What if this is commented out? Matcher matcher = expecteds.matcher(contents); @@ -66,7 +76,8 @@ public void eval(String value, Report report, Score score, String tester) { report.output(value); report.error("Missing \"Expected: ...\"\n"); } else { - report.output(lines, matches, mismatches); + if (hidden == false) + report.output(lines, matches, mismatches); } // If the program run ends in an exception, it can happen that the number of // actual vs. expected values is less than it should be. In that case, diff --git a/app/com/horstmann/codecheck/Calls.java b/app/com/horstmann/codecheck/Calls.java index e0f05fc3..5cf74f9e 100644 --- a/app/com/horstmann/codecheck/Calls.java +++ b/app/com/horstmann/codecheck/Calls.java @@ -18,6 +18,7 @@ public class Call { private List calls = new ArrayList<>(); private Path file; private int lastGroup = -1; + private boolean hidden = false; public Calls(Language language) { this.language = language; @@ -27,6 +28,15 @@ public Path getFile() { return file; } + public boolean isHidden() { + return hidden; + } + + public void setHidden(boolean value) + { + hidden = value; + } + public int getSize() { return calls.size(); } diff --git a/app/com/horstmann/codecheck/Language.java b/app/com/horstmann/codecheck/Language.java index 420bccba..86c6e044 100644 --- a/app/com/horstmann/codecheck/Language.java +++ b/app/com/horstmann/codecheck/Language.java @@ -188,8 +188,9 @@ default Map writeTester(Path file, String contents, List submissionFiles, Substitution sub actual[i] = Util.truncate(actual[i], expected[i].length() + MUCH_LONGER); score.pass(outcomes[i], null); // Pass/fail shown in run table } - report.runTable(null, argNames, args, actual, expected, outcomes); + if (sub.isHidden() == false) + report.runTable(null, argNames, args, actual, expected, outcomes); }); } @@ -168,7 +169,8 @@ private void doCalls(Calls calls, ResourceLoader resourceLoader) throws Exceptio } score.pass(outcomes[i], null /* no report--it's in the table */); } - report.runTable(names, new String[] { "Arguments" }, args, actual, expected, outcomes); + if (calls.isHidden() == false) + report.runTable(names, new String[] { "Arguments" }, args, actual, expected, outcomes); }); } @@ -188,7 +190,11 @@ private void runUnitTests() { report.run(p.toString()); if (!plan.checkCompiled(id, report, score)) return; String outerr = plan.outerr(id); - problem.getLanguage().reportUnitTest(outerr, report, score); + if (problem.getAnnotations().getHiddenTests().contains(p)) + problem.getLanguage().reportUnitTest(outerr, report, score, true); + else + problem.getLanguage().reportUnitTest(outerr, report, score, false); + }); } @@ -205,6 +211,8 @@ private void runTester(Path mainFile, int timeout, int maxOutputLen) throws Exce String outerr = plan.outerr(compileID); AsExpected cond = new AsExpected(comp); String tester = plan.getFileString("use", mainFile); + if (problem.getAnnotations().getHiddenTests().contains(mainFile)) + cond.setHidden(true); if (tester == null) tester = plan.getFileString("solution", mainFile); // In case the student was asked to do it cond.eval(outerr, report, score, tester); @@ -436,8 +444,9 @@ else if ("NJS".equals(reportType)) "*.jar", "*.pdf"); printFiles.removeAll(problem.getAnnotations().getHidden()); + printFiles.removeAll(problem.getAnnotations().getHiddenTests()); printFiles.removeAll(problem.getSolutionFiles().keySet()); - + if (problem.getAnnotations().checkConditions(submissionFiles, report)) { if (problem.getAnnotations().has("CALL")) { Calls calls = problem.getAnnotations().findCalls(); diff --git a/app/com/horstmann/codecheck/Substitution.java b/app/com/horstmann/codecheck/Substitution.java index a7edc984..e149c298 100644 --- a/app/com/horstmann/codecheck/Substitution.java +++ b/app/com/horstmann/codecheck/Substitution.java @@ -14,11 +14,21 @@ public class Substitution { private Map> subs = new LinkedHashMap<>(); private int size; private Language language; + private boolean hidden = false; public Substitution(Language language) { this.language = language; } + public boolean isHidden() { + return hidden; + } + + public void setHidden(boolean value) + { + hidden = value; + } + public void addVariable(Path file, String decl, String args) { if (this.file == null) this.file = file; From 11c085c37f80536bd2ae518d9651aa20a8cd1fa9 Mon Sep 17 00:00:00 2001 From: Anmol Gill Date: Thu, 21 Jul 2022 17:07:44 +0000 Subject: [PATCH 033/217] Made it so SUB does not work with HIDDEN --- app/com/horstmann/codecheck/Annotations.java | 2 -- app/com/horstmann/codecheck/Main.java | 3 +-- app/com/horstmann/codecheck/Substitution.java | 10 ---------- 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/app/com/horstmann/codecheck/Annotations.java b/app/com/horstmann/codecheck/Annotations.java index 34b7ad61..5c929707 100644 --- a/app/com/horstmann/codecheck/Annotations.java +++ b/app/com/horstmann/codecheck/Annotations.java @@ -194,8 +194,6 @@ public Substitution findSubstitution() { for (Annotation a : annotations) { if (a.key.equals("SUB")) sub.addVariable(a.path, a.before, a.args); - if (getHiddenTests().contains(a.path)) // OR (a.key.equals("HIDDEN")) - sub.setHidden(true); } return sub; } diff --git a/app/com/horstmann/codecheck/Main.java b/app/com/horstmann/codecheck/Main.java index 333b0521..308090bb 100644 --- a/app/com/horstmann/codecheck/Main.java +++ b/app/com/horstmann/codecheck/Main.java @@ -110,8 +110,7 @@ private void doSubstitutions(Map submissionFiles, Substitution sub actual[i] = Util.truncate(actual[i], expected[i].length() + MUCH_LONGER); score.pass(outcomes[i], null); // Pass/fail shown in run table } - if (sub.isHidden() == false) - report.runTable(null, argNames, args, actual, expected, outcomes); + report.runTable(null, argNames, args, actual, expected, outcomes); }); } diff --git a/app/com/horstmann/codecheck/Substitution.java b/app/com/horstmann/codecheck/Substitution.java index e149c298..9c75a5cb 100644 --- a/app/com/horstmann/codecheck/Substitution.java +++ b/app/com/horstmann/codecheck/Substitution.java @@ -14,20 +14,10 @@ public class Substitution { private Map> subs = new LinkedHashMap<>(); private int size; private Language language; - private boolean hidden = false; public Substitution(Language language) { this.language = language; } - - public boolean isHidden() { - return hidden; - } - - public void setHidden(boolean value) - { - hidden = value; - } public void addVariable(Path file, String decl, String args) { if (this.file == null) From dd419f08fe64c8aa697a4ea7a05094730cceb84b Mon Sep 17 00:00:00 2001 From: Anmol Gill Date: Thu, 21 Jul 2022 19:54:00 +0000 Subject: [PATCH 034/217] added HIDDENCALL annotation to hide specific CALLS & added some samples using this annotation --- app/com/horstmann/codecheck/Annotations.java | 20 +++++++++++------ app/com/horstmann/codecheck/Calls.java | 20 ++++++++--------- app/com/horstmann/codecheck/Main.java | 23 +++++++++++++++----- samples/java/example10/Numbers.java | 14 ++++++++++++ samples/java/example11/Numbers.java | 11 ++++++++++ samples/java/example12/Numbers.java | 14 ++++++++++++ 6 files changed, 79 insertions(+), 23 deletions(-) create mode 100644 samples/java/example10/Numbers.java create mode 100644 samples/java/example11/Numbers.java create mode 100644 samples/java/example12/Numbers.java diff --git a/app/com/horstmann/codecheck/Annotations.java b/app/com/horstmann/codecheck/Annotations.java index 5c929707..9099ed93 100644 --- a/app/com/horstmann/codecheck/Annotations.java +++ b/app/com/horstmann/codecheck/Annotations.java @@ -13,7 +13,7 @@ public class Annotations { public static final Set VALID_ANNOTATIONS = Set.of( - "HIDDEN", "HIDE", "SHOW", "EDIT", "SOLUTION", "CALL", "SUB", "ID", "SAMPLE", "ARGS", + "HIDDEN", "HIDE", "SHOW", "EDIT", "SOLUTION", "CALL", "HIDDENCALL", "SUB", "ID", "SAMPLE", "ARGS", "IN", "OUT", "TIMEOUT", "TOLERANCE", "IGNORECASE", "IGNORESPACE", "MAXOUTPUTLEN", "REQUIRED", "FORBIDDEN", "SCORING", "INTERLEAVE", "TILE", "FIXED", "OR", "PSEUDO"); public static final Set NON_BLANK_BEFORE_OK = Set.of("SUB", "PSEUDO"); @@ -89,7 +89,7 @@ private void read(Path p, byte[] contents) { solutions.add(p); if (a.key.equals("HIDE")) hidden.add(p); - if (a.key.equals("HIDDEN")) + if (a.key.equals("HIDDEN") || a.key.equals("HIDDENCALL")) hiddenTests.add(p); a.path = p; annotations.add(a); @@ -212,11 +212,17 @@ public boolean isSample(Path p) { public Calls findCalls() { Calls calls = new Calls(language); - for (Annotation a : annotations) { - if (a.key.equals("CALL")) - calls.addCall(a.path, a.args, a.next); - if (getHiddenTests().contains(a.path)) // OR (a.key.equals("HIDDEN")) - calls.setHidden(true); + int callNum = 0; + for (int a=0; a modifiers; + private boolean hidden = false; + + public boolean isHidden() { + return hidden; + } + + public void setHidden(boolean value) + { + hidden = value; + } } private Language language; private List calls = new ArrayList<>(); private Path file; private int lastGroup = -1; - private boolean hidden = false; public Calls(Language language) { this.language = language; @@ -28,15 +37,6 @@ public Path getFile() { return file; } - public boolean isHidden() { - return hidden; - } - - public void setHidden(boolean value) - { - hidden = value; - } - public int getSize() { return calls.size(); } diff --git a/app/com/horstmann/codecheck/Main.java b/app/com/horstmann/codecheck/Main.java index 308090bb..8998034c 100644 --- a/app/com/horstmann/codecheck/Main.java +++ b/app/com/horstmann/codecheck/Main.java @@ -147,7 +147,13 @@ private void doCalls(Calls calls, ResourceLoader resourceLoader) throws Exceptio names[i] = call.name; args[i][0] = call.args; if (lines.size() == 3 && Arrays.asList("true", "false").contains(lines.get(2))) { - expected[i] = lines.get(0); + if (call.isHidden() == true) { + expected[i] = "hidden"; + args[i][0] = "hidden"; + } + else { + expected[i] = lines.get(0); + } actual[i] = Util.truncate(lines.get(1), expected[i].length() + MUCH_LONGER); outcomes[i] = lines.get(2).equals("true"); } else { @@ -161,15 +167,20 @@ private void doCalls(Calls calls, ResourceLoader resourceLoader) throws Exceptio msg.append(line); msg.append('\n'); } String message = msg.toString(); - - expected[i] = lines.size() > 0 ? lines.get(0) : ""; + if (call.isHidden() == true) { + expected[i] = "hidden"; + args[i][0] = "hidden"; + } + else { + expected[i] = lines.get(0); + } actual[i] = message; outcomes[i] = false; } score.pass(outcomes[i], null /* no report--it's in the table */); } - if (calls.isHidden() == false) - report.runTable(names, new String[] { "Arguments" }, args, actual, expected, outcomes); + //if (calls.isHidden() == false) + report.runTable(names, new String[] { "Arguments" }, args, actual, expected, outcomes); }); } @@ -447,7 +458,7 @@ else if ("NJS".equals(reportType)) printFiles.removeAll(problem.getSolutionFiles().keySet()); if (problem.getAnnotations().checkConditions(submissionFiles, report)) { - if (problem.getAnnotations().has("CALL")) { + if (problem.getAnnotations().has("CALL") || problem.getAnnotations().has("HIDDENCALL")) { Calls calls = problem.getAnnotations().findCalls(); mainSourcePaths.remove(calls.getFile()); dependentSourcePaths.add(calls.getFile()); diff --git a/samples/java/example10/Numbers.java b/samples/java/example10/Numbers.java new file mode 100644 index 00000000..c7a891ca --- /dev/null +++ b/samples/java/example10/Numbers.java @@ -0,0 +1,14 @@ +//SOLUTION +public class Numbers +{ +//CALL 3, 4 +//HIDDENCALL -3, 3 +//C +ALL 3, 0 +public double average(int x, int y) + { + //HIDE + return 0.5 * (x + y); + //SHOW // Compute the average of x and y + } +} diff --git a/samples/java/example11/Numbers.java b/samples/java/example11/Numbers.java new file mode 100644 index 00000000..81fc7bb9 --- /dev/null +++ b/samples/java/example11/Numbers.java @@ -0,0 +1,11 @@ +//SOLUTION +public class Numbers +{ +//HIDDENCALL -3, 3 +public double average(int x, int y) + { + //HIDE + return 0.5 * (x + y); + //SHOW // Compute the average of x and y + } +} diff --git a/samples/java/example12/Numbers.java b/samples/java/example12/Numbers.java new file mode 100644 index 00000000..871095f0 --- /dev/null +++ b/samples/java/example12/Numbers.java @@ -0,0 +1,14 @@ +//SOLUTION +public class Numbers +{ +//HIDDENCALL 3, 4 +//HIDDENCALL -3, 3 +//HIDDENCALL 3, 0 +//CALL 4, 0 +public double average(int x, int y) + { + //HIDE + return 0.5 * (x + y); + //SHOW // Compute the average of x and y + } +} From e1fe6c727102d0a17725869c7e4f2360061ffe97 Mon Sep 17 00:00:00 2001 From: Anmol Gill Date: Mon, 25 Jul 2022 16:08:24 +0000 Subject: [PATCH 035/217] remove hidden tester files from student --- app/com/horstmann/codecheck/Annotations.java | 7 ++++++ app/com/horstmann/codecheck/Main.java | 26 ++++++++++---------- samples/java/example13/Numbers.java | 5 ++++ samples/java/example13/NumbersTester.java | 12 +++++++++ 4 files changed, 37 insertions(+), 13 deletions(-) create mode 100644 samples/java/example13/Numbers.java create mode 100644 samples/java/example13/NumbersTester.java diff --git a/app/com/horstmann/codecheck/Annotations.java b/app/com/horstmann/codecheck/Annotations.java index 9099ed93..607323e2 100644 --- a/app/com/horstmann/codecheck/Annotations.java +++ b/app/com/horstmann/codecheck/Annotations.java @@ -57,6 +57,7 @@ public static Annotation parse(String line, String start, String end) { private Set solutions = new TreeSet<>(); private Set hidden = new TreeSet<>(); private Set hiddenTests = new TreeSet<>(); + private Set hiddenTestFiles = new TreeSet<>(); public Annotations(Language language) { this.language = language; @@ -91,6 +92,8 @@ private void read(Path p, byte[] contents) { hidden.add(p); if (a.key.equals("HIDDEN") || a.key.equals("HIDDENCALL")) hiddenTests.add(p); + if (a.key.equals("HIDDEN")) + hiddenTestFiles.add(p); a.path = p; annotations.add(a); } @@ -109,6 +112,10 @@ public Set getHiddenTests() { return Collections.unmodifiableSet(hiddenTests); } + public Set getHiddenTestFiles() { + return Collections.unmodifiableSet(hiddenTestFiles); + } + public String findUniqueKey(String key) { Annotation match = null; for (Annotation a : annotations) { diff --git a/app/com/horstmann/codecheck/Main.java b/app/com/horstmann/codecheck/Main.java index 8998034c..5613631a 100644 --- a/app/com/horstmann/codecheck/Main.java +++ b/app/com/horstmann/codecheck/Main.java @@ -179,7 +179,6 @@ private void doCalls(Calls calls, ResourceLoader resourceLoader) throws Exceptio } score.pass(outcomes[i], null /* no report--it's in the table */); } - //if (calls.isHidden() == false) report.runTable(names, new String[] { "Arguments" }, args, actual, expected, outcomes); }); } @@ -200,7 +199,7 @@ private void runUnitTests() { report.run(p.toString()); if (!plan.checkCompiled(id, report, score)) return; String outerr = plan.outerr(id); - if (problem.getAnnotations().getHiddenTests().contains(p)) + if (problem.getAnnotations().getHiddenTestFiles().contains(p)) problem.getLanguage().reportUnitTest(outerr, report, score, true); else problem.getLanguage().reportUnitTest(outerr, report, score, false); @@ -221,7 +220,7 @@ private void runTester(Path mainFile, int timeout, int maxOutputLen) throws Exce String outerr = plan.outerr(compileID); AsExpected cond = new AsExpected(comp); String tester = plan.getFileString("use", mainFile); - if (problem.getAnnotations().getHiddenTests().contains(mainFile)) + if (problem.getAnnotations().getHiddenTestFiles().contains(mainFile)) cond.setHidden(true); if (tester == null) tester = plan.getFileString("solution", mainFile); // In case the student was asked to do it @@ -430,6 +429,17 @@ else if ("NJS".equals(reportType)) plan = new Plan(problem.getLanguage(), resourceLoader.getProperty("com.horstmann.codecheck.debug") != null); + // TODO: This would be nice to have in Problem, except that one might later need to remove checkstyle.xml + // the use files that the students are entitled to see + Set printFiles = Util.filterNot(problem.getUseFiles().keySet(), + "*.png", "*.PNG", "*.gif", "*.GIF", "*.jpg", "*.jpeg", "*.JPG", "*.bmp", "*.BMP", + "*.jar", "*.pdf"); + + printFiles.removeAll(problem.getAnnotations().getHidden()); + printFiles.removeAll(problem.getAnnotations().getHiddenTests()); + printFiles.removeAll(problem.getAnnotations().getHiddenTestFiles()); + printFiles.removeAll(problem.getSolutionFiles().keySet()); + timeoutMillis = (int) problem.getAnnotations().findUniqueDoubleKey("TIMEOUT", DEFAULT_TIMEOUT_MILLIS); maxOutputLen = (int) problem.getAnnotations().findUniqueDoubleKey("MAXOUTPUTLEN", DEFAULT_MAX_OUTPUT_LEN); double tolerance = problem.getAnnotations().findUniqueDoubleKey("TOLERANCE", DEFAULT_TOLERANCE); @@ -446,16 +456,6 @@ else if ("NJS".equals(reportType)) reportComments(metadata); copyFilesToPlan(submissionFiles); - - // TODO: This would be nice to have in Problem, except that one might later need to remove checkstyle.xml - // the use files that the students are entitled to see - Set printFiles = Util.filterNot(problem.getUseFiles().keySet(), - "*.png", "*.PNG", "*.gif", "*.GIF", "*.jpg", "*.jpeg", "*.JPG", "*.bmp", "*.BMP", - "*.jar", "*.pdf"); - - printFiles.removeAll(problem.getAnnotations().getHidden()); - printFiles.removeAll(problem.getAnnotations().getHiddenTests()); - printFiles.removeAll(problem.getSolutionFiles().keySet()); if (problem.getAnnotations().checkConditions(submissionFiles, report)) { if (problem.getAnnotations().has("CALL") || problem.getAnnotations().has("HIDDENCALL")) { diff --git a/samples/java/example13/Numbers.java b/samples/java/example13/Numbers.java new file mode 100644 index 00000000..cdfe5e38 --- /dev/null +++ b/samples/java/example13/Numbers.java @@ -0,0 +1,5 @@ +//SOLUTION +public class Numbers +{ + public int square(int n) { return n * n; } +} diff --git a/samples/java/example13/NumbersTester.java b/samples/java/example13/NumbersTester.java new file mode 100644 index 00000000..9937f5b9 --- /dev/null +++ b/samples/java/example13/NumbersTester.java @@ -0,0 +1,12 @@ +//HIDDEN +public class NumbersTester +{ + public static void main(String[] args) + { + Numbers nums = new Numbers(); + System.out.println(nums.square(3)); + System.out.println("Expected: 9"); + System.out.println(nums.square(-3)); + System.out.println("Expected: 9"); + } +} From 03c8b40cb5895a17013efaea27554dc744f23775 Mon Sep 17 00:00:00 2001 From: cayhorstmann Date: Mon, 25 Jul 2022 19:02:52 +0200 Subject: [PATCH 036/217] Update Main.java --- app/com/horstmann/codecheck/Main.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/com/horstmann/codecheck/Main.java b/app/com/horstmann/codecheck/Main.java index 5613631a..42a3311c 100644 --- a/app/com/horstmann/codecheck/Main.java +++ b/app/com/horstmann/codecheck/Main.java @@ -147,7 +147,7 @@ private void doCalls(Calls calls, ResourceLoader resourceLoader) throws Exceptio names[i] = call.name; args[i][0] = call.args; if (lines.size() == 3 && Arrays.asList("true", "false").contains(lines.get(2))) { - if (call.isHidden() == true) { + if (call.isHidden()) { expected[i] = "hidden"; args[i][0] = "hidden"; } From 789a6a29d062ac33b29223749ad44a6d4853f1a1 Mon Sep 17 00:00:00 2001 From: Cay Horstmann Date: Mon, 25 Jul 2022 19:05:13 +0200 Subject: [PATCH 037/217] Deploy staging play-codecheck --- app/controllers/Assignment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/Assignment.java b/app/controllers/Assignment.java index 6cb68e49..436be651 100644 --- a/app/controllers/Assignment.java +++ b/app/controllers/Assignment.java @@ -41,7 +41,7 @@ state: as string, not JSON score - with global secondary index + with global secondary index (TODO: Not currently) problemID submitterID From d379c6d807d920c0bc8be53fbadce0903b17893d Mon Sep 17 00:00:00 2001 From: thinhngocnguyen <59405079+thinh241819@users.noreply.github.com> Date: Thu, 28 Jul 2022 11:29:21 +0000 Subject: [PATCH 038/217] modified fixZip() to handle edge case of zip file has only 1 file --- app/controllers/Upload.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/controllers/Upload.java b/app/controllers/Upload.java index f9fd5bca..27bde7ec 100644 --- a/app/controllers/Upload.java +++ b/app/controllers/Upload.java @@ -235,9 +235,15 @@ private static Map fixZip(Map problemFiles) throws I } if (r == null) return problemFiles; Map fixedProblemFiles = new TreeMap<>(); - for (Map.Entry entry : problemFiles.entrySet()) { - fixedProblemFiles.put(r.relativize(entry.getKey()), entry.getValue()); + if(problemFiles.keySet().size() == 1) { + fixedProblemFiles.put(r.getFileName(), problemFiles.get(r)); } + else { + for (Map.Entry entry : problemFiles.entrySet()) { + fixedProblemFiles.put(r.relativize(entry.getKey()), entry.getValue()); + } + } + return fixedProblemFiles; } From 59f5fab800f33d1a514061af51c7aa91ee953a7e Mon Sep 17 00:00:00 2001 From: Anmol Gill Date: Tue, 26 Jul 2022 21:06:54 +0000 Subject: [PATCH 039/217] Added INH annotation to hide specific input lines when testing a whole program --- app/com/horstmann/codecheck/Annotations.java | 2 +- app/com/horstmann/codecheck/Comparison.java | 5 +-- app/com/horstmann/codecheck/Input.java | 32 ++++++++++++++++++ app/com/horstmann/codecheck/Main.java | 35 +++++++++++--------- samples/java/example10/Numbers.java | 3 +- samples/java/hiddenIN1/Numbers.java | 27 +++++++++++++++ samples/java/hiddenIN2/Numbers.java | 29 ++++++++++++++++ 7 files changed, 113 insertions(+), 20 deletions(-) create mode 100644 app/com/horstmann/codecheck/Input.java create mode 100644 samples/java/hiddenIN1/Numbers.java create mode 100644 samples/java/hiddenIN2/Numbers.java diff --git a/app/com/horstmann/codecheck/Annotations.java b/app/com/horstmann/codecheck/Annotations.java index 607323e2..9e313b84 100644 --- a/app/com/horstmann/codecheck/Annotations.java +++ b/app/com/horstmann/codecheck/Annotations.java @@ -14,7 +14,7 @@ public class Annotations { public static final Set VALID_ANNOTATIONS = Set.of( "HIDDEN", "HIDE", "SHOW", "EDIT", "SOLUTION", "CALL", "HIDDENCALL", "SUB", "ID", "SAMPLE", "ARGS", - "IN", "OUT", "TIMEOUT", "TOLERANCE", "IGNORECASE", "IGNORESPACE", "MAXOUTPUTLEN", + "IN", "INH", "OUT", "TIMEOUT", "TOLERANCE", "IGNORECASE", "IGNORESPACE", "MAXOUTPUTLEN", "REQUIRED", "FORBIDDEN", "SCORING", "INTERLEAVE", "TILE", "FIXED", "OR", "PSEUDO"); public static final Set NON_BLANK_BEFORE_OK = Set.of("SUB", "PSEUDO"); diff --git a/app/com/horstmann/codecheck/Comparison.java b/app/com/horstmann/codecheck/Comparison.java index 6595af00..5a17f7ea 100644 --- a/app/com/horstmann/codecheck/Comparison.java +++ b/app/com/horstmann/codecheck/Comparison.java @@ -11,7 +11,7 @@ public class Comparison { private static final int MANY_MORE_LINES = 10; // If actual lines > expected + MANY_MORE_LINES, truncate actual output - public boolean execute(String input, String actual, String expected, Report report, String filename) { + public boolean execute(String input, String actual, String expected, Report report, String filename, boolean hidden) { List lines1 = getLines(actual.replaceAll("〈[^〉]*〉\n", "")); List lines2 = getLines(expected.replaceAll("〈[^〉]*〉\n", "")); @@ -30,7 +30,8 @@ public boolean execute(String input, String actual, String expected, Report repo report.file(filename, actual); } else { - report.output(actual); + if (!hidden) + report.output(actual); } } else { diff --git a/app/com/horstmann/codecheck/Input.java b/app/com/horstmann/codecheck/Input.java new file mode 100644 index 00000000..11abadf5 --- /dev/null +++ b/app/com/horstmann/codecheck/Input.java @@ -0,0 +1,32 @@ +package com.horstmann.codecheck; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class Input{ + private String key; + private String value; + private boolean hidden; + + + public Input(String key, String value, boolean hidden) { + this.key = key; + this.value = value; + this.hidden = hidden; + } + + public boolean getHidden(){ + return hidden; + } + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } +} \ No newline at end of file diff --git a/app/com/horstmann/codecheck/Main.java b/app/com/horstmann/codecheck/Main.java index 42a3311c..6b0c9048 100644 --- a/app/com/horstmann/codecheck/Main.java +++ b/app/com/horstmann/codecheck/Main.java @@ -228,12 +228,12 @@ private void runTester(Path mainFile, int timeout, int maxOutputLen) throws Exce }); } - private void testInputs(Map inputs, Path mainFile, boolean okToInterleave) throws Exception { + private void testInputs(List inputs, Path mainFile, boolean okToInterleave) throws Exception { /* * If there are no inputs, we feed in one empty input to execute the program. */ if (inputs.size() == 0) - inputs.put("", ""); + inputs.add(new Input("","",false)); plan.compile("submissionrun", "submission", mainFile, dependentSourcePaths); boolean runSolution = !problem.getInputMode() && !problem.getAnnotations().isSample(mainFile); @@ -246,14 +246,16 @@ private void testInputs(Map inputs, Path mainFile, boolean okToI plan.checkSolutionCompiled("solutionrun", report, score); plan.checkCompiled("submissionrun", report, score); }); - for (String test : inputs.keySet()) { - String input = inputs.get(test); - testInput(mainFile, runSolution, test, input, timeoutMillis / inputs.size(), maxOutputLen / inputs.size(), okToInterleave); + for (int i=0; i