diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..ac8dcdd5ebbd9abf5947e6a98b6e805cc27784a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/local-maven-repo \ No newline at end of file diff --git a/src/main/java/de/hftstuttgart/dtabackend/rest/v1/task/TaskUpload.java b/src/main/java/de/hftstuttgart/dtabackend/rest/v1/task/TaskUpload.java index 7b140901d99b9527ca5442189d0eab0f33d2cda5..2eec4e14995a14c6546ac12df02c6a9461628b10 100644 --- a/src/main/java/de/hftstuttgart/dtabackend/rest/v1/task/TaskUpload.java +++ b/src/main/java/de/hftstuttgart/dtabackend/rest/v1/task/TaskUpload.java @@ -1,109 +1,110 @@ -package de.hftstuttgart.dtabackend.rest.v1.task; - -import de.hftstuttgart.dtabackend.utils.*; -import de.hftstuttgart.dtabackend.models.ResultSummary; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.tika.Tika; -import org.springframework.core.env.Environment; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import jakarta.servlet.annotation.MultipartConfig; - -/** - * Rest controller for everything related to the TASK files - */ -@RestController -@RequestMapping("/v1/task/*") -@MultipartConfig -public class TaskUpload { - private static final Logger LOG = LogManager.getLogger(TaskUpload.class); - - private final RepoUtil repoUtil; - private final Path testTmpPath; - private final ExecuteTestUtil executeTestUtil; - - public TaskUpload( - Environment env, - RepoUtil repoUtil, - ExecuteTestUtil executeTestUtil - ) { - this.repoUtil = repoUtil; - this.executeTestUtil = executeTestUtil; - - // set path of temporary directory on host and inside our container - this.testTmpPath = Paths.get(env.getProperty("tests.tmp.dir")); - } - - @RequestMapping(method = RequestMethod.POST) - public ResultSummary uploadAndTestFile(@RequestParam("taskFile") MultipartFile taskFileRef, - @RequestParam("assignmentId") String assignmentId - ) throws IOException, InterruptedException { - LOG.info("submission for testing received"); - - LOG.debug("creating new temporary directory"); - Path workDirectory = Files.createTempDirectory(testTmpPath, "dta"); - LOG.debug(String.format("working dir for test is: %s", workDirectory.toAbsolutePath().toString())); - - // define paths for the test, the submission and where the result is to be expected afterwards - Path srcPath = Paths.get(workDirectory.toAbsolutePath().toString(), "src"); - - String mimeInfo = new Tika().detect(taskFileRef.getInputStream()); - switch (mimeInfo) { - case "text/plain": - LOG.debug("textfile uploaded, searching for dta config"); - // find URI in config file - String subDir=""; - Matcher config = RegexUtil.extractConfig(taskFileRef.getInputStream(), Pattern.compile(RegexUtil.DTA_SUBMISSIONCONFIGREGEX)); - if(config==null) { - config = RegexUtil.extractConfig(taskFileRef.getInputStream(), Pattern.compile(RegexUtil.SUBMISSIONCONFIGREGEX)); - if(config==null) - { - throw new RuntimeException("couldn't find repo config for student submission clone"); - } - } - else { - subDir=config.group(4); - } - LOG.debug("calling repo clone"); - repoUtil.cloneRepository(config, srcPath.toAbsolutePath().toString(), subDir); - break; - - case "application/zip": - LOG.debug("zip archive uploaded, extracting content as student submission"); - ArchiveUtil.extractProjectFromZip(taskFileRef.getInputStream(), srcPath.toAbsolutePath()); - break; - - default: - String msg = String.format("couldn't process uploaded file with mime type %s", mimeInfo); - LOG.error(msg); - throw new RuntimeException(msg); - } - - // run test - LOG.debug("calling test execution"); - ResultSummary resultSummary = executeTestUtil.runTests(assignmentId, workDirectory); - - if (mimeInfo.equals("text/plain")) { - LOG.info("check for provided Ticketsystem information"); - UnifiedTicketingUtil.reportResults(taskFileRef.getInputStream(), resultSummary); - } - - taskFileRef.getInputStream().close(); - - LOG.info("submission tested successfully"); - return resultSummary; - } -} +package de.hftstuttgart.dtabackend.rest.v1.task; + +import de.hftstuttgart.dtabackend.utils.*; +import de.hftstuttgart.dtabackend.models.ResultSummary; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.tika.Tika; +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import jakarta.servlet.annotation.MultipartConfig; + +/** + * Rest controller for everything related to the TASK files + */ +@RestController +@RequestMapping("/v1/task/*") +@MultipartConfig +public class TaskUpload { + private static final Logger LOG = LogManager.getLogger(TaskUpload.class); + + private final RepoUtil repoUtil; + private final Path testTmpPath; + private final ExecuteTestUtil executeTestUtil; + + public TaskUpload( + Environment env, + RepoUtil repoUtil, + ExecuteTestUtil executeTestUtil + ) { + this.repoUtil = repoUtil; + this.executeTestUtil = executeTestUtil; + + // set path of temporary directory on host and inside our container + this.testTmpPath = Paths.get(env.getProperty("tests.tmp.dir")); + } + + @RequestMapping(method = RequestMethod.POST) + public ResultSummary uploadAndTestFile(@RequestParam("taskFile") MultipartFile taskFileRef, + @RequestParam("assignmentId") String assignmentId + ) throws IOException, InterruptedException { + LOG.info("submission for testing received"); + + LOG.debug("creating new temporary directory"); + Path workDirectory = Files.createTempDirectory(testTmpPath, "dta"); + LOG.debug(String.format("working dir for test is: %s", workDirectory.toAbsolutePath().toString())); + + // define paths for the test, the submission and where the result is to be expected afterwards + Path srcPath = Paths.get(workDirectory.toAbsolutePath().toString(), "src"); + LOG.debug(String.format("Source path defined as: %s", srcPath.toAbsolutePath().toString())); + + String mimeInfo = new Tika().detect(taskFileRef.getInputStream()); + switch (mimeInfo) { + case "text/plain": + LOG.debug("textfile uploaded, searching for dta config"); + // find URI in config file + String subDir=""; + Matcher config = RegexUtil.extractConfig(taskFileRef.getInputStream(), Pattern.compile(RegexUtil.DTA_SUBMISSIONCONFIGREGEX)); + if(config==null) { + config = RegexUtil.extractConfig(taskFileRef.getInputStream(), Pattern.compile(RegexUtil.SUBMISSIONCONFIGREGEX)); + if(config==null) + { + throw new RuntimeException("couldn't find repo config for student submission clone"); + } + } + else { + subDir=config.group(4); + } + LOG.debug("calling repo clone"); + repoUtil.cloneRepository(config, srcPath.toAbsolutePath().toString(), subDir); + break; + + case "application/zip": + LOG.debug("zip archive uploaded, extracting content as student submission"); + ArchiveUtil.extractProjectFromZip(taskFileRef.getInputStream(), srcPath.toAbsolutePath()); + break; + + default: + String msg = String.format("couldn't process uploaded file with mime type %s", mimeInfo); + LOG.error(msg); + throw new RuntimeException(msg); + } + + // run test + LOG.debug("calling test execution"); + ResultSummary resultSummary = executeTestUtil.runTests(assignmentId, workDirectory); + + if (mimeInfo.equals("text/plain")) { + LOG.info("check for provided Ticketsystem information"); + UnifiedTicketingUtil.reportResults(taskFileRef.getInputStream(), resultSummary); + } + + taskFileRef.getInputStream().close(); + + LOG.info("submission tested successfully"); + return resultSummary; + } +} diff --git a/src/main/java/de/hftstuttgart/dtabackend/rest/v1/unittest/UnitTestUpload.java b/src/main/java/de/hftstuttgart/dtabackend/rest/v1/unittest/UnitTestUpload.java index 79cc5ed7f0abf47a1d274e5bbec9781385edbf77..924f9821076ea24bddd39cd88facce9dd1b30e88 100644 --- a/src/main/java/de/hftstuttgart/dtabackend/rest/v1/unittest/UnitTestUpload.java +++ b/src/main/java/de/hftstuttgart/dtabackend/rest/v1/unittest/UnitTestUpload.java @@ -1,113 +1,116 @@ -package de.hftstuttgart.dtabackend.rest.v1.unittest; - -import de.hftstuttgart.dtabackend.utils.RepoUtil; -import de.hftstuttgart.dtabackend.utils.RegexUtil; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.springframework.core.env.Environment; -import org.springframework.util.FileSystemUtils; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; - -import jakarta.servlet.annotation.MultipartConfig; - -import java.io.*; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Rest controller for anything related to the TEST files. - */ -@RestController -@RequestMapping("/v1/unittest") -@MultipartConfig -public class UnitTestUpload { - - private static final Logger LOG = LogManager.getLogger(UnitTestUpload.class); - private final RepoUtil repoUtil; - private final String assignmentBasePath; - - public UnitTestUpload(Environment env, RepoUtil repoUtil) { - this.repoUtil = repoUtil; - - Path p = Paths.get(env.getProperty("data.dir"), env.getProperty("data.dir.test.folder.name")); - this.assignmentBasePath = p.toAbsolutePath().toString(); - } - - /** - * Create a subfolder for the specific assignment. - * This is called when the teacher creates an assignment and uploads the JUnit test files - * - * @param unitTestFileRef The text file which contains the JUnit tests meta data - * @param assignmentId ID of the created assignment. Generated by Moodle - */ - @RequestMapping(method = RequestMethod.POST) - public void uploadUnitTestFile( - @RequestParam("unitTestFile") MultipartFile unitTestFileRef, - @RequestParam("assignmentId") String assignmentId - ) throws IOException { - LOG.info("received new assignment"); - - File file = Paths.get(this.assignmentBasePath, assignmentId + ".txt").toFile(); - file.mkdirs(); - - // save assignment config - unitTestFileRef.transferTo(file); - LOG.debug(String.format("saved config file to: %s", file.getAbsolutePath())); - - String subDir=""; - Pattern pattern = Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX); - - Matcher config = RegexUtil.extractConfig(new FileInputStream(file), pattern); - if (config == null) { - pattern=Pattern.compile(RegexUtil.TESTCONFIGREGEX); - config = RegexUtil.extractConfig(new FileInputStream(file), pattern); - if(config==null) - { - throw new RuntimeException("couldn't find repo config for unittest clone"); - } - } - else { - subDir=config.group(4); - } - LOG.debug("calling test repo clone"); - // cloning assignment repo to persistent space - repoUtil.cloneRepository(config, Paths.get(this.assignmentBasePath, assignmentId).toAbsolutePath().toString(), subDir); - - LOG.info(String.format("stored new assignment: %s", file.getAbsolutePath())); - } - - /** - * Delete the folder for the assignment. - * Called when the teacher deletes the JUnitTest assignment - * <p> - * {{url}}:8080/v1/unittest?assignmentId=111 - * - * @param assignmentId ID of the assignment to delete. Generated by Moodle - */ - @RequestMapping(method = RequestMethod.DELETE) - public void deleteUnitTestFiles(@RequestParam("assignmentId") String assignmentId) { - LOG.info(String.format("received deletion order for assignment %s", assignmentId)); - - // deleting config file - File file = Paths.get( - this.assignmentBasePath, - assignmentId + ".txt") - .toFile(); - file.delete(); - - // deleting local copy of repository - file = Paths.get( - this.assignmentBasePath, - assignmentId).toFile(); - FileSystemUtils.deleteRecursively(file); - - LOG.info(String.format("assignment %s deletion complete", assignmentId)); - } -} +package de.hftstuttgart.dtabackend.rest.v1.unittest; + +import de.hftstuttgart.dtabackend.utils.RepoUtil; +import de.hftstuttgart.dtabackend.utils.RegexUtil; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.core.env.Environment; +import org.springframework.util.FileSystemUtils; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.servlet.annotation.MultipartConfig; + +import java.io.*; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Rest controller for anything related to the TEST files. + */ +@RestController +@RequestMapping("/v1/unittest") +@MultipartConfig +public class UnitTestUpload { + + private static final Logger LOG = LogManager.getLogger(UnitTestUpload.class); + private final RepoUtil repoUtil; + private final String assignmentBasePath; + + public UnitTestUpload(Environment env, RepoUtil repoUtil) { + this.repoUtil = repoUtil; + + Path p = Paths.get(env.getProperty("data.dir"), env.getProperty("data.dir.test.folder.name")); + this.assignmentBasePath = p.toAbsolutePath().toString(); + LOG.debug(String.format("Assignment base path initialized as: %s", this.assignmentBasePath)); + } + + /** + * Create a subfolder for the specific assignment. + * This is called when the teacher creates an assignment and uploads the JUnit test files + * + * @param unitTestFileRef The text file which contains the JUnit tests meta data + * @param assignmentId ID of the created assignment. Generated by Moodle + */ + @RequestMapping(method = RequestMethod.POST) + public void uploadUnitTestFile( + @RequestParam("unitTestFile") MultipartFile unitTestFileRef, + @RequestParam("assignmentId") String assignmentId + ) throws IOException { + LOG.info("received new assignment"); + + File file = Paths.get(this.assignmentBasePath, assignmentId + ".txt").toFile(); + file.mkdirs(); + + // save assignment config + unitTestFileRef.transferTo(file); + LOG.debug(String.format("saved config file to: %s", file.getAbsolutePath())); + + String subDir=""; + Pattern pattern = Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX); + + Matcher config = RegexUtil.extractConfig(new FileInputStream(file), pattern); + if (config == null) { + pattern=Pattern.compile(RegexUtil.TESTCONFIGREGEX); + config = RegexUtil.extractConfig(new FileInputStream(file), pattern); + if(config==null) + { + throw new RuntimeException("couldn't find repo config for unittest clone"); + } + } + else { + subDir=config.group(4); + } + LOG.debug("calling test repo clone"); + // cloning assignment repo to persistent space + repoUtil.cloneRepository(config, Paths.get(this.assignmentBasePath, assignmentId).toAbsolutePath().toString(), subDir); + + LOG.info(String.format("stored new assignment: %s", file.getAbsolutePath())); + } + + /** + * Delete the folder for the assignment. + * Called when the teacher deletes the JUnitTest assignment + * <p> + * {{url}}:8080/v1/unittest?assignmentId=111 + * + * @param assignmentId ID of the assignment to delete. Generated by Moodle + */ + @RequestMapping(method = RequestMethod.DELETE) + public void deleteUnitTestFiles(@RequestParam("assignmentId") String assignmentId) { + LOG.info(String.format("received deletion order for assignment %s", assignmentId)); + + // deleting config file + File file = Paths.get( + this.assignmentBasePath, + assignmentId + ".txt") + .toFile(); + file.delete(); + LOG.debug(String.format("Deleted file: %s", file.getAbsolutePath())); + + // deleting local copy of repository + file = Paths.get( + this.assignmentBasePath, + assignmentId).toFile(); + FileSystemUtils.deleteRecursively(file); + LOG.debug(String.format("Deleted directory: %s", file.getAbsolutePath())); + + LOG.info(String.format("assignment %s deletion complete", assignmentId)); + } +} diff --git a/src/main/java/de/hftstuttgart/dtabackend/utils/ArchiveUtil.java b/src/main/java/de/hftstuttgart/dtabackend/utils/ArchiveUtil.java index 0408c09433883844db132c0790350172ff1c0b33..8bd320882a35a51af78ea5f239a472f9cb12d428 100644 --- a/src/main/java/de/hftstuttgart/dtabackend/utils/ArchiveUtil.java +++ b/src/main/java/de/hftstuttgart/dtabackend/utils/ArchiveUtil.java @@ -19,6 +19,7 @@ public class ArchiveUtil ZipEntry entry; while ((entry = stream.getNextEntry()) != null) { + LOG.debug(String.format("Processing zip entry: %s", entry.getName())); File file = outDir.resolve(entry.getName()).toFile(); LOG.debug(String.format("processing entry %s", file.getAbsolutePath())); @@ -28,6 +29,7 @@ public class ArchiveUtil LOG.debug("creating new file"); file.createNewFile(); + LOG.debug(String.format("Created new file: %s", file.getAbsolutePath())); byte[] buffer = new byte[2048]; try (FileOutputStream fos = new FileOutputStream(file); diff --git a/src/main/java/de/hftstuttgart/dtabackend/utils/ExecuteTestUtil.java b/src/main/java/de/hftstuttgart/dtabackend/utils/ExecuteTestUtil.java index 333b1771acb94d118d24e7d4b318bfcb3ea356db..1fb38614fa145d96c3ee6c64f2879c7da6396884 100644 --- a/src/main/java/de/hftstuttgart/dtabackend/utils/ExecuteTestUtil.java +++ b/src/main/java/de/hftstuttgart/dtabackend/utils/ExecuteTestUtil.java @@ -67,6 +67,16 @@ public class ExecuteTestUtil { Paths.get(assignmentBasePath, assignmentId), testPath); + LOG.debug("copying exercise manifest from: %s in testPath: %s", + assignmentBasePath + assignmentId + "_checkout", + testPath.toString() ); + Files.copy(Paths.get( + assignmentBasePath, assignmentId + "_checkout", + CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME), + Paths.get( + testPath.toString(), + CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME)); + LOG.debug("copy test config"); Files.copy( Paths.get(assignmentBasePath, assignmentId + ".txt"), @@ -140,14 +150,25 @@ public class ExecuteTestUtil { LOG.info("Checking for optional test competency profile information for paedagogical agent functionality..."); List<TestCompetencyProfile> testCompetencyProfiles=CompetencyAssessmentUtil.readTestCompetencyProfiles(testPathHost, CompetencyAssessmentUtil.TEST_COMPETENCY_MANIFEST_FILE_NAME); - if(testCompetencyProfiles!=null) { + LOG.debug(String.format( + "Reading Test Competency Profiles: basePath=%s, fileName=%s", + testPathHost, + CompetencyAssessmentUtil.TEST_COMPETENCY_MANIFEST_FILE_NAME + )); + if(testCompetencyProfiles!=null) { LOG.info("Found optional test competency profiles, generating agent profile data..."); resultSummary.overallTestCompetencyProfile=CompetencyAssessmentUtil.packFloats(CompetencyAssessmentUtil.sumTestCompetencyProfiles(testCompetencyProfiles)); resultSummary.successfulTestCompetencyProfile=CompetencyAssessmentUtil.packFloats(CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary, true)); LOG.info("Checking for optional exercise competency profile information for paedagogical agent exercise recommendation functionality..."); //testPathHost or assignmentBasePath - Path exerciseManifestFile = Paths.get(testPathHost.toAbsolutePath().toString(), CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME); + Path exerciseManifestFile = Paths.get(testPathHost.toString(), CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME); + LOG.debug(String.format( + "Constructing Path for exercise manifest: testPath=%s, fileName=%s -> Resulting Path=%s", + testPathHost.toString(), + CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME, + exerciseManifestFile.toString() + )); if (Files.exists(exerciseManifestFile)) { LOG.info("Found optional exercise competency profiles, generating recommendations..."); resultSummary.recommendations = recommendNextExercises(assignmentId, testPathHost, testCompetencyProfiles, resultSummary); diff --git a/src/main/java/de/hftstuttgart/dtabackend/utils/FileUtil.java b/src/main/java/de/hftstuttgart/dtabackend/utils/FileUtil.java index edf8324b64dff63dc165b0c228a518d933e00a6a..f185b9cc96528bf447b5f7583518edc764fbf895 100644 --- a/src/main/java/de/hftstuttgart/dtabackend/utils/FileUtil.java +++ b/src/main/java/de/hftstuttgart/dtabackend/utils/FileUtil.java @@ -24,10 +24,12 @@ public class FileUtil { deleteFolderRecursively(f); } else { f.delete(); + System.out.println(String.format("Deleted file: %s", f.getAbsolutePath())); } } } folder.delete(); + System.out.println(String.format("Deleted folder: %s", folder.getAbsolutePath())); } public static void copyFolder(Path src, Path dst) throws IOException { @@ -35,6 +37,7 @@ public class FileUtil { .forEach(source -> { try { Files.copy(source, dst.resolve(src.relativize(source))); + System.out.println(String.format("Copying file from: %s to %s", source.toString(), dst.resolve(src.relativize(source)).toString())); } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } diff --git a/src/main/java/de/hftstuttgart/dtabackend/utils/RepoUtil.java b/src/main/java/de/hftstuttgart/dtabackend/utils/RepoUtil.java index f2c85877fec70f13f7b1f0a5fb56af80024363e3..334bcd71850290ce017715d3cc6b5ddee9f65719 100644 --- a/src/main/java/de/hftstuttgart/dtabackend/utils/RepoUtil.java +++ b/src/main/java/de/hftstuttgart/dtabackend/utils/RepoUtil.java @@ -104,7 +104,7 @@ public class RepoUtil { //copy appropriate path from checkout directory to target directory FileSystemUtils.copyRecursively(new File(checkoutDirectory+subDir), new File(targetPath)); } - LOG.debug(String.format("cloned from %s to %s", config.group(1), targetDirectory)); + LOG.debug(String.format("cloned from %s via %s to %s", config.group(1), checkoutDirectory+subDir, targetDirectory)); } catch (IOException e) { LOG.error(String.format("Error while cloning from %s: could not copy to unit test dir", config.group(1)), e);