Commit a7e5ee27 authored by mamunozgil's avatar mamunozgil
Browse files

Solve Issue: Finding exercise-manifest

1 merge request!1Solve Issue: Finding exercise-manifest
Showing with 257 additions and 225 deletions
+257 -225
/target
/local-maven-repo
\ No newline at end of file
package de.hftstuttgart.dtabackend.rest.v1.task; package de.hftstuttgart.dtabackend.rest.v1.task;
import de.hftstuttgart.dtabackend.utils.*; import de.hftstuttgart.dtabackend.utils.*;
import de.hftstuttgart.dtabackend.models.ResultSummary; import de.hftstuttgart.dtabackend.models.ResultSummary;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.tika.Tika; import org.apache.tika.Tika;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.*; import java.io.*;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import jakarta.servlet.annotation.MultipartConfig; import jakarta.servlet.annotation.MultipartConfig;
/** /**
* Rest controller for everything related to the TASK files * Rest controller for everything related to the TASK files
*/ */
@RestController @RestController
@RequestMapping("/v1/task/*") @RequestMapping("/v1/task/*")
@MultipartConfig @MultipartConfig
public class TaskUpload { public class TaskUpload {
private static final Logger LOG = LogManager.getLogger(TaskUpload.class); private static final Logger LOG = LogManager.getLogger(TaskUpload.class);
private final RepoUtil repoUtil; private final RepoUtil repoUtil;
private final Path testTmpPath; private final Path testTmpPath;
private final ExecuteTestUtil executeTestUtil; private final ExecuteTestUtil executeTestUtil;
public TaskUpload( public TaskUpload(
Environment env, Environment env,
RepoUtil repoUtil, RepoUtil repoUtil,
ExecuteTestUtil executeTestUtil ExecuteTestUtil executeTestUtil
) { ) {
this.repoUtil = repoUtil; this.repoUtil = repoUtil;
this.executeTestUtil = executeTestUtil; this.executeTestUtil = executeTestUtil;
// set path of temporary directory on host and inside our container // set path of temporary directory on host and inside our container
this.testTmpPath = Paths.get(env.getProperty("tests.tmp.dir")); this.testTmpPath = Paths.get(env.getProperty("tests.tmp.dir"));
} }
@RequestMapping(method = RequestMethod.POST) @RequestMapping(method = RequestMethod.POST)
public ResultSummary uploadAndTestFile(@RequestParam("taskFile") MultipartFile taskFileRef, public ResultSummary uploadAndTestFile(@RequestParam("taskFile") MultipartFile taskFileRef,
@RequestParam("assignmentId") String assignmentId @RequestParam("assignmentId") String assignmentId
) throws IOException, InterruptedException { ) throws IOException, InterruptedException {
LOG.info("submission for testing received"); LOG.info("submission for testing received");
LOG.debug("creating new temporary directory"); LOG.debug("creating new temporary directory");
Path workDirectory = Files.createTempDirectory(testTmpPath, "dta"); Path workDirectory = Files.createTempDirectory(testTmpPath, "dta");
LOG.debug(String.format("working dir for test is: %s", workDirectory.toAbsolutePath().toString())); 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 // define paths for the test, the submission and where the result is to be expected afterwards
Path srcPath = Paths.get(workDirectory.toAbsolutePath().toString(), "src"); 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) { String mimeInfo = new Tika().detect(taskFileRef.getInputStream());
case "text/plain": switch (mimeInfo) {
LOG.debug("textfile uploaded, searching for dta config"); case "text/plain":
// find URI in config file LOG.debug("textfile uploaded, searching for dta config");
String subDir=""; // find URI in config file
Matcher config = RegexUtil.extractConfig(taskFileRef.getInputStream(), Pattern.compile(RegexUtil.DTA_SUBMISSIONCONFIGREGEX)); String subDir="";
if(config==null) { Matcher config = RegexUtil.extractConfig(taskFileRef.getInputStream(), Pattern.compile(RegexUtil.DTA_SUBMISSIONCONFIGREGEX));
config = RegexUtil.extractConfig(taskFileRef.getInputStream(), Pattern.compile(RegexUtil.SUBMISSIONCONFIGREGEX)); if(config==null) {
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"); {
} throw new RuntimeException("couldn't find repo config for student submission clone");
} }
else { }
subDir=config.group(4); else {
} subDir=config.group(4);
LOG.debug("calling repo clone"); }
repoUtil.cloneRepository(config, srcPath.toAbsolutePath().toString(), subDir); LOG.debug("calling repo clone");
break; repoUtil.cloneRepository(config, srcPath.toAbsolutePath().toString(), subDir);
break;
case "application/zip":
LOG.debug("zip archive uploaded, extracting content as student submission"); case "application/zip":
ArchiveUtil.extractProjectFromZip(taskFileRef.getInputStream(), srcPath.toAbsolutePath()); LOG.debug("zip archive uploaded, extracting content as student submission");
break; ArchiveUtil.extractProjectFromZip(taskFileRef.getInputStream(), srcPath.toAbsolutePath());
break;
default:
String msg = String.format("couldn't process uploaded file with mime type %s", mimeInfo); default:
LOG.error(msg); String msg = String.format("couldn't process uploaded file with mime type %s", mimeInfo);
throw new RuntimeException(msg); LOG.error(msg);
} throw new RuntimeException(msg);
}
// run test
LOG.debug("calling test execution"); // run test
ResultSummary resultSummary = executeTestUtil.runTests(assignmentId, workDirectory); LOG.debug("calling test execution");
ResultSummary resultSummary = executeTestUtil.runTests(assignmentId, workDirectory);
if (mimeInfo.equals("text/plain")) {
LOG.info("check for provided Ticketsystem information"); if (mimeInfo.equals("text/plain")) {
UnifiedTicketingUtil.reportResults(taskFileRef.getInputStream(), resultSummary); LOG.info("check for provided Ticketsystem information");
} UnifiedTicketingUtil.reportResults(taskFileRef.getInputStream(), resultSummary);
}
taskFileRef.getInputStream().close();
taskFileRef.getInputStream().close();
LOG.info("submission tested successfully");
return resultSummary; LOG.info("submission tested successfully");
} return resultSummary;
} }
}
package de.hftstuttgart.dtabackend.rest.v1.unittest; package de.hftstuttgart.dtabackend.rest.v1.unittest;
import de.hftstuttgart.dtabackend.utils.RepoUtil; import de.hftstuttgart.dtabackend.utils.RepoUtil;
import de.hftstuttgart.dtabackend.utils.RegexUtil; import de.hftstuttgart.dtabackend.utils.RegexUtil;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.util.FileSystemUtils; import org.springframework.util.FileSystemUtils;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.annotation.MultipartConfig; import jakarta.servlet.annotation.MultipartConfig;
import java.io.*; import java.io.*;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
* Rest controller for anything related to the TEST files. * Rest controller for anything related to the TEST files.
*/ */
@RestController @RestController
@RequestMapping("/v1/unittest") @RequestMapping("/v1/unittest")
@MultipartConfig @MultipartConfig
public class UnitTestUpload { public class UnitTestUpload {
private static final Logger LOG = LogManager.getLogger(UnitTestUpload.class); private static final Logger LOG = LogManager.getLogger(UnitTestUpload.class);
private final RepoUtil repoUtil; private final RepoUtil repoUtil;
private final String assignmentBasePath; private final String assignmentBasePath;
public UnitTestUpload(Environment env, RepoUtil repoUtil) { public UnitTestUpload(Environment env, RepoUtil repoUtil) {
this.repoUtil = repoUtil; this.repoUtil = repoUtil;
Path p = Paths.get(env.getProperty("data.dir"), env.getProperty("data.dir.test.folder.name")); Path p = Paths.get(env.getProperty("data.dir"), env.getProperty("data.dir.test.folder.name"));
this.assignmentBasePath = p.toAbsolutePath().toString(); 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 * 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 * @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( @RequestMapping(method = RequestMethod.POST)
@RequestParam("unitTestFile") MultipartFile unitTestFileRef, public void uploadUnitTestFile(
@RequestParam("assignmentId") String assignmentId @RequestParam("unitTestFile") MultipartFile unitTestFileRef,
) throws IOException { @RequestParam("assignmentId") String assignmentId
LOG.info("received new assignment"); ) throws IOException {
LOG.info("received new assignment");
File file = Paths.get(this.assignmentBasePath, assignmentId + ".txt").toFile();
file.mkdirs(); File file = Paths.get(this.assignmentBasePath, assignmentId + ".txt").toFile();
file.mkdirs();
// save assignment config
unitTestFileRef.transferTo(file); // save assignment config
LOG.debug(String.format("saved config file to: %s", file.getAbsolutePath())); unitTestFileRef.transferTo(file);
LOG.debug(String.format("saved config file to: %s", file.getAbsolutePath()));
String subDir="";
Pattern pattern = Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX); String subDir="";
Pattern pattern = Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX);
Matcher config = RegexUtil.extractConfig(new FileInputStream(file), pattern);
if (config == null) { Matcher config = RegexUtil.extractConfig(new FileInputStream(file), pattern);
pattern=Pattern.compile(RegexUtil.TESTCONFIGREGEX); if (config == null) {
config = RegexUtil.extractConfig(new FileInputStream(file), pattern); pattern=Pattern.compile(RegexUtil.TESTCONFIGREGEX);
if(config==null) config = RegexUtil.extractConfig(new FileInputStream(file), pattern);
{ if(config==null)
throw new RuntimeException("couldn't find repo config for unittest clone"); {
} throw new RuntimeException("couldn't find repo config for unittest clone");
} }
else { }
subDir=config.group(4); else {
} subDir=config.group(4);
LOG.debug("calling test repo clone"); }
// cloning assignment repo to persistent space LOG.debug("calling test repo clone");
repoUtil.cloneRepository(config, Paths.get(this.assignmentBasePath, assignmentId).toAbsolutePath().toString(), subDir); // 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()));
} LOG.info(String.format("stored new assignment: %s", file.getAbsolutePath()));
}
/**
* Delete the folder for the assignment. /**
* Called when the teacher deletes the JUnitTest assignment * Delete the folder for the assignment.
* <p> * Called when the teacher deletes the JUnitTest assignment
* {{url}}:8080/v1/unittest?assignmentId=111 * <p>
* * {{url}}:8080/v1/unittest?assignmentId=111
* @param assignmentId ID of the assignment to delete. Generated by Moodle *
*/ * @param assignmentId ID of the assignment to delete. Generated by Moodle
@RequestMapping(method = RequestMethod.DELETE) */
public void deleteUnitTestFiles(@RequestParam("assignmentId") String assignmentId) { @RequestMapping(method = RequestMethod.DELETE)
LOG.info(String.format("received deletion order for assignment %s", assignmentId)); 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( // deleting config file
this.assignmentBasePath, File file = Paths.get(
assignmentId + ".txt") this.assignmentBasePath,
.toFile(); assignmentId + ".txt")
file.delete(); .toFile();
file.delete();
// deleting local copy of repository LOG.debug(String.format("Deleted file: %s", file.getAbsolutePath()));
file = Paths.get(
this.assignmentBasePath, // deleting local copy of repository
assignmentId).toFile(); file = Paths.get(
FileSystemUtils.deleteRecursively(file); this.assignmentBasePath,
assignmentId).toFile();
LOG.info(String.format("assignment %s deletion complete", assignmentId)); FileSystemUtils.deleteRecursively(file);
} LOG.debug(String.format("Deleted directory: %s", file.getAbsolutePath()));
}
LOG.info(String.format("assignment %s deletion complete", assignmentId));
}
}
...@@ -19,6 +19,7 @@ public class ArchiveUtil ...@@ -19,6 +19,7 @@ public class ArchiveUtil
ZipEntry entry; ZipEntry entry;
while ((entry = stream.getNextEntry()) != null) { while ((entry = stream.getNextEntry()) != null) {
LOG.debug(String.format("Processing zip entry: %s", entry.getName()));
File file = outDir.resolve(entry.getName()).toFile(); File file = outDir.resolve(entry.getName()).toFile();
LOG.debug(String.format("processing entry %s", file.getAbsolutePath())); LOG.debug(String.format("processing entry %s", file.getAbsolutePath()));
...@@ -28,6 +29,7 @@ public class ArchiveUtil ...@@ -28,6 +29,7 @@ public class ArchiveUtil
LOG.debug("creating new file"); LOG.debug("creating new file");
file.createNewFile(); file.createNewFile();
LOG.debug(String.format("Created new file: %s", file.getAbsolutePath()));
byte[] buffer = new byte[2048]; byte[] buffer = new byte[2048];
try (FileOutputStream fos = new FileOutputStream(file); try (FileOutputStream fos = new FileOutputStream(file);
......
...@@ -67,6 +67,16 @@ public class ExecuteTestUtil { ...@@ -67,6 +67,16 @@ public class ExecuteTestUtil {
Paths.get(assignmentBasePath, assignmentId), Paths.get(assignmentBasePath, assignmentId),
testPath); 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"); LOG.debug("copy test config");
Files.copy( Files.copy(
Paths.get(assignmentBasePath, assignmentId + ".txt"), Paths.get(assignmentBasePath, assignmentId + ".txt"),
...@@ -140,14 +150,25 @@ public class ExecuteTestUtil { ...@@ -140,14 +150,25 @@ public class ExecuteTestUtil {
LOG.info("Checking for optional test competency profile information for paedagogical agent functionality..."); 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); 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..."); LOG.info("Found optional test competency profiles, generating agent profile data...");
resultSummary.overallTestCompetencyProfile=CompetencyAssessmentUtil.packFloats(CompetencyAssessmentUtil.sumTestCompetencyProfiles(testCompetencyProfiles)); resultSummary.overallTestCompetencyProfile=CompetencyAssessmentUtil.packFloats(CompetencyAssessmentUtil.sumTestCompetencyProfiles(testCompetencyProfiles));
resultSummary.successfulTestCompetencyProfile=CompetencyAssessmentUtil.packFloats(CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary, true)); resultSummary.successfulTestCompetencyProfile=CompetencyAssessmentUtil.packFloats(CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary, true));
LOG.info("Checking for optional exercise competency profile information for paedagogical agent exercise recommendation functionality..."); LOG.info("Checking for optional exercise competency profile information for paedagogical agent exercise recommendation functionality...");
//testPathHost or assignmentBasePath //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)) { if (Files.exists(exerciseManifestFile)) {
LOG.info("Found optional exercise competency profiles, generating recommendations..."); LOG.info("Found optional exercise competency profiles, generating recommendations...");
resultSummary.recommendations = recommendNextExercises(assignmentId, testPathHost, testCompetencyProfiles, resultSummary); resultSummary.recommendations = recommendNextExercises(assignmentId, testPathHost, testCompetencyProfiles, resultSummary);
......
...@@ -24,10 +24,12 @@ public class FileUtil { ...@@ -24,10 +24,12 @@ public class FileUtil {
deleteFolderRecursively(f); deleteFolderRecursively(f);
} else { } else {
f.delete(); f.delete();
System.out.println(String.format("Deleted file: %s", f.getAbsolutePath()));
} }
} }
} }
folder.delete(); folder.delete();
System.out.println(String.format("Deleted folder: %s", folder.getAbsolutePath()));
} }
public static void copyFolder(Path src, Path dst) throws IOException { public static void copyFolder(Path src, Path dst) throws IOException {
...@@ -35,6 +37,7 @@ public class FileUtil { ...@@ -35,6 +37,7 @@ public class FileUtil {
.forEach(source -> { .forEach(source -> {
try { try {
Files.copy(source, dst.resolve(src.relativize(source))); 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) { } catch (IOException e) {
throw new RuntimeException(e.getMessage(), e); throw new RuntimeException(e.getMessage(), e);
} }
......
...@@ -104,7 +104,7 @@ public class RepoUtil { ...@@ -104,7 +104,7 @@ public class RepoUtil {
//copy appropriate path from checkout directory to target directory //copy appropriate path from checkout directory to target directory
FileSystemUtils.copyRecursively(new File(checkoutDirectory+subDir), new File(targetPath)); 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) { catch (IOException e) {
LOG.error(String.format("Error while cloning from %s: could not copy to unit test dir", config.group(1)), e); LOG.error(String.format("Error while cloning from %s: could not copy to unit test dir", config.group(1)), e);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment