diff --git a/pom.xml b/pom.xml
index d05bcf09..294419cc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -142,7 +142,7 @@
- edu.byu.cs.server.Server
+ Main
@@ -169,7 +169,7 @@
true
- edu.byu.cs.server.Server
+ Main
diff --git a/src/main/java/Main.java b/src/main/java/Main.java
new file mode 100644
index 00000000..a533f101
--- /dev/null
+++ b/src/main/java/Main.java
@@ -0,0 +1,103 @@
+import edu.byu.cs.autograder.GradingException;
+import edu.byu.cs.server.endpointprovider.EndpointProvider;
+import edu.byu.cs.server.endpointprovider.EndpointProviderImpl;
+import edu.byu.cs.dataAccess.DaoService;
+import edu.byu.cs.dataAccess.DataAccessException;
+import edu.byu.cs.properties.ApplicationProperties;
+import edu.byu.cs.server.Server;
+import edu.byu.cs.service.SubmissionService;
+import edu.byu.cs.util.ResourceUtils;
+import org.apache.commons.cli.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
+
+public class Main {
+ private static Logger LOGGER = LoggerFactory.getLogger(Main.class);
+
+ private static EndpointProvider endpointProvider = new EndpointProviderImpl();
+
+ public static void main(String[] args) {
+ ResourceUtils.copyResourceFiles("phases", new File(""));
+ setupProperties(args);
+
+ try {
+ DaoService.initializeSqlDAOs();
+ } catch (DataAccessException e) {
+ LOGGER.error("Error setting up database", e);
+ throw new RuntimeException(e);
+ }
+
+ new Server(endpointProvider).start(8080);
+
+ try {
+ SubmissionService.reRunSubmissionsInQueue();
+ } catch (IOException | DataAccessException | GradingException e) {
+ LOGGER.error("Error rerunning submissions already in queue", e);
+ }
+ }
+
+ private static void setupProperties(String[] args) {
+ Options options = getOptions();
+
+ Properties properties = new Properties();
+
+ CommandLineParser parser = new DefaultParser();
+ try {
+ CommandLine cmd = parser.parse(options, args);
+ if (cmd.hasOption("db-host")) {
+ properties.setProperty("db-host", cmd.getOptionValue("db-host"));
+ }
+ if (cmd.hasOption("db-port")) {
+ properties.setProperty("db-port", cmd.getOptionValue("db-port"));
+ }
+ if (cmd.hasOption("db-name")) {
+ properties.setProperty("db-name", cmd.getOptionValue("db-name"));
+ }
+ if (cmd.hasOption("db-user")) {
+ properties.setProperty("db-user", cmd.getOptionValue("db-user"));
+ }
+ if (cmd.hasOption("db-pass")) {
+ properties.setProperty("db-pass", cmd.getOptionValue("db-pass"));
+ }
+ if (cmd.hasOption("frontend-url")) {
+ properties.setProperty("frontend-url", cmd.getOptionValue("frontend-url"));
+ }
+ if (cmd.hasOption("cas-callback-url")) {
+ properties.setProperty("cas-callback-url", cmd.getOptionValue("cas-callback-url"));
+ }
+ if (cmd.hasOption("canvas-token")) {
+ properties.setProperty("canvas-token", cmd.getOptionValue("canvas-token"));
+ }
+ if (cmd.hasOption("use-canvas")) {
+ properties.setProperty("use-canvas", cmd.getOptionValue("use-canvas"));
+ }
+ if (cmd.hasOption("disable-compilation")) {
+ properties.setProperty("run-compilation", "false");
+ }
+ } catch (ParseException e) {
+ throw new RuntimeException("Error parsing command line arguments", e);
+ }
+
+ ApplicationProperties.loadProperties(properties);
+ }
+
+ private static Options getOptions() {
+ Options options = new Options();
+ options.addOption(null, "db-host", true, "Database Host");
+ options.addOption(null, "db-port", true, "Database Port");
+ options.addOption(null, "db-name", true, "Database Name");
+ options.addOption(null, "db-user", true, "Database User");
+ options.addOption(null, "db-pass", true, "Database Password");
+ options.addOption(null, "frontend-url", true, "Frontend URL");
+ options.addOption(null, "cas-callback-url", true, "CAS Callback URL");
+ options.addOption(null, "canvas-token", true, "Canvas Token");
+ options.addOption(null, "use-canvas", true, "Using Canvas");
+ options.addOption(null, "disable-compilation", false, "Turn off student code compilation");
+ return options;
+ }
+
+}
diff --git a/src/main/java/edu/byu/cs/autograder/score/Scorer.java b/src/main/java/edu/byu/cs/autograder/score/Scorer.java
index 1e390dbc..73f80c42 100644
--- a/src/main/java/edu/byu/cs/autograder/score/Scorer.java
+++ b/src/main/java/edu/byu/cs/autograder/score/Scorer.java
@@ -318,9 +318,11 @@ private Rubric.Results mergeResultsWithPrevious(Rubric.RubricType rubricType, Ru
float score = startingScore;
for (Submission previousSubmission : previousSubmissions) {
- Rubric.RubricItem previousItem = previousSubmission.rubric().items().get(rubricType);
- if (previousItem != null && previousItem.results().rawScore() <= results.rawScore()) {
- score = Math.max(score, previousItem.results().score());
+ if(previousSubmission.passed()) {
+ Rubric.RubricItem previousItem = previousSubmission.rubric().items().get(rubricType);
+ if (previousItem != null && previousItem.results().rawScore() <= results.rawScore()) {
+ score = Math.max(score, previousItem.results().score());
+ }
}
}
diff --git a/src/main/java/edu/byu/cs/server/Server.java b/src/main/java/edu/byu/cs/server/Server.java
index 4fab4136..c2deb6be 100644
--- a/src/main/java/edu/byu/cs/server/Server.java
+++ b/src/main/java/edu/byu/cs/server/Server.java
@@ -1,33 +1,28 @@
package edu.byu.cs.server;
-import edu.byu.cs.autograder.GradingException;
import edu.byu.cs.controller.WebSocketController;
-import edu.byu.cs.dataAccess.DaoService;
-import edu.byu.cs.dataAccess.DataAccessException;
-import edu.byu.cs.properties.ApplicationProperties;
-import edu.byu.cs.service.SubmissionService;
-import edu.byu.cs.util.ResourceUtils;
-import org.apache.commons.cli.*;
+import edu.byu.cs.server.endpointprovider.EndpointProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.File;
-import java.io.IOException;
-import java.util.Properties;
-
-import static edu.byu.cs.controller.AdminController.*;
-import static edu.byu.cs.controller.AuthController.*;
-import static edu.byu.cs.controller.CasController.*;
-import static edu.byu.cs.controller.ConfigController.*;
-import static edu.byu.cs.controller.SubmissionController.*;
-import static edu.byu.cs.controller.UserController.*;
import static spark.Spark.*;
public class Server {
private static final Logger LOGGER = LoggerFactory.getLogger(Server.class);
+ private final EndpointProvider provider;
+
+ public Server(EndpointProvider endpointProvider) {
+ this.provider = endpointProvider;
+ }
- public static int setupEndpoints(int port) {
+ public int start(int desiredPort) {
+ int chosenPort = setupEndpoints(desiredPort);
+ LOGGER.info("Server started on port {}", chosenPort);
+ return chosenPort;
+ }
+
+ private int setupEndpoints(int port) {
port(port);
webSocket("/ws", WebSocketController.class);
@@ -35,190 +30,94 @@ public static int setupEndpoints(int port) {
staticFiles.location("/frontend/dist");
- before((request, response) -> {
- response.header("Access-Control-Allow-Headers", "Authorization,Content-Type");
- response.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,PATCH,OPTIONS");
- response.header("Access-Control-Allow-Credentials", "true");
- response.header("Access-Control-Allow-Origin", ApplicationProperties.frontendUrl());
- });
+ before(provider.beforeAll());
path("/auth", () -> {
- get("/callback", callbackGet);
- get("/login", loginGet);
+ get("/callback", provider.callbackGet());
+ get("/login", provider.loginGet());
// all routes after this point require authentication
- post("/logout", logoutPost);
+ post("/logout", provider.logoutPost());
});
path("/api", () -> {
before("/*", (req, res) -> {
- if (!req.requestMethod().equals("OPTIONS"))
- verifyAuthenticatedMiddleware.handle(req, res);
+ if (!req.requestMethod().equals("OPTIONS")) provider.verifyAuthenticatedMiddleware().handle(req, res);
});
- patch("/repo", repoPatch);
+ patch("/repo", provider.repoPatch());
- get("/submit", submitGet);
- post("/submit", submitPost);
+ get("/submit", provider.submitGet());
+ post("/submit", provider.submitPost());
- get("/latest", latestSubmissionForMeGet);
+ get("/latest", provider.latestSubmissionForMeGet());
- get("/submission", submissionXGet);
- get("/submission/:phase", submissionXGet);
+ get("/submission", provider.submissionXGet());
+ get("/submission/:phase", provider.submissionXGet());
- get("/me", meGet);
+ get("/me", provider.meGet());
- get("/config", getConfigStudent);
+ get("/config", provider.getConfigStudent());
path("/admin", () -> {
before("/*", (req, res) -> {
- if (!req.requestMethod().equals("OPTIONS"))
- verifyAdminMiddleware.handle(req, res);
+ if (!req.requestMethod().equals("OPTIONS")) provider.verifyAdminMiddleware().handle(req, res);
});
- patch("/repo/:netId", repoPatchAdmin);
+ patch("/repo/:netId", provider.repoPatchAdmin());
- get("/repo/history", repoHistoryAdminGet);
+ get("/repo/history", provider.repoHistoryAdminGet());
- get("/users", usersGet);
+ get("/users", provider.usersGet());
- post("/submit", adminRepoSubmitPost);
+ post("/submit", provider.adminRepoSubmitPost());
path("/submissions", () -> {
- post("/approve", approveSubmissionPost);
+ post("/approve", provider.approveSubmissionPost());
- get("/latest", latestSubmissionsGet);
+ get("/latest", provider.latestSubmissionsGet());
- get("/latest/:count", latestSubmissionsGet);
+ get("/latest/:count", provider.latestSubmissionsGet());
- get("/active", submissionsActiveGet);
+ get("/active", provider.submissionsActiveGet());
- get("/student/:netId", studentSubmissionsGet);
+ get("/student/:netId", provider.studentSubmissionsGet());
- post("/rerun", submissionsReRunPost);
+ post("/rerun", provider.submissionsReRunPost());
});
- get("/test_mode", testModeGet);
+ get("/test_mode", provider.testModeGet());
- get("/analytics/commit", commitAnalyticsGet);
+ get("/analytics/commit", provider.commitAnalyticsGet());
- get("/analytics/commit/:option", commitAnalyticsGet);
+ get("/analytics/commit/:option", provider.commitAnalyticsGet());
- get("/honorChecker/zip/:section", honorCheckerZipGet);
+ get("/honorChecker/zip/:section", provider.honorCheckerZipGet());
- get("/sections", sectionsGet);
+ get("/sections", provider.sectionsGet());
path("/config", () -> {
- get("", getConfigAdmin);
+ get("", provider.getConfigAdmin());
- post("/phases", updateLivePhases);
- post("/phases/shutdown", scheduleShutdown);
- post("/banner", updateBannerMessage);
+ post("/phases", provider.updateLivePhases());
+ post("/phases/shutdown", provider.scheduleShutdown());
+ post("/banner", provider.updateBannerMessage());
- post("/courseIds", updateCourseIdsPost);
- get("/courseIds", updateCourseIdsUsingCanvasGet);
+ post("/courseIds", provider.updateCourseIdsPost());
+ get("/courseIds", provider.updateCourseIdsUsingCanvasGet());
- post("/penalties", updatePenalties);
+ post("/penalties", provider.updatePenalties());
});
});
});
// spark's notFound method does not work
- get("/*", (req, res) -> {
- if (req.pathInfo().equals("/ws"))
- return null;
-
- String urlParams = req.queryString();
- urlParams = urlParams == null ? "" : "?" + urlParams;
- res.redirect("/" + urlParams, 302);
- return null;
- });
- init();
+ get("/*", provider.defaultGet());
- return port();
- }
+ after(provider.afterAll());
- private static void setupProperties(String[] args) {
- Options options = getOptions();
-
- Properties properties = new Properties();
-
- CommandLineParser parser = new DefaultParser();
- try {
- CommandLine cmd = parser.parse(options, args);
- if (cmd.hasOption("db-host")) {
- properties.setProperty("db-host", cmd.getOptionValue("db-host"));
- }
- if (cmd.hasOption("db-port")) {
- properties.setProperty("db-port", cmd.getOptionValue("db-port"));
- }
- if (cmd.hasOption("db-name")) {
- properties.setProperty("db-name", cmd.getOptionValue("db-name"));
- }
- if (cmd.hasOption("db-user")) {
- properties.setProperty("db-user", cmd.getOptionValue("db-user"));
- }
- if (cmd.hasOption("db-pass")) {
- properties.setProperty("db-pass", cmd.getOptionValue("db-pass"));
- }
- if (cmd.hasOption("frontend-url")) {
- properties.setProperty("frontend-url", cmd.getOptionValue("frontend-url"));
- }
- if (cmd.hasOption("cas-callback-url")) {
- properties.setProperty("cas-callback-url", cmd.getOptionValue("cas-callback-url"));
- }
- if (cmd.hasOption("canvas-token")) {
- properties.setProperty("canvas-token", cmd.getOptionValue("canvas-token"));
- }
- if (cmd.hasOption("use-canvas")) {
- properties.setProperty("use-canvas", cmd.getOptionValue("use-canvas"));
- }
- if (cmd.hasOption("disable-compilation")) {
- properties.setProperty("run-compilation", "false");
- }
- } catch (ParseException e) {
- throw new RuntimeException("Error parsing command line arguments", e);
- }
-
- ApplicationProperties.loadProperties(properties);
- }
-
- private static Options getOptions() {
- Options options = new Options();
- options.addOption(null, "db-host", true, "Database Host");
- options.addOption(null, "db-port", true, "Database Port");
- options.addOption(null, "db-name", true, "Database Name");
- options.addOption(null, "db-user", true, "Database User");
- options.addOption(null, "db-pass", true, "Database Password");
- options.addOption(null, "frontend-url", true, "Frontend URL");
- options.addOption(null, "cas-callback-url", true, "CAS Callback URL");
- options.addOption(null, "canvas-token", true, "Canvas Token");
- options.addOption(null, "use-canvas", true, "Using Canvas");
- options.addOption(null, "disable-compilation", false, "Turn off student code compilation");
- return options;
- }
-
-
- public static void main(String[] args) {
- ResourceUtils.copyResourceFiles("phases", new File(""));
- setupProperties(args);
-
- try {
- DaoService.initializeSqlDAOs();
- } catch (DataAccessException e) {
- LOGGER.error("Error setting up database", e);
- throw new RuntimeException(e);
- }
-
- int port = setupEndpoints(8080);
-
- LOGGER.info("Server started on port {}", port);
+ init();
- try {
- SubmissionService.reRunSubmissionsInQueue();
- } catch (IOException | DataAccessException | GradingException e) {
- LOGGER.error("Error rerunning submissions already in queue", e);
- }
+ return port();
}
-
}
diff --git a/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProvider.java b/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProvider.java
new file mode 100644
index 00000000..b89c1d6a
--- /dev/null
+++ b/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProvider.java
@@ -0,0 +1,64 @@
+package edu.byu.cs.server.endpointprovider;
+
+import spark.Filter;
+import spark.Route;
+
+public interface EndpointProvider {
+
+ // Wildcard endpoints
+
+ Filter beforeAll();
+ Filter afterAll();
+
+ Route defaultGet();
+
+ // AdminController
+
+ Route usersGet();
+ Route testModeGet();
+ Route commitAnalyticsGet();
+ Route honorCheckerZipGet();
+ Route sectionsGet();
+
+ // AuthController
+
+ Filter verifyAuthenticatedMiddleware();
+ Filter verifyAdminMiddleware();
+ Route meGet();
+
+ // CasController
+
+ Route callbackGet();
+ Route loginGet();
+ Route logoutPost();
+
+ // ConfigController
+
+ Route getConfigAdmin();
+ Route getConfigStudent();
+ Route updateLivePhases();
+ Route scheduleShutdown();
+ Route updateBannerMessage();
+ Route updateCourseIdsPost();
+ Route updateCourseIdsUsingCanvasGet();
+ Route updatePenalties();
+
+ // SubmissionController
+
+ Route submitPost();
+ Route adminRepoSubmitPost();
+ Route submitGet();
+ Route latestSubmissionForMeGet();
+ Route submissionXGet();
+ Route latestSubmissionsGet();
+ Route submissionsActiveGet();
+ Route studentSubmissionsGet();
+ Route approveSubmissionPost();
+ Route submissionsReRunPost();
+
+ // UserController
+
+ Route repoPatch();
+ Route repoPatchAdmin();
+ Route repoHistoryAdminGet();
+}
diff --git a/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProviderImpl.java b/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProviderImpl.java
new file mode 100644
index 00000000..1ba31d02
--- /dev/null
+++ b/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProviderImpl.java
@@ -0,0 +1,211 @@
+package edu.byu.cs.server.endpointprovider;
+
+import edu.byu.cs.controller.*;
+import edu.byu.cs.properties.ApplicationProperties;
+import spark.Filter;
+import spark.Route;
+
+public class EndpointProviderImpl implements EndpointProvider {
+
+ // Wildcard endpoints
+
+ @Override
+ public Filter beforeAll() {
+ return (request, response) -> {
+ response.header("Access-Control-Allow-Headers", "Authorization,Content-Type");
+ response.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,PATCH,OPTIONS");
+ response.header("Access-Control-Allow-Credentials", "true");
+ response.header("Access-Control-Allow-Origin", ApplicationProperties.frontendUrl());
+ };
+ }
+
+ @Override
+ public Filter afterAll() {
+ return (req, res) -> {};
+ }
+
+ @Override
+ public Route defaultGet() {
+ return (req, res) -> {
+ if (req.pathInfo().equals("/ws"))
+ return null;
+
+ String urlParams = req.queryString();
+ urlParams = urlParams == null ? "" : "?" + urlParams;
+ res.redirect("/" + urlParams, 302);
+ return null;
+ };
+ }
+
+ // AdminController
+
+ @Override
+ public Route usersGet() {
+ return AdminController.usersGet;
+ }
+
+ @Override
+ public Route testModeGet() {
+ return AdminController.testModeGet;
+ }
+
+ @Override
+ public Route commitAnalyticsGet() {
+ return AdminController.commitAnalyticsGet;
+ }
+
+ @Override
+ public Route honorCheckerZipGet() {
+ return AdminController.honorCheckerZipGet;
+ }
+
+ @Override
+ public Route sectionsGet() {
+ return AdminController.sectionsGet;
+ }
+
+ // AuthController
+
+ @Override
+ public Filter verifyAuthenticatedMiddleware() {
+ return AuthController.verifyAuthenticatedMiddleware;
+ }
+
+ @Override
+ public Filter verifyAdminMiddleware() {
+ return AuthController.verifyAdminMiddleware;
+ }
+
+ @Override
+ public Route meGet() {
+ return AuthController.meGet;
+ }
+
+ // CasController
+
+ @Override
+ public Route callbackGet() {
+ return CasController.callbackGet;
+ }
+
+ @Override
+ public Route loginGet() {
+ return CasController.loginGet;
+ }
+
+ @Override
+ public Route logoutPost() {
+ return CasController.logoutPost;
+ }
+
+ // ConfigController
+
+ @Override
+ public Route getConfigAdmin() {
+ return ConfigController.getConfigAdmin;
+ }
+
+ @Override
+ public Route getConfigStudent() {
+ return ConfigController.getConfigStudent;
+ }
+
+ @Override
+ public Route updateLivePhases() {
+ return ConfigController.updateLivePhases;
+ }
+
+ @Override
+ public Route scheduleShutdown() {
+ return ConfigController.scheduleShutdown;
+ }
+
+ @Override
+ public Route updateBannerMessage() {
+ return ConfigController.updateBannerMessage;
+ }
+
+ @Override
+ public Route updateCourseIdsPost() {
+ return ConfigController.updateCourseIdsPost;
+ }
+
+ @Override
+ public Route updateCourseIdsUsingCanvasGet() {
+ return ConfigController.updateCourseIdsUsingCanvasGet;
+ }
+
+ @Override
+ public Route updatePenalties() {
+ return ConfigController.updatePenalties;
+ }
+
+ // SubmissionController
+
+ @Override
+ public Route submitPost() {
+ return SubmissionController.submitPost;
+ }
+
+ @Override
+ public Route adminRepoSubmitPost() {
+ return SubmissionController.adminRepoSubmitPost;
+ }
+
+ @Override
+ public Route submitGet() {
+ return SubmissionController.submitGet;
+ }
+
+ @Override
+ public Route latestSubmissionForMeGet() {
+ return SubmissionController.latestSubmissionForMeGet;
+ }
+
+ @Override
+ public Route submissionXGet() {
+ return SubmissionController.submissionXGet;
+ }
+
+ @Override
+ public Route latestSubmissionsGet() {
+ return SubmissionController.latestSubmissionsGet;
+ }
+
+ @Override
+ public Route submissionsActiveGet() {
+ return SubmissionController.submissionsActiveGet;
+ }
+
+ @Override
+ public Route studentSubmissionsGet() {
+ return SubmissionController.studentSubmissionsGet;
+ }
+
+ @Override
+ public Route approveSubmissionPost() {
+ return SubmissionController.approveSubmissionPost;
+ }
+
+ @Override
+ public Route submissionsReRunPost() {
+ return SubmissionController.submissionsReRunPost;
+ }
+
+ // UserController
+
+ @Override
+ public Route repoPatch() {
+ return UserController.repoPatch;
+ }
+
+ @Override
+ public Route repoPatchAdmin() {
+ return UserController.repoPatchAdmin;
+ }
+
+ @Override
+ public Route repoHistoryAdminGet() {
+ return UserController.repoHistoryAdminGet;
+ }
+}
diff --git a/src/test/java/edu/byu/cs/autograder/score/ScorerTest.java b/src/test/java/edu/byu/cs/autograder/score/ScorerTest.java
index 2cdaece6..46b54e78 100644
--- a/src/test/java/edu/byu/cs/autograder/score/ScorerTest.java
+++ b/src/test/java/edu/byu/cs/autograder/score/ScorerTest.java
@@ -274,6 +274,21 @@ void score_doesNotDecrease_when_distributedHigherPriorScore() throws CanvasExcep
Assertions.assertEquals(UNIT_TESTS_POSSIBLE_POINTS, rubricItems.get(Rubric.RubricType.UNIT_TESTS).results().score());
}
+ @Test
+ void score_doesDecrease_when_higherPriorScoreOfFailedSubmission() throws CanvasException, DataAccessException {
+ Submission lastSubmission = previousSubmissionHelper(
+ new Phase3SubmissionValues(0, CODE_QUALITY_POSSIBLE_POINTS, UNIT_TESTS_POSSIBLE_POINTS, -1),
+ new Phase3SubmissionValues(PASSOFF_POSSIBLE_POINTS, CODE_QUALITY_POSSIBLE_POINTS, UNIT_TESTS_POSSIBLE_POINTS, 30)
+ );
+
+ Assertions.assertNotNull(lastSubmission);
+ EnumMap rubricItems = lastSubmission.rubric().items();
+
+ Assertions.assertEquals(PASSOFF_POSSIBLE_POINTS / 2f, rubricItems.get(Rubric.RubricType.PASSOFF_TESTS).results().score());
+ Assertions.assertEquals(CODE_QUALITY_POSSIBLE_POINTS / 2f, rubricItems.get(Rubric.RubricType.QUALITY).results().score());
+ Assertions.assertEquals(UNIT_TESTS_POSSIBLE_POINTS / 2f, rubricItems.get(Rubric.RubricType.UNIT_TESTS).results().score());
+ }
+
// Helper Methods for constructing
/**