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; ...@@ -5,6 +5,9 @@ import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.exception.DockerException; import com.github.dockerjava.api.exception.DockerException;
import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.HostConfig; 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.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl; import com.github.dockerjava.core.DockerClientImpl;
...@@ -15,6 +18,8 @@ import org.springframework.core.env.Environment; ...@@ -15,6 +18,8 @@ import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.stream.Collectors;
@Component @Component
public class DockerUtil { public class DockerUtil {
...@@ -37,7 +42,7 @@ public class DockerUtil { ...@@ -37,7 +42,7 @@ public class DockerUtil {
dockerClient = DockerClientImpl.getInstance(dockerClientConfig, httpClient); 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)); LOG.debug(String.format("pull image: %s", image));
try { try {
...@@ -87,4 +92,62 @@ public class DockerUtil { ...@@ -87,4 +92,62 @@ public class DockerUtil {
return ret; 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; ...@@ -3,7 +3,6 @@ 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;
...@@ -36,7 +35,6 @@ public class ExecuteTestUtil { ...@@ -36,7 +35,6 @@ public class ExecuteTestUtil {
private final DockerUtil dockerUtil; private final DockerUtil dockerUtil;
private final String assignmentBasePath; private final String assignmentBasePath;
private final Path testTmpPathHost;
public ExecuteTestUtil( public ExecuteTestUtil(
Environment env, Environment env,
...@@ -49,27 +47,22 @@ public class ExecuteTestUtil { ...@@ -49,27 +47,22 @@ public class ExecuteTestUtil {
env.getProperty("data.dir"), env.getProperty("data.dir"),
env.getProperty("data.dir.test.folder.name")); env.getProperty("data.dir.test.folder.name"));
this.assignmentBasePath = p.toAbsolutePath().toString(); 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 { 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 // Define paths for the test, the submission, and where the result is expected afterwards
Path testPath = Paths.get(workDirectory.toAbsolutePath().toString(), "/test"); Path testPath = Paths.get("/data/test"); // Volume mount path in the container
Path srcPath = Paths.get(workDirectory.toAbsolutePath().toString(), "/src"); Path srcPath = Paths.get("/data/src"); // Volume mount path in the container
Path resultPath = Paths.get(workDirectory.toAbsolutePath().toString(), "/result"); Path resultPath = Paths.get("/data/result"); // Volume mount path in the container
// clone stored test to tmpdir // Clone stored test to testPath
LOG.debug("copying pre-downloaded unitttest repo"); LOG.debug("Copying pre-downloaded unit test repo");
FileUtil.copyFolder( FileUtil.copyFolder(
Paths.get(assignmentBasePath, assignmentId), Paths.get(assignmentBasePath, assignmentId),
testPath); testPath);
LOG.debug("copying exercise manifest from: %s in testPath: %s", LOG.debug("Copying exercise manifest");
assignmentBasePath + assignmentId + "_checkout",
testPath.toString() );
Files.copy(Paths.get( Files.copy(Paths.get(
assignmentBasePath, assignmentId + "_checkout", assignmentBasePath, assignmentId + "_checkout",
CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME), CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME),
...@@ -77,253 +70,178 @@ public class ExecuteTestUtil { ...@@ -77,253 +70,178 @@ public class ExecuteTestUtil {
testPath.toString(), testPath.toString(),
CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME)); CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME));
LOG.debug("copy test config"); LOG.debug("Copying test config");
Files.copy( Files.copy(
Paths.get(assignmentBasePath, assignmentId + ".txt"), Paths.get(assignmentBasePath, assignmentId + ".txt"),
Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt")); Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt"));
Files.createDirectory(resultPath); 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(workDirectory.toAbsolutePath().toString(), "config.txt").toFile()), Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX));
String image=""; 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(workDirectory.toAbsolutePath().toString(), "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 unittest image extraction");
}
image=config.group(4);
} }
else image = config.group(4);
{ } else {
image=config.group(5); 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( dockerUtil.runContainer(
image, image,
new Bind(testPathHost.toAbsolutePath().toString(), new Volume("/data/test")), new Volume("/data/test"),
new Bind(srcPathHost.toAbsolutePath().toString(), new Volume("/data/src")), new Volume("/data/src"),
new Bind(resultPathHost.toAbsolutePath().toString(), new Volume("/data/result")) new Volume("/data/result")
); );
ResultSummary resultSummary = generateResult(assignmentId, resultPath, testPathHost); ResultSummary resultSummary = generateResult(assignmentId, resultPath, testPath);
return resultSummary; return resultSummary;
} }
private ResultSummary generateResult(String assignmentId, Path resultPath, Path testPathHost) 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 = 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()) { if (!resultFile.exists() || !resultFile.isFile()) {
LOG.error(String.format("Could not find result file in %s", resultFile.getAbsolutePath())); 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(); 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 " + resultSummary.timestamp + " with " + resultSummary.results.size() + " test results.");
LOG.info("Checking for optional test competency profile information for paedagogical agent functionality..."); LOG.info("Checking for optional test competency profile information for pedagogical agent functionality...");
List<TestCompetencyProfile> testCompetencyProfiles=CompetencyAssessmentUtil.readTestCompetencyProfiles(testPathHost, 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(
"Reading Test Competency Profiles: basePath=%s, fileName=%s", "Reading Test Competency Profiles: basePath=%s, fileName=%s",
testPathHost, testPath,
CompetencyAssessmentUtil.TEST_COMPETENCY_MANIFEST_FILE_NAME CompetencyAssessmentUtil.TEST_COMPETENCY_MANIFEST_FILE_NAME
)); ));
if(testCompetencyProfiles!=null) { 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 pedagogical agent exercise recommendation functionality...");
//testPathHost or assignmentBasePath Path exerciseManifestFile = Paths.get(testPath.toString(), CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME);
Path exerciseManifestFile = Paths.get(testPathHost.toString(), CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME);
LOG.debug(String.format( LOG.debug(String.format(
"Constructing Path for exercise manifest: testPath=%s, fileName=%s -> Resulting Path=%s", "Constructing Path for exercise manifest: testPath=%s, fileName=%s -> Resulting Path=%s",
testPathHost.toString(), testPath.toString(),
CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME, CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME,
exerciseManifestFile.toString() 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, testPath, testCompetencyProfiles, resultSummary);
} }
} }
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 testPathHost, List<TestCompetencyProfile> testCompetencyProfiles, ResultSummary resultSummary)
throws FileNotFoundException { throws FileNotFoundException {
LOG.debug("Starting recommendNextExercises with assignmentId: {}", assignmentId);
// 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);
LOG.debug("Compiled regex pattern for DTA_TESTCONFIGREGEX.");
File file = Paths.get(assignmentBasePath, assignmentId + ".txt").toFile(); File file = Paths.get(assignmentBasePath, assignmentId + ".txt").toFile();
LOG.debug("Resolved file path for assignmentId {}: {}", assignmentId, file.getAbsolutePath());
FileInputStream configFileStream = new FileInputStream(file); FileInputStream configFileStream = new FileInputStream(file);
LOG.debug("Opened FileInputStream for file: {}", file.getAbsolutePath());
Matcher config = RegexUtil.extractConfig(configFileStream, pattern); Matcher config = RegexUtil.extractConfig(configFileStream, pattern);
LOG.debug("Extracted configuration using regex pattern."); String testRepoURL = config.group(1);
String testRepoURL = config.group(1) + config.group(4);
LOG.debug("Constructed testRepoURL: {}", testRepoURL);
List<ExerciseCompetencyProfile> exerciseCompetencyProfiles = CompetencyAssessmentUtil.readExerciseCompetencyProfiles( List<ExerciseCompetencyProfile> exerciseCompetencyProfiles = CompetencyAssessmentUtil.readExerciseCompetencyProfiles(testPathHost, CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME);
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; int currentTopicIndex=0;
float currentDifficulty = 0.0f; float currentDifficulty=0.0f;
// build course topic order //build course topic order
LOG.debug("Building 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) { for (ExerciseCompetencyProfile e : exerciseCompetencyProfiles) {
if (!topicOrder.containsKey(e.exerciseTopicName)) { if (!topicOrder.containsKey(e.exerciseTopicName)) {
topicOrder.put(e.exerciseTopicName, order++); topicOrder.put(e.exerciseTopicName, order++);
LOG.debug("Added topic {} to topicOrder with order {}", e.exerciseTopicName, order - 1);
} }
if (e.exerciseURL.equals(testRepoURL)) { if (e.exerciseURL.equals(testRepoURL)) {
currentTopicIndex = order; currentTopicIndex = order;
currentDifficulty = e.difficulty; currentDifficulty = e.difficulty;
LOG.debug("Matched current testRepoURL to topic: {}, index: {}, difficulty: {}", e.exerciseTopicName, currentTopicIndex, currentDifficulty);
} }
} }
// filter exercises according to success //filter exercises according to success
LOG.debug("Filtering exercises according to success.");
float[] unsuccessful = CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary, false); float[] unsuccessful = CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary, false);
LOG.debug("Computed unsuccessful competency profile: {}", unsuccessful);
List<ExerciseCompetencyProfile> filteredExercises = filterExercisesByTopicsAndDifficulty( List<ExerciseCompetencyProfile> filteredExercises = filterExercisesByTopicsAndDifficulty(
exerciseCompetencyProfiles, topicOrder, currentTopicIndex, testRepoURL, exerciseCompetencyProfiles, topicOrder, currentTopicIndex, testRepoURL,
currentDifficulty, unsuccessful, resultSummary); currentDifficulty, unsuccessful, resultSummary);
LOG.debug("Filtered exercises count: {}", filteredExercises.size());
// compute recommendations //compute recommendations
LOG.debug("Computing recommendations from filtered exercises.");
List<Recommendation> recommendedExercises = new ArrayList<>(); List<Recommendation> recommendedExercises = new ArrayList<>();
for (ExerciseCompetencyProfile exerciseProfile : filteredExercises) { for (ExerciseCompetencyProfile exerciseProfile : filteredExercises) {
Recommendation recommendation = new Recommendation( Recommendation recommendation = new Recommendation(exerciseProfile.exerciseTopicName, exerciseProfile.exerciseURL, exerciseProfile.exerciseName,
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);
LOG.info("Recommending exercise {}/{} with score {}", recommendation.topic, recommendation.exerciseName, recommendation.score); LOG.info("Recommending exercise "+recommendation.topic+"/"+recommendation.exerciseName+" with score "+recommendation.score);
} }
//sort the recommendations for successful or resilient learners, otherwise reverse in display
// 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()); recommendedExercises.stream().sorted(Recommendation.COMPARE_BY_SCORE).collect(Collectors.toList());
LOG.debug("Completed recommendNextExercises with {} recommendations.", recommendedExercises.size());
return recommendedExercises; 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 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() 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());
LOG.debug("Filtered exercises by topic index. Remaining exercises count: {}", filteredExercises.size());
// Filter by difficulty according to success //filter by difficulty according to success
if (isFullSuccess(unsuccessful)) { 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());
filteredExercises = filteredExercises.stream()
.filter(profile -> profile.difficulty >= currentDifficulty && !testRepoURL.equals(profile.exerciseURL))
.collect(Collectors.toList());
} else { } else {
LOG.debug("Detected partial success, filtering exercises with difficulty <= {}.", currentDifficulty); filteredExercises = filteredExercises.stream().filter(profile -> profile.difficulty <= currentDifficulty).collect(Collectors.toList());
filteredExercises = filteredExercises.stream()
.filter(profile -> profile.difficulty <= currentDifficulty)
.collect(Collectors.toList());
} }
LOG.debug("Filtered exercises count after difficulty filter: {}", filteredExercises.size());
return filteredExercises; return filteredExercises;
} }
public static boolean isFullSuccess(float[] unsuccessful) { public static boolean isFullSuccess(float[] unsuccessful) {
LOG.debug("Checking for full success. Unsuccessful array: {}", unsuccessful);
for (float value : unsuccessful) { for (float value : unsuccessful) {
if (value != 0.0f) { if (value != 0.0f) {
LOG.debug("Found non-zero value in unsuccessful array: {}. Returning false for full success.", value);
return false; return false;
} }
} }
LOG.debug("All values in unsuccessful array are zero. Returning true for full success.");
return true; 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);
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; 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++) {
// Competency profile difference to not fully achieved competencies component score += exerciseProfile.competencyAssessments[i] * unsuccessful[i];
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 //difficulty component
float difficultyComponent = exerciseProfile.difficulty * (0.5f + Math.abs(currentDifficulty - exerciseProfile.difficulty)); score = score * (exerciseProfile.difficulty*(0.5f+Math.abs(currentDifficulty-exerciseProfile.difficulty)));
score = score * difficultyComponent;
LOG.debug("Applied difficulty component: {}, updated score: {}", difficultyComponent, score);
// Topic component //topic component
float topicMultiplier = topicOrder.get(exerciseProfile.exerciseTopicName); score *= 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; score = Math.round(score * 10.0f) / 10.0f;
LOG.debug("Final rounded score: {}", score);
return score; 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