Commit c47948b0 authored by mamunozgil's avatar mamunozgil
Browse files

Added volumes for the backend host

parent f8255ef9
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;
...@@ -31,12 +30,11 @@ import java.util.regex.Pattern; ...@@ -31,12 +30,11 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Component @Component
public class ExecuteTestUtil { 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 assignmentBasePath;
private final Path testTmpPathHost;
public ExecuteTestUtil( public ExecuteTestUtil(
Environment env, Environment env,
...@@ -49,281 +47,201 @@ public class ExecuteTestUtil { ...@@ -49,281 +47,201 @@ 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", Files.copy(Paths.get(
testPath.toString() );
Files.copy(Paths.get(
assignmentBasePath, assignmentId + "_checkout", assignmentBasePath, assignmentId + "_checkout",
CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME), CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME),
Paths.get( Paths.get(
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); image = config.group(5);
}
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( 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", testPath.toString(),
testPathHost.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, testPath, testCompetencyProfiles, resultSummary);
resultSummary.recommendations = recommendNextExercises(assignmentId, testPathHost, 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
Pattern pattern = Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX);
// fetch repo url from original test upload File file = Paths.get(assignmentBasePath, assignmentId + ".txt").toFile();
Pattern pattern = Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX); FileInputStream configFileStream = new FileInputStream(file);
LOG.debug("Compiled regex pattern for DTA_TESTCONFIGREGEX."); Matcher config = RegexUtil.extractConfig(configFileStream, pattern);
String testRepoURL = config.group(1);
File file = Paths.get(assignmentBasePath, assignmentId + ".txt").toFile();
LOG.debug("Resolved file path for assignmentId {}: {}", assignmentId, file.getAbsolutePath()); List<ExerciseCompetencyProfile> exerciseCompetencyProfiles = CompetencyAssessmentUtil.readExerciseCompetencyProfiles(testPathHost, CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME);
FileInputStream configFileStream = new FileInputStream(file); int currentTopicIndex=0;
LOG.debug("Opened FileInputStream for file: {}", file.getAbsolutePath()); float currentDifficulty=0.0f;
Matcher config = RegexUtil.extractConfig(configFileStream, pattern); //build course topic order
LOG.debug("Extracted configuration using regex pattern."); Map<String, Integer> topicOrder = new HashMap<>();
int order = 1;
String testRepoURL = config.group(1) + config.group(4); for (ExerciseCompetencyProfile e : exerciseCompetencyProfiles) {
LOG.debug("Constructed testRepoURL: {}", testRepoURL); if (!topicOrder.containsKey(e.exerciseTopicName)) {
topicOrder.put(e.exerciseTopicName, order++);
List<ExerciseCompetencyProfile> exerciseCompetencyProfiles = CompetencyAssessmentUtil.readExerciseCompetencyProfiles( }
testPathHost, CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME); if (e.exerciseURL.equals(testRepoURL)) {
LOG.debug("Read exercise competency profiles from path: {}", testPathHost.resolve(CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME)); currentTopicIndex = order;
currentDifficulty = e.difficulty;
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);
} }
if (e.exerciseURL.equals(testRepoURL)) {
currentTopicIndex = order; //filter exercises according to success
currentDifficulty = e.difficulty; float[] unsuccessful = CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary, false);
LOG.debug("Matched current testRepoURL to topic: {}, index: {}, difficulty: {}", e.exerciseTopicName, currentTopicIndex, currentDifficulty); 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 public static List<ExerciseCompetencyProfile> filterExercisesByTopicsAndDifficulty(List<ExerciseCompetencyProfile> exerciseCompetencyProfiles,
LOG.debug("Filtering exercises according to success."); Map<String, Integer> topicOrder, int currentTopicIndex, String testRepoURL, float currentDifficulty, float[] unsuccessful,
float[] unsuccessful = CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary, false); ResultSummary resultSummary) {
LOG.debug("Computed unsuccessful competency profile: {}", unsuccessful); //filter out all advanced topics in any case
//option for later: include next topic if fullsuccess and current difficulty == max difficulty
List<ExerciseCompetencyProfile> filteredExercises = filterExercisesByTopicsAndDifficulty( List<ExerciseCompetencyProfile> filteredExercises = exerciseCompetencyProfiles.stream()
exerciseCompetencyProfiles, topicOrder, currentTopicIndex, testRepoURL, .filter(testProfile -> topicOrder.get(testProfile.exerciseTopicName) <= currentTopicIndex)
currentDifficulty, unsuccessful, resultSummary); .collect(Collectors.toList());
LOG.debug("Filtered exercises count: {}", filteredExercises.size());
//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 return filteredExercises;
LOG.debug("Computing recommendations from filtered exercises."); }
List<Recommendation> recommendedExercises = new ArrayList<>();
for (ExerciseCompetencyProfile exerciseProfile : filteredExercises) { public static boolean isFullSuccess(float[] unsuccessful) {
Recommendation recommendation = new Recommendation( for (float value : unsuccessful) {
exerciseProfile.exerciseTopicName, exerciseProfile.exerciseURL, exerciseProfile.exerciseName, if (value != 0.0f) {
exerciseProfile.difficulty, calculateScore(exerciseProfile, unsuccessful, topicOrder, currentDifficulty)); return false;
recommendedExercises.add(recommendation); }
LOG.info("Recommending exercise {}/{} with score {}", recommendation.topic, recommendation.exerciseName, recommendation.score); }
return true;
} }
// sort the recommendations for successful or resilient learners, otherwise reverse in display public static float calculateScore(ExerciseCompetencyProfile exerciseProfile, float[] unsuccessful, Map<String, Integer> topicOrder, float currentDifficulty) {
LOG.debug("Sorting recommendations."); //ensure factor 1 for full success not to blank out score, thus offset the base
recommendedExercises.stream().sorted(Recommendation.COMPARE_BY_SCORE).collect(Collectors.toList()); float score = 1.0f;
//competency profile difference to not fully achieved competencies component
LOG.debug("Completed recommendNextExercises with {} recommendations.", recommendedExercises.size()); for (int i = 0; i < exerciseProfile.competencyAssessments.length-1; i++) {
return recommendedExercises; score += exerciseProfile.competencyAssessments[i] * unsuccessful[i];
} }
public static List<ExerciseCompetencyProfile> filterExercisesByTopicsAndDifficulty( //difficulty component
List<ExerciseCompetencyProfile> exerciseCompetencyProfiles, score = score * (exerciseProfile.difficulty*(0.5f+Math.abs(currentDifficulty-exerciseProfile.difficulty)));
Map<String, Integer> topicOrder, int currentTopicIndex, String testRepoURL,
float currentDifficulty, float[] unsuccessful, ResultSummary resultSummary) { //topic component
score *= topicOrder.get(exerciseProfile.exerciseTopicName);
LOG.debug("Starting filterExercisesByTopicsAndDifficulty with currentTopicIndex: {}, currentDifficulty: {}, testRepoURL: {}",
currentTopicIndex, currentDifficulty, testRepoURL); score = Math.round(score * 10.0f) / 10.0f;
return score;
// 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;
}
} }
\ 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