Commit 92f938c9 authored by mamunozgil's avatar mamunozgil
Browse files

Refactor paths to dta-tests-assignments

parent 6c7bc49f
Pipeline #10776 passed with stage
in 18 seconds
Showing with 169 additions and 139 deletions
+169 -139
......@@ -23,88 +23,117 @@ import java.util.regex.Pattern;
import jakarta.servlet.annotation.MultipartConfig;
/**
* Rest controller for everything related to the TASK files
* Rest controller for handling code assignment file uploads and testing.
*/
@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;
private final Tika tika;
public TaskUpload(
Environment env,
RepoUtil repoUtil,
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"));
this.tika = new Tika();
}
@RequestMapping(method = RequestMethod.POST)
public ResultSummary uploadAndTestFile(@RequestParam("taskFile") MultipartFile taskFileRef,
@RequestParam("assignmentId") String assignmentId
) throws IOException, InterruptedException {
LOG.info("submission for testing received");
@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()));
Path workDirectory = createWorkDirectory();
Path srcPath = defineSourcePath(workDirectory);
// 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 mimeType = tika.detect(taskFileRef.getInputStream());
processUploadedFile(taskFileRef, srcPath, mimeType);
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");
ResultSummary resultSummary = executeTestUtil.runTests(assignmentId, workDirectory);
if (isPlainTextFile(mimeType)) {
processTicketingInformation(taskFileRef, resultSummary);
}
LOG.info("Submission tested successfully");
return resultSummary;
}
else {
subDir=config.group(4);
private Path createWorkDirectory() throws IOException {
LOG.debug("Creating new temporary directory");
Path workDirectory = Files.createTempDirectory(testTmpPath, "dta-submission");
LOG.debug("Working directory for test: {}", workDirectory.toAbsolutePath());
return workDirectory;
}
LOG.debug("calling repo clone");
repoUtil.cloneRepository(config, srcPath.toAbsolutePath().toString(), subDir);
private Path defineSourcePath(Path workDirectory) {
Path srcPath = workDirectory.resolve("src");
LOG.debug("Source path defined as: {}", srcPath.toAbsolutePath());
return srcPath;
}
private void processUploadedFile(MultipartFile taskFileRef, Path srcPath, String mimeType) throws IOException {
switch (mimeType) {
case "text/plain":
handlePlainTextFile(taskFileRef, srcPath);
break;
case "application/zip":
LOG.debug("zip archive uploaded, extracting content as student submission");
ArchiveUtil.extractProjectFromZip(taskFileRef.getInputStream(), srcPath.toAbsolutePath());
handleZipFile(taskFileRef, srcPath);
break;
default:
String msg = String.format("couldn't process uploaded file with mime type %s", mimeInfo);
LOG.error(msg);
throw new RuntimeException(msg);
handleUnsupportedFileType(mimeType);
}
}
// run test
LOG.debug("calling test execution");
ResultSummary resultSummary = executeTestUtil.runTests(assignmentId, workDirectory);
private void handlePlainTextFile(MultipartFile taskFileRef, Path srcPath) throws IOException {
LOG.debug("Text file uploaded, searching for DTA config");
if (mimeInfo.equals("text/plain")) {
LOG.info("check for provided Ticketsystem information");
UnifiedTicketingUtil.reportResults(taskFileRef.getInputStream(), resultSummary);
Matcher config = findRepositoryConfig(taskFileRef);
String subDir = config != null ? config.group(4) : "";
LOG.debug("Cloning repository");
repoUtil.cloneRepository(config, srcPath.toAbsolutePath().toString(), subDir);
}
taskFileRef.getInputStream().close();
private Matcher findRepositoryConfig(MultipartFile taskFileRef) throws IOException {
Matcher config = RegexUtil.extractConfig(taskFileRef.getInputStream(), Pattern.compile(RegexUtil.DTA_SUBMISSIONCONFIGREGEX));
LOG.info("submission tested successfully");
return resultSummary;
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");
}
}
return config;
}
private void handleZipFile(MultipartFile taskFileRef, Path srcPath) throws IOException {
LOG.debug("ZIP archive uploaded, extracting content");
ArchiveUtil.extractProjectFromZip(taskFileRef.getInputStream(), srcPath.toAbsolutePath());
}
private void handleUnsupportedFileType(String mimeType) {
String msg = String.format("Couldn't process uploaded file with MIME type: %s", mimeType);
LOG.error(msg);
throw new RuntimeException(msg);
}
private boolean isPlainTextFile(String mimeType) {
return "text/plain".equals(mimeType);
}
private void processTicketingInformation(MultipartFile taskFileRef, ResultSummary resultSummary) throws IOException {
LOG.info("Checking for provided Ticketing system information");
UnifiedTicketingUtil.reportResults(taskFileRef.getInputStream(), resultSummary);
}
}
......@@ -3,6 +3,7 @@ package de.hftstuttgart.dtabackend.utils;
import com.fasterxml.jackson.core.exc.StreamReadException;
import com.fasterxml.jackson.databind.DatabindException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.Volume;
import de.hftstuttgart.dtabackend.models.ExerciseCompetencyProfile;
......@@ -33,59 +34,59 @@ public class ExecuteTestUtil {
private static final Logger LOG = LogManager.getLogger(ExecuteTestUtil.class);
private final DockerUtil dockerUtil;
private final String assignmentBasePath;
private final String containerTestDir;
private final String basePath;
private final String containerBasePath;
public ExecuteTestUtil(
Environment env,
DockerUtil dockerUtil
) {
public ExecuteTestUtil(Environment env, DockerUtil dockerUtil) {
this.dockerUtil = dockerUtil;
// set base path for assignments to be stored
Path p = Paths.get(
env.getProperty("data.dir"), ///data
env.getProperty("data.dir.test.folder.name")); //UnitTests
this.assignmentBasePath = p.toAbsolutePath().toString();
this.containerTestDir = env.getProperty( "data.dir");
this.basePath = p.toAbsolutePath().toString();
this.containerBasePath = env.getProperty( "data.dir");
}
public ResultSummary runTests(String assignmentId, Path workDirectory) throws IOException, InterruptedException {
// Define paths for the test, the submission, and where the result is expected afterwards
String containerTestDir = this.containerTestDir;
Path resultPath = Paths.get(containerTestDir, "result");
// Define paths for the submission-specific directories
String containerTestDir = this.containerBasePath + workDirectory.getFileName(); // /dta-test-assignments/dta-submissionID
Path testPath = Paths.get(containerTestDir, "test");
Path srcPath = Paths.get(containerTestDir, "src");
Path resultPath = Paths.get(containerTestDir, "result");
// Ensure directories exist
Files.createDirectories(testPath);
Files.createDirectories(srcPath);
Files.createDirectories(resultPath);
// Clone stored test to testPath
LOG.debug("Copying pre-downloaded unit test repo");
FileUtil.copyFolder(
Paths.get(assignmentBasePath, assignmentId),
testPath);
FileUtil.copyFolder(Paths.get(basePath, assignmentId), testPath);
LOG.debug("Copying exercise manifest");
Files.copy(Paths.get(
assignmentBasePath, assignmentId + "_checkout",
CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME),
Paths.get(
testPath.toString(),
CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME));
Files.copy(
Paths.get(basePath, assignmentId + "_checkout", CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME),
testPath.resolve(CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME)
);
LOG.debug("Copying test config");
Files.copy(
Paths.get(assignmentBasePath, assignmentId + ".txt"),
Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt"));
Files.createDirectory(resultPath);
Paths.get(basePath, assignmentId + ".txt"),
Paths.get(containerTestDir, "config.txt")
);
LOG.info("Reading test config");
Matcher config = RegexUtil.extractConfig(
new FileInputStream(Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt").toFile()), Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX));
String image = "";
new FileInputStream(Paths.get(containerTestDir, "config.txt").toFile()),
Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX)
);
String image;
if (config == null) {
config = RegexUtil.extractConfig(
new FileInputStream(Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt").toFile()), Pattern.compile(RegexUtil.TESTCONFIGREGEX));
new FileInputStream(Paths.get(containerTestDir, "config.txt").toFile()),
Pattern.compile(RegexUtil.TESTCONFIGREGEX)
);
if (config == null) {
throw new RuntimeException("Couldn't find repo config for unit test image extraction");
}
......@@ -94,34 +95,32 @@ public class ExecuteTestUtil {
image = config.group(5);
}
// Start the test-container with professor-given image and volume mounts for test, submission, and result
dockerUtil.runContainerWithVolumes(
// Start the test-container with professor-given image and submission-specific volume mounts
dockerUtil.runContainerWithBinds(
image,
new Volume("test_volume: /data/test"),
new Volume("src_volume: /data/src"),
new Volume("result_volume: /data/result")
new Bind(testPath.toString(), new Volume("/data/test")),
new Bind(srcPath.toString(), new Volume("/data/src")),
new Bind(resultPath.toString(), new Volume("/data/result"))
);
ResultSummary resultSummary = generateResult(assignmentId, resultPath, testPath);
return resultSummary;
return generateResult(assignmentId, resultPath, testPath);
}
private ResultSummary generateResult(String assignmentId, Path resultPath, Path testPath)
throws IOException, StreamReadException, DatabindException, MalformedURLException {
// Define expected result file
File resultFile = Paths.get(resultPath.toAbsolutePath().toString(), "result.json").toFile();
File resultFile = resultPath.resolve("result.json").toFile();
// Check if result file is there
if (!resultFile.exists() || !resultFile.isFile()) {
LOG.error(String.format("Could not find result file in %s", resultFile.getAbsolutePath()));
LOG.error("Could not find result file in {}", resultFile.getAbsolutePath());
throw new RuntimeException("No result file found");
}
LOG.debug("Parse results JSON");
LOG.debug("Parsing results JSON");
ObjectMapper objectMapper = new ObjectMapper();
ResultSummary resultSummary = objectMapper.readValue(resultFile.toURI().toURL(), ResultSummary.class);
LOG.debug("Result JSON returned time " + resultSummary.timestamp + " with " + resultSummary.results.size() + " test results.");
LOG.debug("Result JSON returned time {} with {} test results.", resultSummary.timestamp, resultSummary.results.size());
LOG.info("Checking for optional test competency profile information for pedagogical agent functionality...");
List<TestCompetencyProfile> testCompetencyProfiles = CompetencyAssessmentUtil.readTestCompetencyProfiles(testPath, CompetencyAssessmentUtil.TEST_COMPETENCY_MANIFEST_FILE_NAME);
......@@ -154,23 +153,25 @@ public class ExecuteTestUtil {
/*
* exercise recommendation part
*/
public List<Recommendation> recommendNextExercises(String assignmentId, Path testPathHost, List<TestCompetencyProfile> testCompetencyProfiles, ResultSummary resultSummary)
public List<Recommendation> recommendNextExercises(String assignmentId, Path testPath, List<TestCompetencyProfile> testCompetencyProfiles, ResultSummary resultSummary)
throws FileNotFoundException {
// fetch repo url from original test upload
Pattern pattern = Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX);
File file = Paths.get(assignmentBasePath, assignmentId + ".txt").toFile();
FileInputStream configFileStream = new FileInputStream(file);
Matcher config = RegexUtil.extractConfig(configFileStream, pattern);
File configFile = Paths.get(basePath, assignmentId + ".txt").toFile();
Matcher config = RegexUtil.extractConfig(new FileInputStream(configFile), pattern);
String testRepoURL = config.group(1);
List<ExerciseCompetencyProfile> exerciseCompetencyProfiles = CompetencyAssessmentUtil.readExerciseCompetencyProfiles(testPathHost, CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME);
List<ExerciseCompetencyProfile> exerciseCompetencyProfiles = CompetencyAssessmentUtil.readExerciseCompetencyProfiles(
testPath, CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME
);
int currentTopicIndex=0;
float currentDifficulty=0.0f;
int currentTopicIndex = 0;
float currentDifficulty = 0.0f;
//build course topic order
Map<String, Integer> topicOrder = new HashMap<>();
int order = 1;
for (ExerciseCompetencyProfile e : exerciseCompetencyProfiles) {
if (!topicOrder.containsKey(e.exerciseTopicName)) {
topicOrder.put(e.exerciseTopicName, order++);
......
......@@ -9,7 +9,7 @@ spring.http.multipart.max-file-size=5Mb
###############################################
# Holds the uploaded Zip-Files
tests.tmp.dir=/tmp/dta-tests
tests.tmp.dir=/dta-tests-assignments
host.tests.tmp.dir=${tests.tmp.dir}
data.dir=/data
data.dir.test.folder.name=UnitTests
......
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