Commit c1884045 authored by mamunozgil's avatar mamunozgil
Browse files

Refactor for backtracking manifesto

parent c873f92d
This commit is part of merge request !1. Comments created here will be created in the context of that merge request.
Showing with 321 additions and 308 deletions
+321 -308
......@@ -3,134 +3,188 @@ package de.hftstuttgart.dtabackend.utils;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.fasterxml.jackson.core.exc.StreamReadException;
import com.fasterxml.jackson.databind.DatabindException;
import de.hftstuttgart.dtabackend.models.ExerciseCompetencyProfile;
import de.hftstuttgart.dtabackend.models.ICompetencyProfile;
import de.hftstuttgart.dtabackend.models.Result;
import de.hftstuttgart.dtabackend.models.ResultSummary;
import de.hftstuttgart.dtabackend.models.TestCompetencyProfile;
import java.io.FileNotFoundException;
public class CompetencyAssessmentUtil {
private static final Logger LOG = LogManager.getLogger(CompetencyAssessmentUtil.class);
public static String TEST_COMPETENCY_MANIFEST_FILE_NAME="competency-tests.mft";
public static String EXERCISE_COMPETENCY_MANIFEST_FILE_NAME="exercise-tests.mft";
/*public static void main(String[] args) throws StreamReadException, DatabindException, MalformedURLException, IOException {
ResultSummary summary=ExecuteTestUtil.generateResult("1", Path.of(args[0]), Path.of(args[1]));
System.out.println(summary.successfulTestCompetencyProfile);
}
*/
public static float[] sumTestCompetencyProfiles(List<TestCompetencyProfile> testCompetencyProfiles) {
float[] tcpTotalProfile=new float[ICompetencyProfile.MAX_COMPETENCY_DIMENSIONS];
for(TestCompetencyProfile currentProfile: testCompetencyProfiles) {
tcpTotalProfile=ICompetencyProfile.competencySum(tcpTotalProfile, currentProfile.competencyAssessments);
}
return tcpTotalProfile;
}
public static float[] sumSuccessfulCompetencyProfiles(List<TestCompetencyProfile> testCompetencyProfiles, ResultSummary resultSummary, boolean success) {
float[] sumSuccessful=new float[ICompetencyProfile.MAX_COMPETENCY_DIMENSIONS];
for(Result currentResult: resultSummary.results) {
boolean isSuccess = Integer.valueOf(currentResult.state).equals(Result.State.SUCCESS.ordinal());
if (isSuccess == success) {
TestCompetencyProfile currentProfile=new TestCompetencyProfile();
currentProfile.testPackageName=(currentResult.packageName!=null)?currentResult.packageName:"";
currentProfile.testClassName=(currentResult.className!=null)?currentResult.className:"";
currentProfile.testName=(currentResult.name!=null)?currentResult.name:"";
int testIndex=testCompetencyProfiles.indexOf(currentProfile);
if(testIndex!=-1) {
sumSuccessful=ICompetencyProfile.competencySum(sumSuccessful, testCompetencyProfiles.get(testIndex).competencyAssessments);
}
}
}
return sumSuccessful;
}
public static List<TestCompetencyProfile> readTestCompetencyProfiles(Path testPath, String fileName) {
List<TestCompetencyProfile> testCompetencyProfiles=new ArrayList<TestCompetencyProfile>();
try {
BufferedReader testCompetencyManifest=new BufferedReader(new FileReader(new File(testPath.toFile(), fileName)));
String testEntry=testCompetencyManifest.readLine();
while(testEntry!=null)
{
String[] testEntyComponents=testEntry.split(ICompetencyProfile.COMPETENCY_SEPARATOR);
TestCompetencyProfile currentProfile=new TestCompetencyProfile();
currentProfile.testPackageName=testEntyComponents[0];
currentProfile.testClassName=testEntyComponents[1];
currentProfile.testName=testEntyComponents[2];
for(int competencyIndex=0; competencyIndex<ICompetencyProfile.MAX_COMPETENCY_DIMENSIONS; competencyIndex++) {
currentProfile.competencyAssessments[competencyIndex]=Float.valueOf(testEntyComponents[competencyIndex+3]);
}
testCompetencyProfiles.add(currentProfile);
testEntry=testCompetencyManifest.readLine();
}
testCompetencyManifest.close();
LOG.info("Added "+testCompetencyProfiles.size()+" test competency profiles from test competency manifest. Optional agent functionality enabled.");
} catch (FileNotFoundException e) {
LOG.info("Test competency manifest file for agent feedback not found. Skipping optional functionality.");
testCompetencyProfiles=null;
} catch (IOException e) {
LOG.info("Test competency manifest file for agent feedback unreadable. Skipping optional functionality.");
testCompetencyProfiles=null;
}
return testCompetencyProfiles;
}
public static List<ExerciseCompetencyProfile> readExerciseCompetencyProfiles(Path exercisePath, String fileName) {
List<ExerciseCompetencyProfile> exerciseCompetencyProfiles = new ArrayList<>();
try (BufferedReader exerciseCompetencyManifest = new BufferedReader(new FileReader(new File(exercisePath.toFile(), fileName)))) {
String exerciseEntry = exerciseCompetencyManifest.readLine();
while (exerciseEntry != null) {
String[] exerciseEntyComponents = exerciseEntry.split(ExerciseCompetencyProfile.COMPETENCY_SEPARATOR);
ExerciseCompetencyProfile currentProfile = new ExerciseCompetencyProfile();
currentProfile.exerciseTopicName = exerciseEntyComponents[0];
currentProfile.exerciseName = exerciseEntyComponents[1];
currentProfile.exerciseURL = exerciseEntyComponents[2];
for (int competencyIndex = 0; competencyIndex < ExerciseCompetencyProfile.MAX_COMPETENCY_DIMENSIONS; competencyIndex++) {
currentProfile.competencyAssessments[competencyIndex] = Float.valueOf(exerciseEntyComponents[competencyIndex+3]);
}
currentProfile.difficulty = Float.parseFloat(exerciseEntyComponents[19]);
exerciseCompetencyProfiles.add(currentProfile);
exerciseEntry = exerciseCompetencyManifest.readLine();
}
exerciseCompetencyManifest.close();
LOG.info("Added " + exerciseCompetencyProfiles.size() + " test competency profiles from exercise competency manifest.");
} catch (FileNotFoundException e) {
LOG.info("Exercise competency manifest file not found.");
} catch (IOException e) {
LOG.info("Exercise competency manifest file unreadable.");
}
return exerciseCompetencyProfiles;
}
public static String packFloats(float[] array) {
return IntStream.range(0, array.length)
.mapToObj(i -> String.valueOf(array[i]))
.collect(Collectors.joining(";"));
}
private static final Logger LOG = LogManager.getLogger(CompetencyAssessmentUtil.class);
public static final String TEST_COMPETENCY_MANIFEST_FILE_NAME = "competency-tests.mft";
public static final String EXERCISE_COMPETENCY_MANIFEST_FILE_NAME = "exercise-tests.mft";
/**
* Retrieves the base directory where the test competency manifest is located
* by traversing upwards until it finds the file "exercise-tests.mft".
*
* @param startDir Starting directory path to begin the search
* @return Path of the base directory if found; otherwise, null
*/
public static Path getBaseDirectory(Path startDir) {
Path currentDir = startDir;
while (currentDir != null) {
Path manifestPath = currentDir.resolve(EXERCISE_COMPETENCY_MANIFEST_FILE_NAME);
if (Files.exists(manifestPath)) {
return currentDir;
}
currentDir = currentDir.getParent();
}
LOG.warn("Base directory with " + EXERCISE_COMPETENCY_MANIFEST_FILE_NAME + " not found starting from " + startDir);
return null;
}
/**
* Reads and loads test competency profiles from the base directory.
*
* @param exercisePath Path to the starting exercise directory
* @return List of TestCompetencyProfile instances loaded from the manifest
*/
public static List<TestCompetencyProfile> readTestCompetencyProfiles(Path exercisePath) {
Path baseDir = getBaseDirectory(exercisePath);
if (baseDir == null) {
LOG.error("Unable to locate the base directory for reading test competency profiles.");
return null;
}
return readTestCompetencyProfiles(baseDir, TEST_COMPETENCY_MANIFEST_FILE_NAME);
}
/**
* Reads and loads exercise competency profiles from the specified exercise directory.
*
* @param exercisePath Path to the exercise directory
* @return List of ExerciseCompetencyProfile instances loaded from the manifest
*/
public static List<ExerciseCompetencyProfile> readExerciseCompetencyProfiles(Path exercisePath) {
return readExerciseCompetencyProfiles(exercisePath, EXERCISE_COMPETENCY_MANIFEST_FILE_NAME);
}
private static List<TestCompetencyProfile> readTestCompetencyProfiles(Path baseDir, String fileName) {
List<TestCompetencyProfile> testCompetencyProfiles = new ArrayList<>();
Path manifestPath = baseDir.resolve(fileName);
try (BufferedReader testCompetencyManifest = new BufferedReader(new FileReader(manifestPath.toFile()))) {
String testEntry = testCompetencyManifest.readLine();
while (testEntry != null) {
String[] testEntryComponents = testEntry.split(ICompetencyProfile.COMPETENCY_SEPARATOR);
TestCompetencyProfile currentProfile = new TestCompetencyProfile();
currentProfile.testPackageName = testEntryComponents[0];
currentProfile.testClassName = testEntryComponents[1];
currentProfile.testName = testEntryComponents[2];
for (int competencyIndex = 0; competencyIndex < ICompetencyProfile.MAX_COMPETENCY_DIMENSIONS; competencyIndex++) {
currentProfile.competencyAssessments[competencyIndex] = Float.valueOf(testEntryComponents[competencyIndex + 3]);
}
testCompetencyProfiles.add(currentProfile);
testEntry = testCompetencyManifest.readLine();
}
LOG.info("Loaded " + testCompetencyProfiles.size() + " test competency profiles from " + manifestPath);
} catch (IOException e) {
LOG.error("Error reading test competency manifest file at " + manifestPath, e);
}
return testCompetencyProfiles;
}
private static List<ExerciseCompetencyProfile> readExerciseCompetencyProfiles(Path exercisePath, String fileName) {
List<ExerciseCompetencyProfile> exerciseCompetencyProfiles = new ArrayList<>();
Path manifestPath = exercisePath.resolve(fileName);
try (BufferedReader exerciseCompetencyManifest = new BufferedReader(new FileReader(manifestPath.toFile()))) {
String exerciseEntry = exerciseCompetencyManifest.readLine();
while (exerciseEntry != null) {
String[] exerciseEntryComponents = exerciseEntry.split(ExerciseCompetencyProfile.COMPETENCY_SEPARATOR);
ExerciseCompetencyProfile currentProfile = new ExerciseCompetencyProfile();
currentProfile.exerciseTopicName = exerciseEntryComponents[0];
currentProfile.exerciseName = exerciseEntryComponents[1];
currentProfile.exerciseURL = exerciseEntryComponents[2];
for (int competencyIndex = 0; competencyIndex < ExerciseCompetencyProfile.MAX_COMPETENCY_DIMENSIONS; competencyIndex++) {
currentProfile.competencyAssessments[competencyIndex] = Float.valueOf(exerciseEntryComponents[competencyIndex + 3]);
}
currentProfile.difficulty = Float.parseFloat(exerciseEntryComponents[19]);
exerciseCompetencyProfiles.add(currentProfile);
exerciseEntry = exerciseCompetencyManifest.readLine();
}
LOG.info("Loaded " + exerciseCompetencyProfiles.size() + " exercise competency profiles from " + manifestPath);
} catch (IOException e) {
LOG.error("Error reading exercise competency manifest file at " + manifestPath, e);
}
return exerciseCompetencyProfiles;
}
/**
* Converts an array of floats into a semicolon-separated string.
*
* @param array Array of float values to pack
* @return Semicolon-separated string of float values
*/
public static String packFloats(float[] array) {
return IntStream.range(0, array.length)
.mapToObj(i -> String.valueOf(array[i]))
.collect(Collectors.joining(";"));
}
/**
* Sums the competency profiles across all test competency profiles.
*
* @param testCompetencyProfiles List of test competency profiles
* @return An array representing the sum of competencies
*/
public static float[] sumTestCompetencyProfiles(List<TestCompetencyProfile> testCompetencyProfiles) {
float[] tcpTotalProfile = new float[ICompetencyProfile.MAX_COMPETENCY_DIMENSIONS];
for (TestCompetencyProfile currentProfile : testCompetencyProfiles) {
tcpTotalProfile = ICompetencyProfile.competencySum(tcpTotalProfile, currentProfile.competencyAssessments);
}
return tcpTotalProfile;
}
/**
* Sums only the successful test competency profiles, based on the results summary.
*
* @param testCompetencyProfiles List of test competency profiles
* @param resultSummary The result summary containing test results
* @param success Indicates whether to sum successful (true) or unsuccessful (false) profiles
* @return An array representing the sum of competencies for successful or unsuccessful profiles
*/
public static float[] sumSuccessfulCompetencyProfiles(List<TestCompetencyProfile> testCompetencyProfiles, ResultSummary resultSummary, boolean success) {
float[] sumSuccessful = new float[ICompetencyProfile.MAX_COMPETENCY_DIMENSIONS];
for (Result currentResult : resultSummary.results) {
boolean isSuccess = Integer.valueOf(currentResult.state).equals(Result.State.SUCCESS.ordinal());
if (isSuccess == success) {
TestCompetencyProfile currentProfile = new TestCompetencyProfile();
currentProfile.testPackageName = (currentResult.packageName != null) ? currentResult.packageName : "";
currentProfile.testClassName = (currentResult.className != null) ? currentResult.className : "";
currentProfile.testName = (currentResult.name != null) ? currentResult.name : "";
int testIndex = testCompetencyProfiles.indexOf(currentProfile);
if (testIndex != -1) {
sumSuccessful = ICompetencyProfile.competencySum(sumSuccessful, testCompetencyProfiles.get(testIndex).competencyAssessments);
}
}
}
return sumSuccessful;
}
/**
* Checks if all elements in the competency profile array are zero (indicating full success).
*
* @param competencyProfile Array of competency values
* @return True if all values are zero; false otherwise
*/
public static boolean isFullSuccess(float[] competencyProfile) {
for (float value : competencyProfile) {
if (value != 0.0f) {
return false;
}
}
return true;
}
}
......@@ -7,7 +7,6 @@ import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.Volume;
import de.hftstuttgart.dtabackend.models.ExerciseCompetencyProfile;
import de.hftstuttgart.dtabackend.models.ICompetencyProfile;
import de.hftstuttgart.dtabackend.models.Recommendation;
import de.hftstuttgart.dtabackend.models.ResultSummary;
import de.hftstuttgart.dtabackend.models.TestCompetencyProfile;
......@@ -22,10 +21,7 @@ import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
......@@ -38,217 +34,180 @@ public class ExecuteTestUtil {
private final String assignmentBasePath;
private final Path testTmpPathHost;
public ExecuteTestUtil(
Environment env,
DockerUtil dockerUtil
) {
public ExecuteTestUtil(Environment env, DockerUtil dockerUtil) {
this.dockerUtil = dockerUtil;
// set base path for assignments to be stored
Path p = Paths.get(
this.assignmentBasePath = Paths.get(
env.getProperty("data.dir"),
env.getProperty("data.dir.test.folder.name"));
this.assignmentBasePath = p.toAbsolutePath().toString();
env.getProperty("data.dir.test.folder.name"))
.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");
// clone stored test to tmpdir
LOG.debug("copying pre-downloaded unitttest repo");
FileUtil.copyFolder(
Paths.get(assignmentBasePath, assignmentId),
testPath);
LOG.debug("copy test config");
Files.copy(
Paths.get(assignmentBasePath, assignmentId + ".txt"),
Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt"));
// Define paths
Path testPath = workDirectory.resolve("test");
Path srcPath = workDirectory.resolve("src");
Path resultPath = workDirectory.resolve("result");
// Clone test to temporary directory
LOG.debug("Copying pre-downloaded unittest repo");
FileUtil.copyFolder(Paths.get(assignmentBasePath, assignmentId), testPath);
// Copy configuration file
LOG.debug("Copy test config");
Files.copy(Paths.get(assignmentBasePath, assignmentId + ".txt"), workDirectory.resolve("config.txt"));
Files.createDirectory(resultPath);
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);
}
// 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()
);
// Load container image configuration
LOG.info("Reading test config");
String image = loadImageConfig(workDirectory);
// Define paths to mount in the container
Path testPathHost = testTmpPathHost.resolve(workDirectory.getFileName()).resolve(testPath.getFileName());
Path srcPathHost = testTmpPathHost.resolve(workDirectory.getFileName()).resolve(srcPath.getFileName());
Path resultPathHost = testTmpPathHost.resolve(workDirectory.getFileName()).resolve(resultPath.getFileName());
// start test-container with professor given image and bind mounts for test, submission and result
// Start container with mounts
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 Bind(testPathHost.toString(), new Volume("/data/test")),
new Bind(srcPathHost.toString(), new Volume("/data/src")),
new Bind(resultPathHost.toString(), new Volume("/data/result"))
);
ResultSummary resultSummary = generateResult(assignmentId, resultPath, testPathHost);
return resultSummary;
return generateResult(assignmentId, resultPath, testPathHost);
}
private String loadImageConfig(Path workDirectory) throws FileNotFoundException {
Matcher config = RegexUtil.extractConfig(
new FileInputStream(workDirectory.resolve("config.txt").toFile()),
Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX));
if (config == null) {
config = RegexUtil.extractConfig(
new FileInputStream(workDirectory.resolve("config.txt").toFile()),
Pattern.compile(RegexUtil.TESTCONFIGREGEX));
if (config == null) {
throw new RuntimeException("Couldn't find repo config for unittest image extraction");
}
return config.group(4);
}
return config.group(5);
}
private ResultSummary generateResult(String assignmentId, Path resultPath, Path testPathHost)
throws IOException, StreamReadException, DatabindException, MalformedURLException {
// define expected result file
File resultFile = Paths.get(resultPath.toAbsolutePath().toString(), "result.json").toFile();
private ResultSummary generateResult(String assignmentId, Path resultPath, Path testPathHost)
throws IOException, StreamReadException, DatabindException, MalformedURLException {
File resultFile = resultPath.resolve("result.json").toFile();
// check if result file is there
if (!resultFile.exists() || !resultFile.isFile()) {
LOG.error(String.format("Could not find result file in %s", resultFile.getAbsolutePath()));
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 at " + resultSummary.timestamp + " with " + resultSummary.results.size() + " test results.");
LOG.info("Checking for optional test competency profile information...");
List<TestCompetencyProfile> testCompetencyProfiles = CompetencyAssessmentUtil.readTestCompetencyProfiles(testPathHost);
if (testCompetencyProfiles != null) {
LOG.info("Found test competency profiles, generating profile data...");
resultSummary.overallTestCompetencyProfile = CompetencyAssessmentUtil.packFloats(CompetencyAssessmentUtil.sumTestCompetencyProfiles(testCompetencyProfiles));
resultSummary.successfulTestCompetencyProfile = CompetencyAssessmentUtil.packFloats(CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary, true));
Path exerciseManifestFile = testPathHost.resolve(CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME);
if (Files.exists(exerciseManifestFile)) {
LOG.info("Found exercise competency profiles, generating recommendations...");
resultSummary.recommendations = recommendNextExercises(assignmentId, testPathHost, testCompetencyProfiles, resultSummary);
}
}
return resultSummary;
}
public List<Recommendation> recommendNextExercises(String assignmentId, Path testPathHost, List<TestCompetencyProfile> testCompetencyProfiles, ResultSummary resultSummary)
throws FileNotFoundException {
String testRepoURL = RegexUtil.extractConfig(
new FileInputStream(Paths.get(assignmentBasePath, assignmentId + ".txt").toFile()),
Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX)).group(1);
List<ExerciseCompetencyProfile> exerciseCompetencyProfiles = CompetencyAssessmentUtil.readExerciseCompetencyProfiles(testPathHost);
int currentTopicIndex = 0;
float currentDifficulty = 0.0f;
Map<String, Integer> topicOrder = new HashMap<>();
int order = 1;
// Determine currentTopicIndex and set currentDifficulty based on exercise profiles
for (ExerciseCompetencyProfile e : exerciseCompetencyProfiles) {
if (!topicOrder.containsKey(e.exerciseTopicName)) {
topicOrder.put(e.exerciseTopicName, order++);
}
if (e.exerciseURL.equals(testRepoURL)) {
currentTopicIndex = order;
currentDifficulty = e.difficulty; // Directly assign to currentDifficulty
}
}
// Sum competencies for unsuccessful tests
float[] unsuccessful = CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary, false);
// Filter exercises based on topics and difficulty
List<ExerciseCompetencyProfile> filteredExercises = filterExercisesByTopicsAndDifficulty(
exerciseCompetencyProfiles, topicOrder, currentTopicIndex, testRepoURL, currentDifficulty, unsuccessful);
// Generate recommendations without using lambda expressions
List<Recommendation> recommendedExercises = new ArrayList<>();
for (ExerciseCompetencyProfile profile : filteredExercises) {
float score = calculateScore(profile, unsuccessful, topicOrder, currentDifficulty);
Recommendation recommendation = new Recommendation(
profile.exerciseTopicName,
profile.exerciseURL,
profile.exerciseName,
profile.difficulty,
score);
recommendedExercises.add(recommendation);
}
// Sort the recommendations using Collections.sort and a comparator
Collections.sort(recommendedExercises, Recommendation.COMPARE_BY_SCORE);
return recommendedExercises;
}
public static List<ExerciseCompetencyProfile> filterExercisesByTopicsAndDifficulty(List<ExerciseCompetencyProfile> profiles,
Map<String, Integer> topicOrder, int currentTopicIndex, String testRepoURL, float currentDifficulty, float[] unsuccessful) {
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);
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.toAbsolutePath().toString(), CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME);
if (Files.exists(exerciseManifestFile)) {
LOG.info("Found optional exercise competency profiles, generating recommendations...");
resultSummary.recommendations = recommendNextExercises(assignmentId, testPathHost, testCompetencyProfiles, resultSummary);
}
List<ExerciseCompetencyProfile> filteredExercises = profiles.stream()
.filter(profile -> topicOrder.get(profile.exerciseTopicName) <= currentTopicIndex)
.collect(Collectors.toList());
if (CompetencyAssessmentUtil.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());
}
return resultSummary;
}
/*
* 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;
}
}
//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;
}
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());
}
return filteredExercises;
}
public static boolean isFullSuccess(float[] unsuccessful) {
for (float value : unsuccessful) {
if (value != 0.0f) {
return false;
}
}
return true;
}
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;
}
return filteredExercises;
}
public static float calculateScore(ExerciseCompetencyProfile profile, float[] unsuccessful, Map<String, Integer> topicOrder, float currentDifficulty) {
float score = 1.0f;
for (int i = 0; i < profile.competencyAssessments.length - 1; i++) {
score += profile.competencyAssessments[i] * unsuccessful[i];
}
score *= profile.difficulty * (0.5f + Math.abs(currentDifficulty - profile.difficulty));
score *= topicOrder.getOrDefault(profile.exerciseTopicName, 1);
return Math.round(score * 10.0f) / 10.0f;
}
}
\ 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