diff --git a/backend/app/src/main/java/com/ugent/pidgeon/controllers/SubmissionController.java b/backend/app/src/main/java/com/ugent/pidgeon/controllers/SubmissionController.java index 6eab58b5..fb717792 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/controllers/SubmissionController.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/controllers/SubmissionController.java @@ -8,17 +8,15 @@ import com.ugent.pidgeon.model.json.SubmissionJson; import com.ugent.pidgeon.model.submissionTesting.DockerOutput; import com.ugent.pidgeon.model.submissionTesting.DockerSubmissionTestModel; -import com.ugent.pidgeon.model.submissionTesting.DockerTestOutput; import com.ugent.pidgeon.model.submissionTesting.SubmissionTemplateModel; import com.ugent.pidgeon.postgre.models.*; import com.ugent.pidgeon.postgre.models.types.DockerTestState; +import com.ugent.pidgeon.postgre.models.types.DockerTestType; import com.ugent.pidgeon.postgre.models.types.UserRole; import com.ugent.pidgeon.postgre.repository.*; import com.ugent.pidgeon.util.*; import java.util.concurrent.CompletableFuture; -import java.util.zip.ZipException; import java.util.logging.Level; -import java.util.concurrent.CompletableFuture; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; @@ -33,7 +31,6 @@ import java.nio.file.Path; import java.time.OffsetDateTime; import java.util.List; -import java.util.function.Function; import java.util.logging.Logger; import java.util.zip.ZipFile; @@ -63,9 +60,11 @@ public class SubmissionController { private EntityToJsonConverter entityToJsonConverter; @Autowired private CommonDatabaseActions commonDatabaseActions; + @Autowired + private TestUtil testUtil; - private SubmissionTemplateModel.SubmissionResult runStructureTest(ZipFile file, TestEntity testEntity) throws IOException { + private SubmissionTemplateModel.SubmissionResult runStructureTest(ZipFile file, TestEntity testEntity) throws IOException { // There is no structure test for this project if(testEntity.getStructureTemplate() == null){ return null; @@ -261,13 +260,21 @@ public ResponseEntity submitFile(@RequestParam("file") MultipartFile file, @P submission.setStructureAccepted(structureTestResult.passed); submission.setStructureFeedback(structureTestResult.feedback); } - // Define docker test as running - submission.setDockerTestState(DockerTestState.running); + + if (testEntity.getDockerTestTemplate() != null) { + submission.setDockerType(DockerTestType.TEMPLATE); + } else if (testEntity.getDockerTestScript() != null) { + submission.setDockerType(DockerTestType.SIMPLE); + } else { + submission.setDockerType(DockerTestType.NONE); + } // save the first feedback, without docker feedback submissionRepository.save(submission); if (testEntity.getDockerTestScript() != null) { + // Define docker test as running + submission.setDockerTestState(DockerTestState.running); // run docker tests in background File finalSavedFile = savedFile; CompletableFuture.runAsync(() -> { @@ -351,56 +358,7 @@ public ResponseEntity getSubmissionFile(@PathVariable("submissionid") long su } } - - public ResponseEntity getFeedbackReponseEntity(long submissionid, Auth auth, Function feedbackGetter) { - - CheckResult checkResult = submissionUtil.canGetSubmission(submissionid, auth.getUserEntity()); - if (!checkResult.getStatus().equals(HttpStatus.OK)) { - return ResponseEntity.status(checkResult.getStatus()).body(checkResult.getMessage()); - } - SubmissionEntity submission = checkResult.getData(); - - HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.CONTENT_TYPE, String.valueOf(MediaType.TEXT_PLAIN)); - return ResponseEntity.ok().headers(headers).body(feedbackGetter.apply(submission)); - } - - /** - * Function to get the structure feedback of a submission - * - * @param submissionid ID of the submission to get the feedback from - * @param auth authentication object of the requesting user - * @return ResponseEntity with the feedback - * @ApiDog apiDog documentation - * @HttpMethod GET - * @AllowedRoles teacher, student - * @ApiPath /api/submissions/{submissionid}/structurefeedback - */ - @GetMapping(ApiRoutes.SUBMISSION_BASE_PATH + "/{submissionid}/structurefeedback") - //Route to get the structure feedback - @Roles({UserRole.teacher, UserRole.student}) - public ResponseEntity getStructureFeedback(@PathVariable("submissionid") long submissionid, Auth auth) { - return getFeedbackReponseEntity(submissionid, auth, SubmissionEntity::getStructureFeedback); - } - - /** - * Function to get the docker feedback of a submission - * - * @param submissionid ID of the submission to get the feedback from - * @param auth authentication object of the requesting user - * @return ResponseEntity with the feedback - * @ApiDog apiDog documentation - * @HttpMethod GET - * @AllowedRoles teacher, student - * @ApiPath /api/submissions/{submissionid}/dockerfeedback - */ - @GetMapping(ApiRoutes.SUBMISSION_BASE_PATH + "/{submissionid}/dockerfeedback") //Route to get the docker feedback - @Roles({UserRole.teacher, UserRole.student}) - public ResponseEntity getDockerFeedback(@PathVariable("submissionid") long submissionid, Auth auth) { - return getFeedbackReponseEntity(submissionid, auth, SubmissionEntity::getDockerFeedback); - } - - + /** * Function to delete a submission * diff --git a/backend/app/src/main/java/com/ugent/pidgeon/model/json/DockerTestFeedbackJson.java b/backend/app/src/main/java/com/ugent/pidgeon/model/json/DockerTestFeedbackJson.java new file mode 100644 index 00000000..1fae8f07 --- /dev/null +++ b/backend/app/src/main/java/com/ugent/pidgeon/model/json/DockerTestFeedbackJson.java @@ -0,0 +1,14 @@ +package com.ugent.pidgeon.model.json; + + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.ugent.pidgeon.postgre.models.types.DockerTestType; + +@JsonSerialize(using = DockerTestFeedbackJsonSerializer.class) +public record DockerTestFeedbackJson( + DockerTestType type, + String feedback, + boolean allowed +) { + +} diff --git a/backend/app/src/main/java/com/ugent/pidgeon/model/json/DockerTestFeedbackJsonSerializer.java b/backend/app/src/main/java/com/ugent/pidgeon/model/json/DockerTestFeedbackJsonSerializer.java new file mode 100644 index 00000000..29e541ad --- /dev/null +++ b/backend/app/src/main/java/com/ugent/pidgeon/model/json/DockerTestFeedbackJsonSerializer.java @@ -0,0 +1,24 @@ +package com.ugent.pidgeon.model.json; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.ugent.pidgeon.postgre.models.types.DockerTestType; +import java.io.IOException; + +public class DockerTestFeedbackJsonSerializer extends JsonSerializer { + + @Override + public void serialize(DockerTestFeedbackJson value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeStartObject(); + gen.writeStringField("type", value.type().toString()); + if (value.type() == DockerTestType.TEMPLATE) { + gen.writeFieldName("feedback"); + gen.writeRawValue(value.feedback().replace("\n", "\\n")); + } else { + gen.writeStringField("feedback", value.feedback()); + } + gen.writeBooleanField("allowed", value.allowed()); + gen.writeEndObject(); + } +} + diff --git a/backend/app/src/main/java/com/ugent/pidgeon/model/json/SubmissionJson.java b/backend/app/src/main/java/com/ugent/pidgeon/model/json/SubmissionJson.java index fdf02220..5813934b 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/model/json/SubmissionJson.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/model/json/SubmissionJson.java @@ -14,30 +14,24 @@ public class SubmissionJson { private String fileUrl; private Boolean structureAccepted; - private Boolean dockerAccepted; private String dockerStatus; @JsonSerialize(using = OffsetDateTimeSerializer.class) private OffsetDateTime submissionTime; - private String structureFeedbackUrl; + private String structureFeedback; - public String getDockerFeedbackUrl() { - return dockerFeedbackUrl; - } - public void setDockerFeedbackUrl(String dockerFeedbackUrl) { - this.dockerFeedbackUrl = dockerFeedbackUrl; - } + private DockerTestFeedbackJson dockerFeedback; + - private String dockerFeedbackUrl; public SubmissionJson() { } public SubmissionJson( long id, String projectUrl, String groupUrl, Long projectId, Long groupId, String fileUrl, - Boolean structureAccepted, OffsetDateTime submissionTime, Boolean dockerAccepted, String structureFeedbackUrl, String dockerFeedbackUrl, String dockerStatus) { + Boolean structureAccepted, OffsetDateTime submissionTime, String structureFeedbackUrl, DockerTestFeedbackJson dockerFeedback, String dockerStatus) { this.submissionId = id; this.projectUrl = projectUrl; this.groupUrl = groupUrl; @@ -46,9 +40,8 @@ public SubmissionJson( this.fileUrl = fileUrl; this.structureAccepted = structureAccepted; this.submissionTime = submissionTime; - this.dockerAccepted = dockerAccepted; - this.structureFeedbackUrl = structureFeedbackUrl; - this.dockerFeedbackUrl = dockerFeedbackUrl; + this.dockerFeedback = dockerFeedback; + this.structureFeedback = structureFeedbackUrl; this.dockerStatus = dockerStatus; } @@ -100,20 +93,14 @@ public void setSubmissionTime(OffsetDateTime submissionTime) { this.submissionTime = submissionTime; } - public Boolean getDockerAccepted() { - return dockerAccepted; - } - public void setDockerAccepted(Boolean dockerAccepted) { - this.dockerAccepted = dockerAccepted; - } - public String getStructureFeedbackUrl() { - return structureFeedbackUrl; + public String getStructureFeedback() { + return structureFeedback; } - public void setStructureFeedbackUrl(String structureFeedbackUrl) { - this.structureFeedbackUrl = structureFeedbackUrl; + public void setStructureFeedback(String structureFeedback) { + this.structureFeedback = structureFeedback; } public Long getProjectId() { @@ -139,4 +126,12 @@ public String getDockerStatus() { public void setDockerStatus(String dockerStatus) { this.dockerStatus = dockerStatus; } + + public DockerTestFeedbackJson getDockerFeedback() { + return dockerFeedback; + } + + public void setDockerFeedback(DockerTestFeedbackJson dockerFeedback) { + this.dockerFeedback = dockerFeedback; + } } diff --git a/backend/app/src/main/java/com/ugent/pidgeon/model/submissionTesting/DockerSubtestResult.java b/backend/app/src/main/java/com/ugent/pidgeon/model/submissionTesting/DockerSubtestResult.java index cecc7d51..4d50f98f 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/model/submissionTesting/DockerSubtestResult.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/model/submissionTesting/DockerSubtestResult.java @@ -68,7 +68,7 @@ public boolean isAllowed() { @Override public String getFeedbackAsString() { // Display feedback as a json, only display testName and testDescription if they are not empty - String testDescription = this.testDescription.isEmpty() ? "" : ",\"testDescription\":\"" + this.testDescription + "\""; + String testDescription = this.testDescription.isEmpty() ? "" : "\",\"testDescription\":\"" + this.testDescription; return "{\"testName\":\"" + testName + testDescription + "\",\"correct\":\"" + correct + "\",\"output\":\"" + output + "\"}"; } } diff --git a/backend/app/src/main/java/com/ugent/pidgeon/model/submissionTesting/DockerTemplateTestOutput.java b/backend/app/src/main/java/com/ugent/pidgeon/model/submissionTesting/DockerTemplateTestOutput.java index 3322e6f0..c2b9cbd3 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/model/submissionTesting/DockerTemplateTestOutput.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/model/submissionTesting/DockerTemplateTestOutput.java @@ -26,6 +26,7 @@ public String getFeedbackAsString(){ for (DockerSubtestResult subtestResult : subtestResults) { feedback.append(subtestResult.getFeedbackAsString()).append(","); } - return feedback + "\"allowed\": \"" + allowed + "\"}"; + feedback.append("]"); + return feedback.toString(); } } diff --git a/backend/app/src/main/java/com/ugent/pidgeon/postgre/models/SubmissionEntity.java b/backend/app/src/main/java/com/ugent/pidgeon/postgre/models/SubmissionEntity.java index 7e3e57b3..2ecdcd8e 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/postgre/models/SubmissionEntity.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/postgre/models/SubmissionEntity.java @@ -1,6 +1,6 @@ package com.ugent.pidgeon.postgre.models; -import com.ugent.pidgeon.model.submissionTesting.DockerTestOutput; +import com.ugent.pidgeon.postgre.models.types.DockerTestType; import com.ugent.pidgeon.postgre.models.types.DockerTestState; import jakarta.persistence.*; @@ -41,6 +41,9 @@ public class SubmissionEntity { @Column(name="docker_test_state") private String dockerTestState; + @Column(name="docker_type") + private String dockerType; + public SubmissionEntity() { } @@ -136,4 +139,20 @@ public DockerTestState getDockerTestState() { public void setDockerTestState(DockerTestState dockerTestState) { this.dockerTestState = dockerTestState.toString(); } + + public DockerTestType getDockerTestType() { + if (dockerType == null) { + return DockerTestType.NONE; + } + return switch (dockerType) { + case "SIMPLE" -> DockerTestType.SIMPLE; + case "TEMPLATE" -> DockerTestType.TEMPLATE; + case "NONE" -> DockerTestType.NONE; + default -> null; + }; + } + + public void setDockerType(DockerTestType dockerType) { + this.dockerType = dockerType.toString(); + } } diff --git a/backend/app/src/main/java/com/ugent/pidgeon/postgre/models/types/DockerTestType.java b/backend/app/src/main/java/com/ugent/pidgeon/postgre/models/types/DockerTestType.java new file mode 100644 index 00000000..eefd122c --- /dev/null +++ b/backend/app/src/main/java/com/ugent/pidgeon/postgre/models/types/DockerTestType.java @@ -0,0 +1,7 @@ +package com.ugent.pidgeon.postgre.models.types; + +public enum DockerTestType { + SIMPLE, + TEMPLATE, + NONE +} diff --git a/backend/app/src/main/java/com/ugent/pidgeon/util/EntityToJsonConverter.java b/backend/app/src/main/java/com/ugent/pidgeon/util/EntityToJsonConverter.java index 1b2fc2a6..d2559817 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/util/EntityToJsonConverter.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/util/EntityToJsonConverter.java @@ -6,6 +6,8 @@ import com.ugent.pidgeon.model.json.*; import com.ugent.pidgeon.postgre.models.*; import com.ugent.pidgeon.postgre.models.types.CourseRelation; +import com.ugent.pidgeon.postgre.models.types.DockerTestState; +import com.ugent.pidgeon.postgre.models.types.DockerTestType; import com.ugent.pidgeon.postgre.repository.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -33,9 +35,13 @@ public class EntityToJsonConverter { private SubmissionRepository submissionRepository; @Autowired private ClusterUtil clusterUtil; + @Autowired + private TestUtil testUtil; + @Autowired + private TestRepository testRepository; - public GroupJson groupEntityToJson(GroupEntity groupEntity) { + public GroupJson groupEntityToJson(GroupEntity groupEntity) { GroupClusterEntity cluster = groupClusterRepository.findById(groupEntity.getClusterId()).orElse(null); GroupJson group = new GroupJson(cluster.getMaxSize(), groupEntity.getId(), groupEntity.getName(), ApiRoutes.CLUSTER_BASE_PATH + "/" + groupEntity.getClusterId()); if (cluster != null && cluster.getGroupAmount() > 1){ @@ -222,6 +228,18 @@ public CourseReferenceJson courseEntityToCourseReference(CourseEntity course) { public SubmissionJson getSubmissionJson(SubmissionEntity submission) { + DockerTestFeedbackJson feedback; + TestEntity test = testRepository.findByProjectId(submission.getProjectId()).orElse(null); + if (submission.getDockerTestState().equals(DockerTestState.running)) { + feedback = null; + } else if (submission.getDockerTestType().equals(DockerTestType.NONE)) { + feedback = new DockerTestFeedbackJson(DockerTestType.NONE, "", true); + } + else if (submission.getDockerTestType().equals(DockerTestType.SIMPLE)) { + feedback = new DockerTestFeedbackJson(DockerTestType.SIMPLE, submission.getDockerFeedback(), submission.getDockerAccepted()); + } else { + feedback = new DockerTestFeedbackJson(DockerTestType.TEMPLATE, submission.getDockerFeedback(), submission.getDockerAccepted()); + } return new SubmissionJson( submission.getId(), ApiRoutes.PROJECT_BASE_PATH + "/" + submission.getProjectId(), @@ -231,9 +249,8 @@ public SubmissionJson getSubmissionJson(SubmissionEntity submission) { ApiRoutes.SUBMISSION_BASE_PATH + "/" + submission.getId() + "/file", submission.getStructureAccepted(), submission.getSubmissionTime(), - submission.getDockerAccepted(), - ApiRoutes.SUBMISSION_BASE_PATH + "/" + submission.getId() + "/structurefeedback", - ApiRoutes.SUBMISSION_BASE_PATH + "/" + submission.getId() + "/dockerfeedback", + submission.getStructureFeedback(), + feedback, submission.getDockerTestState().toString() ); } diff --git a/backend/app/tmp/dockerTestOutput1714216839420/artifacts/HelloWorld b/backend/app/tmp/dockerTestOutput1714216839420/artifacts/HelloWorld deleted file mode 100644 index fb62334e..00000000 --- a/backend/app/tmp/dockerTestOutput1714216839420/artifacts/HelloWorld +++ /dev/null @@ -1 +0,0 @@ -HelloWorld! diff --git a/backend/app/tmp/dockerTestOutput1714216889818/artifacts/HelloWorld b/backend/app/tmp/dockerTestOutput1714216889818/artifacts/HelloWorld deleted file mode 100644 index fb62334e..00000000 --- a/backend/app/tmp/dockerTestOutput1714216889818/artifacts/HelloWorld +++ /dev/null @@ -1 +0,0 @@ -HelloWorld! diff --git a/backend/app/tmp/dockerTestOutput1714216912989/artifacts/HelloWorld b/backend/app/tmp/dockerTestOutput1714216912989/artifacts/HelloWorld deleted file mode 100644 index fb62334e..00000000 --- a/backend/app/tmp/dockerTestOutput1714216912989/artifacts/HelloWorld +++ /dev/null @@ -1 +0,0 @@ -HelloWorld! diff --git a/backend/app/tmp/dockerTestOutput1714216961554/artifacts/HelloWorld b/backend/app/tmp/dockerTestOutput1714216961554/artifacts/HelloWorld deleted file mode 100644 index fb62334e..00000000 --- a/backend/app/tmp/dockerTestOutput1714216961554/artifacts/HelloWorld +++ /dev/null @@ -1 +0,0 @@ -HelloWorld! diff --git a/backend/app/tmp/dockerTestOutput1714224774160/input/helloworld.txt b/backend/app/tmp/dockerTestOutput1714224774160/input/helloworld.txt deleted file mode 100644 index 023130af..00000000 --- a/backend/app/tmp/dockerTestOutput1714224774160/input/helloworld.txt +++ /dev/null @@ -1 +0,0 @@ -Hello Happy World! \ No newline at end of file diff --git a/backend/app/tmp/dockerTestOutput1714224808414/input/helloworld.txt b/backend/app/tmp/dockerTestOutput1714224808414/input/helloworld.txt deleted file mode 100644 index 023130af..00000000 --- a/backend/app/tmp/dockerTestOutput1714224808414/input/helloworld.txt +++ /dev/null @@ -1 +0,0 @@ -Hello Happy World! \ No newline at end of file diff --git a/backend/app/tmp/dockerTestOutput1714224826998/input/helloworld.txt b/backend/app/tmp/dockerTestOutput1714224826998/input/helloworld.txt deleted file mode 100644 index 023130af..00000000 --- a/backend/app/tmp/dockerTestOutput1714224826998/input/helloworld.txt +++ /dev/null @@ -1 +0,0 @@ -Hello Happy World! \ No newline at end of file diff --git a/backend/app/tmp/test/HelloWorld.sh b/backend/app/tmp/test/HelloWorld.sh deleted file mode 100644 index c01f9ac9..00000000 --- a/backend/app/tmp/test/HelloWorld.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -echo 'HelloWorld!' \ No newline at end of file diff --git a/backend/app/tmp/test/HelloWorld2.sh b/backend/app/tmp/test/HelloWorld2.sh deleted file mode 100644 index d52b7493..00000000 --- a/backend/app/tmp/test/HelloWorld2.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -echo 'HelloWorld2!' \ No newline at end of file diff --git a/backend/app/tmp/test/testInput b/backend/app/tmp/test/testInput deleted file mode 100644 index 7ef4f4c1..00000000 --- a/backend/app/tmp/test/testInput +++ /dev/null @@ -1 +0,0 @@ -This is a test input file diff --git a/backend/database/start_database.sql b/backend/database/start_database.sql index 818b9d02..41cffe31 100644 --- a/backend/database/start_database.sql +++ b/backend/database/start_database.sql @@ -109,6 +109,7 @@ CREATE TABLE submissions ( structure_feedback TEXT, docker_feedback TEXT, docker_test_state VARCHAR(10) DEFAULT "running", + docker_type VARCHAR(10) DEFAULT "simple", submission_time TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP );