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; ...@@ -23,88 +23,117 @@ 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 handling code assignment file uploads and testing.
*/ */
@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;
private final Tika tika;
public TaskUpload( public TaskUpload(Environment env, RepoUtil repoUtil, ExecuteTestUtil executeTestUtil) {
Environment env,
RepoUtil repoUtil,
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
this.testTmpPath = Paths.get(env.getProperty("tests.tmp.dir")); this.testTmpPath = Paths.get(env.getProperty("tests.tmp.dir"));
this.tika = new Tika();
} }
@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");
Path workDirectory = createWorkDirectory();
Path srcPath = defineSourcePath(workDirectory);
String mimeType = tika.detect(taskFileRef.getInputStream());
processUploadedFile(taskFileRef, srcPath, mimeType);
ResultSummary resultSummary = executeTestUtil.runTests(assignmentId, workDirectory);
if (isPlainTextFile(mimeType)) {
processTicketingInformation(taskFileRef, resultSummary);
}
LOG.debug("creating new temporary directory"); LOG.info("Submission tested successfully");
Path workDirectory = Files.createTempDirectory(testTmpPath, "dta"); return resultSummary;
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 private Path createWorkDirectory() throws IOException {
Path srcPath = Paths.get(workDirectory.toAbsolutePath().toString(), "src"); LOG.debug("Creating new temporary directory");
LOG.debug(String.format("Source path defined as: %s", srcPath.toAbsolutePath().toString())); Path workDirectory = Files.createTempDirectory(testTmpPath, "dta-submission");
LOG.debug("Working directory for test: {}", workDirectory.toAbsolutePath());
return workDirectory;
}
String mimeInfo = new Tika().detect(taskFileRef.getInputStream()); private Path defineSourcePath(Path workDirectory) {
switch (mimeInfo) { 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": case "text/plain":
LOG.debug("textfile uploaded, searching for dta config"); handlePlainTextFile(taskFileRef, srcPath);
// 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; break;
case "application/zip": case "application/zip":
LOG.debug("zip archive uploaded, extracting content as student submission"); handleZipFile(taskFileRef, srcPath);
ArchiveUtil.extractProjectFromZip(taskFileRef.getInputStream(), srcPath.toAbsolutePath());
break; break;
default: default:
String msg = String.format("couldn't process uploaded file with mime type %s", mimeInfo); handleUnsupportedFileType(mimeType);
LOG.error(msg);
throw new RuntimeException(msg);
} }
}
// run test private void handlePlainTextFile(MultipartFile taskFileRef, Path srcPath) throws IOException {
LOG.debug("calling test execution"); LOG.debug("Text file uploaded, searching for DTA config");
ResultSummary resultSummary = executeTestUtil.runTests(assignmentId, workDirectory);
Matcher config = findRepositoryConfig(taskFileRef);
String subDir = config != null ? config.group(4) : "";
LOG.debug("Cloning repository");
repoUtil.cloneRepository(config, srcPath.toAbsolutePath().toString(), subDir);
}
if (mimeInfo.equals("text/plain")) { private Matcher findRepositoryConfig(MultipartFile taskFileRef) throws IOException {
LOG.info("check for provided Ticketsystem information"); Matcher config = RegexUtil.extractConfig(taskFileRef.getInputStream(), Pattern.compile(RegexUtil.DTA_SUBMISSIONCONFIGREGEX));
UnifiedTicketingUtil.reportResults(taskFileRef.getInputStream(), 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;
taskFileRef.getInputStream().close(); }
LOG.info("submission tested successfully"); private void handleZipFile(MultipartFile taskFileRef, Path srcPath) throws IOException {
return resultSummary; 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; ...@@ -3,6 +3,7 @@ package de.hftstuttgart.dtabackend.utils;
import com.fasterxml.jackson.core.exc.StreamReadException; import com.fasterxml.jackson.core.exc.StreamReadException;
import com.fasterxml.jackson.databind.DatabindException; import com.fasterxml.jackson.databind.DatabindException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.Volume; import com.github.dockerjava.api.model.Volume;
import de.hftstuttgart.dtabackend.models.ExerciseCompetencyProfile; import de.hftstuttgart.dtabackend.models.ExerciseCompetencyProfile;
...@@ -33,59 +34,59 @@ public class ExecuteTestUtil { ...@@ -33,59 +34,59 @@ public class ExecuteTestUtil {
private static final Logger LOG = LogManager.getLogger(ExecuteTestUtil.class); private static final Logger LOG = LogManager.getLogger(ExecuteTestUtil.class);
private final DockerUtil dockerUtil; private final DockerUtil dockerUtil;
private final String assignmentBasePath; private final String basePath;
private final String containerTestDir; private final String containerBasePath;
public ExecuteTestUtil( public ExecuteTestUtil(Environment env, DockerUtil dockerUtil) {
Environment env,
DockerUtil dockerUtil
) {
this.dockerUtil = dockerUtil; this.dockerUtil = dockerUtil;
// set base path for assignments to be stored // set base path for assignments to be stored
Path p = Paths.get( Path p = Paths.get(
env.getProperty("data.dir"), ///data env.getProperty("data.dir"), ///data
env.getProperty("data.dir.test.folder.name")); //UnitTests env.getProperty("data.dir.test.folder.name")); //UnitTests
this.assignmentBasePath = p.toAbsolutePath().toString(); this.basePath = p.toAbsolutePath().toString();
this.containerTestDir = env.getProperty( "data.dir"); this.containerBasePath = env.getProperty( "data.dir");
} }
public ResultSummary runTests(String assignmentId, Path workDirectory) throws IOException, InterruptedException { public ResultSummary runTests(String assignmentId, Path workDirectory) throws IOException, InterruptedException {
// 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");
// Define paths for the test, the submission, and where the result is expected afterwards // Ensure directories exist
String containerTestDir = this.containerTestDir; Files.createDirectories(testPath);
Path resultPath = Paths.get(containerTestDir, "result"); Files.createDirectories(srcPath);
Path testPath = Paths.get(containerTestDir, "test"); Files.createDirectories(resultPath);
Path srcPath = Paths.get(containerTestDir, "src");
// Clone stored test to testPath // Clone stored test to testPath
LOG.debug("Copying pre-downloaded unit test repo"); LOG.debug("Copying pre-downloaded unit test repo");
FileUtil.copyFolder( FileUtil.copyFolder(Paths.get(basePath, assignmentId), testPath);
Paths.get(assignmentBasePath, assignmentId),
testPath);
LOG.debug("Copying exercise manifest"); LOG.debug("Copying exercise manifest");
Files.copy(Paths.get( Files.copy(
assignmentBasePath, assignmentId + "_checkout", Paths.get(basePath, assignmentId + "_checkout", CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME),
CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME), testPath.resolve(CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME)
Paths.get( );
testPath.toString(),
CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME));
LOG.debug("Copying test config"); LOG.debug("Copying test config");
Files.copy( Files.copy(
Paths.get(assignmentBasePath, assignmentId + ".txt"), Paths.get(basePath, assignmentId + ".txt"),
Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt")); Paths.get(containerTestDir, "config.txt")
);
Files.createDirectory(resultPath);
LOG.info("Reading test config"); LOG.info("Reading test config");
Matcher config = RegexUtil.extractConfig( Matcher config = RegexUtil.extractConfig(
new FileInputStream(Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt").toFile()), Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX)); new FileInputStream(Paths.get(containerTestDir, "config.txt").toFile()),
String image = ""; Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX)
);
String image;
if (config == null) { if (config == null) {
config = RegexUtil.extractConfig( 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) { if (config == null) {
throw new RuntimeException("Couldn't find repo config for unit test image extraction"); throw new RuntimeException("Couldn't find repo config for unit test image extraction");
} }
...@@ -94,35 +95,33 @@ public class ExecuteTestUtil { ...@@ -94,35 +95,33 @@ public class ExecuteTestUtil {
image = config.group(5); image = config.group(5);
} }
// Start the test-container with professor-given image and volume mounts for test, submission, and result // Start the test-container with professor-given image and submission-specific volume mounts
dockerUtil.runContainerWithVolumes( dockerUtil.runContainerWithBinds(
image, image,
new Volume("test_volume: /data/test"), new Bind(testPath.toString(), new Volume("/data/test")),
new Volume("src_volume: /data/src"), new Bind(srcPath.toString(), new Volume("/data/src")),
new Volume("result_volume: /data/result") new Bind(resultPath.toString(), new Volume("/data/result"))
); );
ResultSummary resultSummary = generateResult(assignmentId, resultPath, testPath); return generateResult(assignmentId, resultPath, testPath);
return resultSummary;
} }
private ResultSummary generateResult(String assignmentId, Path resultPath, Path testPath) private ResultSummary generateResult(String assignmentId, Path resultPath, Path testPath)
throws IOException, StreamReadException, DatabindException, MalformedURLException { throws IOException, StreamReadException, DatabindException, MalformedURLException {
// Define expected result file // 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 // Check if result file is there
if (!resultFile.exists() || !resultFile.isFile()) { 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"); throw new RuntimeException("No result file found");
} }
LOG.debug("Parse results JSON"); LOG.debug("Parsing results JSON");
ObjectMapper objectMapper = new ObjectMapper(); ObjectMapper objectMapper = new ObjectMapper();
ResultSummary resultSummary = objectMapper.readValue(resultFile.toURI().toURL(), ResultSummary.class); 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..."); 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); List<TestCompetencyProfile> testCompetencyProfiles = CompetencyAssessmentUtil.readTestCompetencyProfiles(testPath, CompetencyAssessmentUtil.TEST_COMPETENCY_MANIFEST_FILE_NAME);
LOG.debug(String.format( LOG.debug(String.format(
...@@ -150,46 +149,48 @@ public class ExecuteTestUtil { ...@@ -150,46 +149,48 @@ public class ExecuteTestUtil {
} }
return resultSummary; return resultSummary;
} }
/* /*
* exercise recommendation part * 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 { throws FileNotFoundException {
// fetch repo url from original test upload // fetch repo url from original test upload
Pattern pattern = Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX); Pattern pattern = Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX);
File file = Paths.get(assignmentBasePath, assignmentId + ".txt").toFile(); File configFile = Paths.get(basePath, assignmentId + ".txt").toFile();
FileInputStream configFileStream = new FileInputStream(file); Matcher config = RegexUtil.extractConfig(new FileInputStream(configFile), pattern);
Matcher config = RegexUtil.extractConfig(configFileStream, pattern); String testRepoURL = config.group(1);
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; int currentTopicIndex = 0;
float currentDifficulty=0.0f; float currentDifficulty = 0.0f;
//build course topic order //build course topic order
Map<String, Integer> topicOrder = new HashMap<>(); Map<String, Integer> topicOrder = new HashMap<>();
int order = 1; int order = 1;
for (ExerciseCompetencyProfile e : exerciseCompetencyProfiles) {
if (!topicOrder.containsKey(e.exerciseTopicName)) { for (ExerciseCompetencyProfile e : exerciseCompetencyProfiles) {
topicOrder.put(e.exerciseTopicName, order++); if (!topicOrder.containsKey(e.exerciseTopicName)) {
} topicOrder.put(e.exerciseTopicName, order++);
if (e.exerciseURL.equals(testRepoURL)) { }
currentTopicIndex = order; if (e.exerciseURL.equals(testRepoURL)) {
currentDifficulty = e.difficulty; currentTopicIndex = order;
} currentDifficulty = e.difficulty;
} }
}
//filter exercises according to success //filter exercises according to success
float[] unsuccessful = CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary, false); float[] unsuccessful = CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary, false);
List<ExerciseCompetencyProfile> filteredExercises = filterExercisesByTopicsAndDifficulty( List<ExerciseCompetencyProfile> filteredExercises = filterExercisesByTopicsAndDifficulty(
exerciseCompetencyProfiles, topicOrder, currentTopicIndex, testRepoURL, exerciseCompetencyProfiles, topicOrder, currentTopicIndex, testRepoURL,
currentDifficulty, unsuccessful, resultSummary); currentDifficulty, unsuccessful, resultSummary);
//compute recommendations //compute recommendations
List<Recommendation> recommendedExercises = new ArrayList<>(); List<Recommendation> recommendedExercises = new ArrayList<>();
for (ExerciseCompetencyProfile exerciseProfile : filteredExercises) { for (ExerciseCompetencyProfile exerciseProfile : filteredExercises) {
Recommendation recommendation = new Recommendation(exerciseProfile.exerciseTopicName, exerciseProfile.exerciseURL, exerciseProfile.exerciseName, Recommendation recommendation = new Recommendation(exerciseProfile.exerciseTopicName, exerciseProfile.exerciseURL, exerciseProfile.exerciseName,
exerciseProfile.difficulty, calculateScore(exerciseProfile, unsuccessful, topicOrder, currentDifficulty)); exerciseProfile.difficulty, calculateScore(exerciseProfile, unsuccessful, topicOrder, currentDifficulty));
recommendedExercises.add(recommendation); recommendedExercises.add(recommendation);
...@@ -199,7 +200,7 @@ public class ExecuteTestUtil { ...@@ -199,7 +200,7 @@ public class ExecuteTestUtil {
recommendedExercises.stream().sorted(Recommendation.COMPARE_BY_SCORE).collect(Collectors.toList()); recommendedExercises.stream().sorted(Recommendation.COMPARE_BY_SCORE).collect(Collectors.toList());
return recommendedExercises; return recommendedExercises;
} }
public static List<ExerciseCompetencyProfile> filterExercisesByTopicsAndDifficulty(List<ExerciseCompetencyProfile> exerciseCompetencyProfiles, public static List<ExerciseCompetencyProfile> filterExercisesByTopicsAndDifficulty(List<ExerciseCompetencyProfile> exerciseCompetencyProfiles,
Map<String, Integer> topicOrder, int currentTopicIndex, String testRepoURL, float currentDifficulty, float[] unsuccessful, Map<String, Integer> topicOrder, int currentTopicIndex, String testRepoURL, float currentDifficulty, float[] unsuccessful,
...@@ -208,34 +209,34 @@ public class ExecuteTestUtil { ...@@ -208,34 +209,34 @@ public class ExecuteTestUtil {
//option for later: include next topic if fullsuccess and current difficulty == max difficulty //option for later: include next topic if fullsuccess and current difficulty == max difficulty
List<ExerciseCompetencyProfile> filteredExercises = exerciseCompetencyProfiles.stream() List<ExerciseCompetencyProfile> filteredExercises = exerciseCompetencyProfiles.stream()
.filter(testProfile -> topicOrder.get(testProfile.exerciseTopicName) <= currentTopicIndex) .filter(testProfile -> topicOrder.get(testProfile.exerciseTopicName) <= currentTopicIndex)
.collect(Collectors.toList()); .collect(Collectors.toList());
//filter by difficulty according to success //filter by difficulty according to success
if (isFullSuccess(unsuccessful)) { if (isFullSuccess(unsuccessful)) {
filteredExercises = filteredExercises.stream().filter(profile -> profile.difficulty >= currentDifficulty && !testRepoURL.equals(profile.exerciseURL)).collect(Collectors.toList()); filteredExercises = filteredExercises.stream().filter(profile -> profile.difficulty >= currentDifficulty && !testRepoURL.equals(profile.exerciseURL)).collect(Collectors.toList());
} else { } else {
filteredExercises = filteredExercises.stream().filter(profile -> profile.difficulty <= currentDifficulty).collect(Collectors.toList()); filteredExercises = filteredExercises.stream().filter(profile -> profile.difficulty <= currentDifficulty).collect(Collectors.toList());
} }
return filteredExercises; return filteredExercises;
} }
public static boolean isFullSuccess(float[] unsuccessful) { public static boolean isFullSuccess(float[] unsuccessful) {
for (float value : unsuccessful) { for (float value : unsuccessful) {
if (value != 0.0f) { if (value != 0.0f) {
return false; return false;
} }
} }
return true; return true;
} }
public static float calculateScore(ExerciseCompetencyProfile exerciseProfile, float[] unsuccessful, Map<String, Integer> topicOrder, float currentDifficulty) { public static float calculateScore(ExerciseCompetencyProfile exerciseProfile, float[] unsuccessful, Map<String, Integer> topicOrder, float currentDifficulty) {
//ensure factor 1 for full success not to blank out score, thus offset the base //ensure factor 1 for full success not to blank out score, thus offset the base
float score = 1.0f; float score = 1.0f;
//competency profile difference to not fully achieved competencies component //competency profile difference to not fully achieved competencies component
for (int i = 0; i < exerciseProfile.competencyAssessments.length-1; i++) { for (int i = 0; i < exerciseProfile.competencyAssessments.length-1; i++) {
score += exerciseProfile.competencyAssessments[i] * unsuccessful[i]; score += exerciseProfile.competencyAssessments[i] * unsuccessful[i];
} }
//difficulty component //difficulty component
score = score * (exerciseProfile.difficulty*(0.5f+Math.abs(currentDifficulty-exerciseProfile.difficulty))); score = score * (exerciseProfile.difficulty*(0.5f+Math.abs(currentDifficulty-exerciseProfile.difficulty)));
...@@ -245,5 +246,5 @@ public class ExecuteTestUtil { ...@@ -245,5 +246,5 @@ public class ExecuteTestUtil {
score = Math.round(score * 10.0f) / 10.0f; score = Math.round(score * 10.0f) / 10.0f;
return score; return score;
} }
} }
\ No newline at end of file
...@@ -9,7 +9,7 @@ spring.http.multipart.max-file-size=5Mb ...@@ -9,7 +9,7 @@ spring.http.multipart.max-file-size=5Mb
############################################### ###############################################
# Holds the uploaded Zip-Files # Holds the uploaded Zip-Files
tests.tmp.dir=/tmp/dta-tests tests.tmp.dir=/dta-tests-assignments
host.tests.tmp.dir=${tests.tmp.dir} host.tests.tmp.dir=${tests.tmp.dir}
data.dir=/data data.dir=/data
data.dir.test.folder.name=UnitTests 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