Commit c47948b0 authored by mamunozgil's avatar mamunozgil
Browse files

Added volumes for the backend host

No related merge requests found
Pipeline #10761 passed with stage
in 18 seconds
Showing with 214 additions and 233 deletions
+214 -233
......@@ -5,6 +5,9 @@ import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.exception.DockerException;
import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.HostConfig;
import com.github.dockerjava.api.model.Mount;
import com.github.dockerjava.api.model.MountType;
import com.github.dockerjava.api.model.Volume;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
......@@ -15,6 +18,8 @@ import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Arrays;
import java.util.stream.Collectors;
@Component
public class DockerUtil {
......@@ -37,7 +42,7 @@ public class DockerUtil {
dockerClient = DockerClientImpl.getInstance(dockerClientConfig, httpClient);
}
public int runContainer(String image, Bind... binds) throws InterruptedException, IOException
public int runContainerWithBinds(String image, Bind... binds) throws InterruptedException, IOException
{
LOG.debug(String.format("pull image: %s", image));
try {
......@@ -87,4 +92,62 @@ public class DockerUtil {
return ret;
}
public int runContainerWithVolumes(String image, Volume... volumes) throws InterruptedException, IOException {
LOG.debug(String.format("Pulling image: %s", image));
try {
dockerClient.pullImageCmd(image)
.start()
.awaitCompletion()
.close();
} catch (DockerException e) {
LOG.error(String.format(
"Pulling Docker image %s failed with %s, trying with local image",
image,
e.getMessage()));
}
LOG.debug("Creating container");
CreateContainerResponse containerResponse;
try {
// Prepare the host configuration with volumes
HostConfig hostConfig = HostConfig.newHostConfig()
.withMounts(
Arrays.stream(volumes)
.map(volume -> new Mount().withTarget(volume.getPath()).withType(MountType.VOLUME))
.collect(Collectors.toList())
);
// Create the container with the configured volumes
containerResponse = dockerClient.createContainerCmd("testcontainer")
.withImage(image)
.withHostConfig(hostConfig)
.exec();
} catch (DockerException e) {
LOG.error(String.format(
"Creating Docker Testrunner container failed with %s", e.getMessage()));
throw e;
}
LOG.debug(String.format("Container created: %s", containerResponse.getId()));
LOG.debug(String.format("Starting container %s", containerResponse.getId()));
dockerClient.startContainerCmd(containerResponse.getId()).exec();
LOG.debug(String.format("Waiting for completion of container %s", containerResponse.getId()));
int ret = dockerClient
.waitContainerCmd(containerResponse.getId())
.start()
.awaitCompletion()
.awaitStatusCode();
LOG.debug(String.format("Container completed with status %d", ret));
LOG.debug(String.format("Deleting container %s", containerResponse.getId()));
dockerClient.removeContainerCmd(containerResponse.getId())
.withRemoveVolumes(true)
.exec();
return ret;
}
}
......@@ -3,7 +3,6 @@ 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;
......@@ -31,12 +30,11 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
@Component
public class ExecuteTestUtil {
public class ExecuteTestUtil {
private static final Logger LOG = LogManager.getLogger(ExecuteTestUtil.class);
private final DockerUtil dockerUtil;
private final String assignmentBasePath;
private final Path testTmpPathHost;
public ExecuteTestUtil(
Environment env,
......@@ -49,281 +47,201 @@ public class ExecuteTestUtil {
env.getProperty("data.dir"),
env.getProperty("data.dir.test.folder.name"));
this.assignmentBasePath = p.toAbsolutePath().toString();
// set path of temporary directory on host _and_ inside our container, _must_ be identical
this.testTmpPathHost = Paths.get(env.getProperty("host.tests.tmp.dir"));
}
public ResultSummary runTests(String assignmentId, Path workDirectory) throws IOException, InterruptedException {
// define paths for the test, the submission and where the result is to be expected afterwards
Path testPath = Paths.get(workDirectory.toAbsolutePath().toString(), "/test");
Path srcPath = Paths.get(workDirectory.toAbsolutePath().toString(), "/src");
Path resultPath = Paths.get(workDirectory.toAbsolutePath().toString(), "/result");
// Define paths for the test, the submission, and where the result is expected afterwards
Path testPath = Paths.get("/data/test"); // Volume mount path in the container
Path srcPath = Paths.get("/data/src"); // Volume mount path in the container
Path resultPath = Paths.get("/data/result"); // Volume mount path in the container
// clone stored test to tmpdir
LOG.debug("copying pre-downloaded unitttest repo");
// Clone stored test to testPath
LOG.debug("Copying pre-downloaded unit test repo");
FileUtil.copyFolder(
Paths.get(assignmentBasePath, assignmentId),
testPath);
LOG.debug("copying exercise manifest from: %s in testPath: %s",
assignmentBasePath + assignmentId + "_checkout",
testPath.toString() );
Files.copy(Paths.get(
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));
CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME),
Paths.get(
testPath.toString(),
CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME));
LOG.debug("copy test config");
LOG.debug("Copying test config");
Files.copy(
Paths.get(assignmentBasePath, assignmentId + ".txt"),
Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt"));
Files.createDirectory(resultPath);
LOG.info("reading test config");
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="";
if(config==null)
{
config = RegexUtil.extractConfig(
new FileInputStream(Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt").toFile()), Pattern.compile(RegexUtil.TESTCONFIGREGEX));
if(config==null)
{
throw new RuntimeException("couldn't find repo config for unittest image extraction");
}
image=config.group(4);
}
else
{
image=config.group(5);
String image = "";
if (config == null) {
config = RegexUtil.extractConfig(
new FileInputStream(Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt").toFile()), Pattern.compile(RegexUtil.TESTCONFIGREGEX));
if (config == null) {
throw new RuntimeException("Couldn't find repo config for unit test image extraction");
}
image = config.group(4);
} else {
image = config.group(5);
}
// define the paths to mount as Binds from Host to the test-container
Path testPathHost = Paths.get(
testTmpPathHost.toAbsolutePath().toString(),
workDirectory.getName(workDirectory.getNameCount()-1).toString(),
testPath.getName(testPath.getNameCount()-1).toString()
);
Path srcPathHost = Paths.get(
testTmpPathHost.toAbsolutePath().toString(),
workDirectory.getName(workDirectory.getNameCount()-1).toString(),
srcPath.getName(srcPath.getNameCount()-1).toString()
);
Path resultPathHost = Paths.get(
testTmpPathHost.toAbsolutePath().toString(),
workDirectory.getName(workDirectory.getNameCount()-1).toString(),
resultPath.getName(resultPath.getNameCount()-1).toString()
);
// start test-container with professor given image and bind mounts for test, submission and result
// Start the test-container with professor-given image and volume mounts for test, submission, and result
dockerUtil.runContainer(
image,
new Bind(testPathHost.toAbsolutePath().toString(), new Volume("/data/test")),
new Bind(srcPathHost.toAbsolutePath().toString(), new Volume("/data/src")),
new Bind(resultPathHost.toAbsolutePath().toString(), new Volume("/data/result"))
new Volume("/data/test"),
new Volume("/data/src"),
new Volume("/data/result")
);
ResultSummary resultSummary = generateResult(assignmentId, resultPath, testPathHost);
ResultSummary resultSummary = generateResult(assignmentId, resultPath, testPath);
return resultSummary;
}
private ResultSummary generateResult(String assignmentId, Path resultPath, Path testPathHost)
throws IOException, StreamReadException, DatabindException, MalformedURLException {
// define expected result file
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();
// check if result file is there
// Check if result file is there
if (!resultFile.exists() || !resultFile.isFile()) {
LOG.error(String.format("Could not find result file in %s", resultFile.getAbsolutePath()));
throw new RuntimeException("no resultfile found");
throw new RuntimeException("No result file found");
}
LOG.debug("parse results json");
LOG.debug("Parse 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 " + resultSummary.timestamp + " with " + resultSummary.results.size() + " test results.");
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);
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);
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.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);
}
"Reading Test Competency Profiles: basePath=%s, fileName=%s",
testPath,
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 pedagogical agent exercise recommendation functionality...");
Path exerciseManifestFile = Paths.get(testPath.toString(), CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME);
LOG.debug(String.format(
"Constructing Path for exercise manifest: testPath=%s, fileName=%s -> Resulting Path=%s",
testPath.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, testPath, testCompetencyProfiles, resultSummary);
}
}
return resultSummary;
}
return resultSummary;
}
/*
* exercise recommendation part
*/
public List<Recommendation> recommendNextExercises(String assignmentId, Path testPathHost, List<TestCompetencyProfile> testCompetencyProfiles, ResultSummary resultSummary)
throws FileNotFoundException {
LOG.debug("Starting recommendNextExercises with assignmentId: {}", assignmentId);
// fetch repo url from original test upload
Pattern pattern = Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX);
LOG.debug("Compiled regex pattern for DTA_TESTCONFIGREGEX.");
File file = Paths.get(assignmentBasePath, assignmentId + ".txt").toFile();
LOG.debug("Resolved file path for assignmentId {}: {}", assignmentId, file.getAbsolutePath());
FileInputStream configFileStream = new FileInputStream(file);
LOG.debug("Opened FileInputStream for file: {}", file.getAbsolutePath());
Matcher config = RegexUtil.extractConfig(configFileStream, pattern);
LOG.debug("Extracted configuration using regex pattern.");
String testRepoURL = config.group(1) + config.group(4);
LOG.debug("Constructed testRepoURL: {}", testRepoURL);
List<ExerciseCompetencyProfile> exerciseCompetencyProfiles = CompetencyAssessmentUtil.readExerciseCompetencyProfiles(
testPathHost, CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME);
LOG.debug("Read exercise competency profiles from path: {}", testPathHost.resolve(CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME));
int currentTopicIndex = 0;
float currentDifficulty = 0.0f;
// build course topic order
LOG.debug("Building 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++);
LOG.debug("Added topic {} to topicOrder with order {}", e.exerciseTopicName, order - 1);
/*
* exercise recommendation part
*/
public List<Recommendation> recommendNextExercises(String assignmentId, Path testPathHost, 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);
String testRepoURL = config.group(1);
List<ExerciseCompetencyProfile> exerciseCompetencyProfiles = CompetencyAssessmentUtil.readExerciseCompetencyProfiles(testPathHost, CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME);
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++);
}
if (e.exerciseURL.equals(testRepoURL)) {
currentTopicIndex = order;
currentDifficulty = e.difficulty;
}
}
if (e.exerciseURL.equals(testRepoURL)) {
currentTopicIndex = order;
currentDifficulty = e.difficulty;
LOG.debug("Matched current testRepoURL to topic: {}, index: {}, difficulty: {}", e.exerciseTopicName, currentTopicIndex, currentDifficulty);
//filter exercises according to success
float[] unsuccessful = CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary, false);
List<ExerciseCompetencyProfile> filteredExercises = filterExercisesByTopicsAndDifficulty(
exerciseCompetencyProfiles, topicOrder, currentTopicIndex, testRepoURL,
currentDifficulty, unsuccessful, resultSummary);
//compute recommendations
List<Recommendation> recommendedExercises = new ArrayList<>();
for (ExerciseCompetencyProfile exerciseProfile : filteredExercises) {
Recommendation recommendation = new Recommendation(exerciseProfile.exerciseTopicName, exerciseProfile.exerciseURL, exerciseProfile.exerciseName,
exerciseProfile.difficulty, calculateScore(exerciseProfile, unsuccessful, topicOrder, currentDifficulty));
recommendedExercises.add(recommendation);
LOG.info("Recommending exercise "+recommendation.topic+"/"+recommendation.exerciseName+" with score "+recommendation.score);
}
//sort the recommendations for successful or resilient learners, otherwise reverse in display
recommendedExercises.stream().sorted(Recommendation.COMPARE_BY_SCORE).collect(Collectors.toList());
return recommendedExercises;
}
// filter exercises according to success
LOG.debug("Filtering exercises according to success.");
float[] unsuccessful = CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary, false);
LOG.debug("Computed unsuccessful competency profile: {}", unsuccessful);
List<ExerciseCompetencyProfile> filteredExercises = filterExercisesByTopicsAndDifficulty(
exerciseCompetencyProfiles, topicOrder, currentTopicIndex, testRepoURL,
currentDifficulty, unsuccessful, resultSummary);
LOG.debug("Filtered exercises count: {}", filteredExercises.size());
public static List<ExerciseCompetencyProfile> filterExercisesByTopicsAndDifficulty(List<ExerciseCompetencyProfile> exerciseCompetencyProfiles,
Map<String, Integer> topicOrder, int currentTopicIndex, String testRepoURL, float currentDifficulty, float[] unsuccessful,
ResultSummary resultSummary) {
//filter out all advanced topics in any case
//option for later: include next topic if fullsuccess and current difficulty == max difficulty
List<ExerciseCompetencyProfile> filteredExercises = exerciseCompetencyProfiles.stream()
.filter(testProfile -> topicOrder.get(testProfile.exerciseTopicName) <= currentTopicIndex)
.collect(Collectors.toList());
//filter by difficulty according to success
if (isFullSuccess(unsuccessful)) {
filteredExercises = filteredExercises.stream().filter(profile -> profile.difficulty >= currentDifficulty && !testRepoURL.equals(profile.exerciseURL)).collect(Collectors.toList());
} else {
filteredExercises = filteredExercises.stream().filter(profile -> profile.difficulty <= currentDifficulty).collect(Collectors.toList());
}
// compute recommendations
LOG.debug("Computing recommendations from filtered exercises.");
List<Recommendation> recommendedExercises = new ArrayList<>();
for (ExerciseCompetencyProfile exerciseProfile : filteredExercises) {
Recommendation recommendation = new Recommendation(
exerciseProfile.exerciseTopicName, exerciseProfile.exerciseURL, exerciseProfile.exerciseName,
exerciseProfile.difficulty, calculateScore(exerciseProfile, unsuccessful, topicOrder, currentDifficulty));
recommendedExercises.add(recommendation);
LOG.info("Recommending exercise {}/{} with score {}", recommendation.topic, recommendation.exerciseName, recommendation.score);
return filteredExercises;
}
public static boolean isFullSuccess(float[] unsuccessful) {
for (float value : unsuccessful) {
if (value != 0.0f) {
return false;
}
}
return true;
}
// sort the recommendations for successful or resilient learners, otherwise reverse in display
LOG.debug("Sorting recommendations.");
recommendedExercises.stream().sorted(Recommendation.COMPARE_BY_SCORE).collect(Collectors.toList());
LOG.debug("Completed recommendNextExercises with {} recommendations.", recommendedExercises.size());
return recommendedExercises;
}
public static List<ExerciseCompetencyProfile> filterExercisesByTopicsAndDifficulty(
List<ExerciseCompetencyProfile> exerciseCompetencyProfiles,
Map<String, Integer> topicOrder, int currentTopicIndex, String testRepoURL,
float currentDifficulty, float[] unsuccessful, ResultSummary resultSummary) {
LOG.debug("Starting filterExercisesByTopicsAndDifficulty with currentTopicIndex: {}, currentDifficulty: {}, testRepoURL: {}",
currentTopicIndex, currentDifficulty, testRepoURL);
// Filter out all advanced topics in any case
List<ExerciseCompetencyProfile> filteredExercises = exerciseCompetencyProfiles.stream()
.filter(testProfile -> topicOrder.get(testProfile.exerciseTopicName) <= currentTopicIndex)
.collect(Collectors.toList());
LOG.debug("Filtered exercises by topic index. Remaining exercises count: {}", filteredExercises.size());
// Filter by difficulty according to success
if (isFullSuccess(unsuccessful)) {
LOG.debug("Detected full success, filtering exercises with difficulty >= {} and excluding current testRepoURL.", currentDifficulty);
filteredExercises = filteredExercises.stream()
.filter(profile -> profile.difficulty >= currentDifficulty && !testRepoURL.equals(profile.exerciseURL))
.collect(Collectors.toList());
} else {
LOG.debug("Detected partial success, filtering exercises with difficulty <= {}.", currentDifficulty);
filteredExercises = filteredExercises.stream()
.filter(profile -> profile.difficulty <= currentDifficulty)
.collect(Collectors.toList());
}
LOG.debug("Filtered exercises count after difficulty filter: {}", filteredExercises.size());
return filteredExercises;
}
public static boolean isFullSuccess(float[] unsuccessful) {
LOG.debug("Checking for full success. Unsuccessful array: {}", unsuccessful);
for (float value : unsuccessful) {
if (value != 0.0f) {
LOG.debug("Found non-zero value in unsuccessful array: {}. Returning false for full success.", value);
return false;
}
}
LOG.debug("All values in unsuccessful array are zero. Returning true for full success.");
return true;
}
public static float calculateScore(ExerciseCompetencyProfile exerciseProfile, float[] unsuccessful,
Map<String, Integer> topicOrder, float currentDifficulty) {
LOG.debug("Starting calculateScore for exercise: {}, difficulty: {}, currentDifficulty: {}",
exerciseProfile.exerciseName, exerciseProfile.difficulty, currentDifficulty);
float score = 1.0f;
LOG.debug("Initial score set to 1.0");
// Competency profile difference to not fully achieved competencies component
for (int i = 0; i < exerciseProfile.competencyAssessments.length - 1; i++) {
float adjustment = exerciseProfile.competencyAssessments[i] * unsuccessful[i];
score += adjustment;
LOG.debug("Adjusted score by competency index {}: {}, new score: {}", i, adjustment, score);
}
// Difficulty component
float difficultyComponent = exerciseProfile.difficulty * (0.5f + Math.abs(currentDifficulty - exerciseProfile.difficulty));
score = score * difficultyComponent;
LOG.debug("Applied difficulty component: {}, updated score: {}", difficultyComponent, score);
// Topic component
float topicMultiplier = topicOrder.get(exerciseProfile.exerciseTopicName);
score *= topicMultiplier;
LOG.debug("Applied topic multiplier: {}, updated score: {}", topicMultiplier, score);
// Round score
score = Math.round(score * 10.0f) / 10.0f;
LOG.debug("Final rounded score: {}", score);
return score;
}
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
float score = 1.0f;
//competency profile difference to not fully achieved competencies component
for (int i = 0; i < exerciseProfile.competencyAssessments.length-1; i++) {
score += exerciseProfile.competencyAssessments[i] * unsuccessful[i];
}
//difficulty component
score = score * (exerciseProfile.difficulty*(0.5f+Math.abs(currentDifficulty-exerciseProfile.difficulty)));
//topic component
score *= topicOrder.get(exerciseProfile.exerciseTopicName);
score = Math.round(score * 10.0f) / 10.0f;
return score;
}
}
\ No newline at end of file
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