Commit 375b37bf authored by mamunozgil's avatar mamunozgil
Browse files

Refactor for backtracking manifesto

parent 78c0a10f
Pipeline #10340 passed with stage
Showing with 321 additions and 308 deletions
+321 -308
...@@ -3,134 +3,188 @@ package de.hftstuttgart.dtabackend.utils; ...@@ -3,134 +3,188 @@ package de.hftstuttgart.dtabackend.utils;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.net.MalformedURLException; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; 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.ExerciseCompetencyProfile;
import de.hftstuttgart.dtabackend.models.ICompetencyProfile; import de.hftstuttgart.dtabackend.models.ICompetencyProfile;
import de.hftstuttgart.dtabackend.models.Result; import de.hftstuttgart.dtabackend.models.Result;
import de.hftstuttgart.dtabackend.models.ResultSummary; import de.hftstuttgart.dtabackend.models.ResultSummary;
import de.hftstuttgart.dtabackend.models.TestCompetencyProfile; import de.hftstuttgart.dtabackend.models.TestCompetencyProfile;
import java.io.FileNotFoundException;
public class CompetencyAssessmentUtil { public class CompetencyAssessmentUtil {
private static final Logger LOG = LogManager.getLogger(CompetencyAssessmentUtil.class); private static final Logger LOG = LogManager.getLogger(CompetencyAssessmentUtil.class);
public static String TEST_COMPETENCY_MANIFEST_FILE_NAME="competency-tests.mft"; public static final String TEST_COMPETENCY_MANIFEST_FILE_NAME = "competency-tests.mft";
public static String EXERCISE_COMPETENCY_MANIFEST_FILE_NAME="exercise-tests.mft"; public static final 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])); * Retrieves the base directory where the test competency manifest is located
System.out.println(summary.successfulTestCompetencyProfile); * 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 float[] sumTestCompetencyProfiles(List<TestCompetencyProfile> testCompetencyProfiles) { public static Path getBaseDirectory(Path startDir) {
float[] tcpTotalProfile=new float[ICompetencyProfile.MAX_COMPETENCY_DIMENSIONS]; Path currentDir = startDir;
for(TestCompetencyProfile currentProfile: testCompetencyProfiles) { while (currentDir != null) {
tcpTotalProfile=ICompetencyProfile.competencySum(tcpTotalProfile, currentProfile.competencyAssessments); Path manifestPath = currentDir.resolve(EXERCISE_COMPETENCY_MANIFEST_FILE_NAME);
if (Files.exists(manifestPath)) {
return currentDir;
} }
return tcpTotalProfile; currentDir = currentDir.getParent();
} }
LOG.warn("Base directory with " + EXERCISE_COMPETENCY_MANIFEST_FILE_NAME + " not found starting from " + startDir);
public static float[] sumSuccessfulCompetencyProfiles(List<TestCompetencyProfile> testCompetencyProfiles, ResultSummary resultSummary, boolean success) { return null;
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);
} }
/**
* 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);
} }
return sumSuccessful;
/**
* 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);
} }
public static List<TestCompetencyProfile> readTestCompetencyProfiles(Path testPath, String fileName) { private static List<TestCompetencyProfile> readTestCompetencyProfiles(Path baseDir, String fileName) {
List<TestCompetencyProfile> testCompetencyProfiles=new ArrayList<TestCompetencyProfile>(); List<TestCompetencyProfile> testCompetencyProfiles = new ArrayList<>();
try { Path manifestPath = baseDir.resolve(fileName);
BufferedReader testCompetencyManifest=new BufferedReader(new FileReader(new File(testPath.toFile(), fileName)));
String testEntry=testCompetencyManifest.readLine(); try (BufferedReader testCompetencyManifest = new BufferedReader(new FileReader(manifestPath.toFile()))) {
while(testEntry!=null) String testEntry = testCompetencyManifest.readLine();
{ while (testEntry != null) {
String[] testEntyComponents=testEntry.split(ICompetencyProfile.COMPETENCY_SEPARATOR); String[] testEntryComponents = testEntry.split(ICompetencyProfile.COMPETENCY_SEPARATOR);
TestCompetencyProfile currentProfile=new TestCompetencyProfile(); TestCompetencyProfile currentProfile = new TestCompetencyProfile();
currentProfile.testPackageName=testEntyComponents[0]; currentProfile.testPackageName = testEntryComponents[0];
currentProfile.testClassName=testEntyComponents[1]; currentProfile.testClassName = testEntryComponents[1];
currentProfile.testName=testEntyComponents[2]; currentProfile.testName = testEntryComponents[2];
for(int competencyIndex=0; competencyIndex<ICompetencyProfile.MAX_COMPETENCY_DIMENSIONS; competencyIndex++) { for (int competencyIndex = 0; competencyIndex < ICompetencyProfile.MAX_COMPETENCY_DIMENSIONS; competencyIndex++) {
currentProfile.competencyAssessments[competencyIndex]=Float.valueOf(testEntyComponents[competencyIndex+3]); currentProfile.competencyAssessments[competencyIndex] = Float.valueOf(testEntryComponents[competencyIndex + 3]);
} }
testCompetencyProfiles.add(currentProfile); testCompetencyProfiles.add(currentProfile);
testEntry=testCompetencyManifest.readLine(); testEntry = testCompetencyManifest.readLine();
} }
testCompetencyManifest.close(); LOG.info("Loaded " + testCompetencyProfiles.size() + " test competency profiles from " + manifestPath);
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) { } catch (IOException e) {
LOG.info("Test competency manifest file for agent feedback unreadable. Skipping optional functionality."); LOG.error("Error reading test competency manifest file at " + manifestPath, e);
testCompetencyProfiles=null;
} }
return testCompetencyProfiles; return testCompetencyProfiles;
} }
public static List<ExerciseCompetencyProfile> readExerciseCompetencyProfiles(Path exercisePath, String fileName) { private static List<ExerciseCompetencyProfile> readExerciseCompetencyProfiles(Path exercisePath, String fileName) {
List<ExerciseCompetencyProfile> exerciseCompetencyProfiles = new ArrayList<>(); List<ExerciseCompetencyProfile> exerciseCompetencyProfiles = new ArrayList<>();
Path manifestPath = exercisePath.resolve(fileName);
try (BufferedReader exerciseCompetencyManifest = new BufferedReader(new FileReader(new File(exercisePath.toFile(), fileName)))) { try (BufferedReader exerciseCompetencyManifest = new BufferedReader(new FileReader(manifestPath.toFile()))) {
String exerciseEntry = exerciseCompetencyManifest.readLine(); String exerciseEntry = exerciseCompetencyManifest.readLine();
while (exerciseEntry != null) { while (exerciseEntry != null) {
String[] exerciseEntyComponents = exerciseEntry.split(ExerciseCompetencyProfile.COMPETENCY_SEPARATOR); String[] exerciseEntryComponents = exerciseEntry.split(ExerciseCompetencyProfile.COMPETENCY_SEPARATOR);
ExerciseCompetencyProfile currentProfile = new ExerciseCompetencyProfile(); ExerciseCompetencyProfile currentProfile = new ExerciseCompetencyProfile();
currentProfile.exerciseTopicName = exerciseEntryComponents[0];
currentProfile.exerciseTopicName = exerciseEntyComponents[0]; currentProfile.exerciseName = exerciseEntryComponents[1];
currentProfile.exerciseName = exerciseEntyComponents[1]; currentProfile.exerciseURL = exerciseEntryComponents[2];
currentProfile.exerciseURL = exerciseEntyComponents[2];
for (int competencyIndex = 0; competencyIndex < ExerciseCompetencyProfile.MAX_COMPETENCY_DIMENSIONS; competencyIndex++) { for (int competencyIndex = 0; competencyIndex < ExerciseCompetencyProfile.MAX_COMPETENCY_DIMENSIONS; competencyIndex++) {
currentProfile.competencyAssessments[competencyIndex] = Float.valueOf(exerciseEntyComponents[competencyIndex+3]); currentProfile.competencyAssessments[competencyIndex] = Float.valueOf(exerciseEntryComponents[competencyIndex + 3]);
} }
currentProfile.difficulty = Float.parseFloat(exerciseEntryComponents[19]);
currentProfile.difficulty = Float.parseFloat(exerciseEntyComponents[19]);
exerciseCompetencyProfiles.add(currentProfile); exerciseCompetencyProfiles.add(currentProfile);
exerciseEntry = exerciseCompetencyManifest.readLine(); exerciseEntry = exerciseCompetencyManifest.readLine();
} }
exerciseCompetencyManifest.close(); LOG.info("Loaded " + exerciseCompetencyProfiles.size() + " exercise competency profiles from " + manifestPath);
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) { } catch (IOException e) {
LOG.info("Exercise competency manifest file unreadable."); LOG.error("Error reading exercise competency manifest file at " + manifestPath, e);
} }
return exerciseCompetencyProfiles; 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) { public static String packFloats(float[] array) {
return IntStream.range(0, array.length) return IntStream.range(0, array.length)
.mapToObj(i -> String.valueOf(array[i])) .mapToObj(i -> String.valueOf(array[i]))
.collect(Collectors.joining(";")); .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; ...@@ -7,7 +7,6 @@ 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;
import de.hftstuttgart.dtabackend.models.ICompetencyProfile;
import de.hftstuttgart.dtabackend.models.Recommendation; import de.hftstuttgart.dtabackend.models.Recommendation;
import de.hftstuttgart.dtabackend.models.ResultSummary; import de.hftstuttgart.dtabackend.models.ResultSummary;
import de.hftstuttgart.dtabackend.models.TestCompetencyProfile; import de.hftstuttgart.dtabackend.models.TestCompetencyProfile;
...@@ -22,10 +21,7 @@ import java.net.MalformedURLException; ...@@ -22,10 +21,7 @@ import java.net.MalformedURLException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList; import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -38,217 +34,180 @@ public class ExecuteTestUtil { ...@@ -38,217 +34,180 @@ public class ExecuteTestUtil {
private final String assignmentBasePath; private final String assignmentBasePath;
private final Path testTmpPathHost; private final Path testTmpPathHost;
public ExecuteTestUtil( public ExecuteTestUtil(Environment env, DockerUtil dockerUtil) {
Environment env,
DockerUtil dockerUtil
) {
this.dockerUtil = dockerUtil; this.dockerUtil = dockerUtil;
this.assignmentBasePath = Paths.get(
// set base path for assignments to be stored
Path p = Paths.get(
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(); .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")); 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
// define paths for the test, the submission and where the result is to be expected afterwards Path testPath = workDirectory.resolve("test");
Path testPath = Paths.get(workDirectory.toAbsolutePath().toString(), "/test"); Path srcPath = workDirectory.resolve("src");
Path srcPath = Paths.get(workDirectory.toAbsolutePath().toString(), "/src"); Path resultPath = workDirectory.resolve("result");
Path resultPath = Paths.get(workDirectory.toAbsolutePath().toString(), "/result");
// Clone test to temporary directory
// clone stored test to tmpdir LOG.debug("Copying pre-downloaded unittest repo");
LOG.debug("copying pre-downloaded unitttest repo"); FileUtil.copyFolder(Paths.get(assignmentBasePath, assignmentId), testPath);
FileUtil.copyFolder(
Paths.get(assignmentBasePath, assignmentId), // Copy configuration file
testPath); LOG.debug("Copy test config");
Files.copy(Paths.get(assignmentBasePath, assignmentId + ".txt"), workDirectory.resolve("config.txt"));
LOG.debug("copy test config");
Files.copy(
Paths.get(assignmentBasePath, assignmentId + ".txt"),
Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt"));
Files.createDirectory(resultPath); Files.createDirectory(resultPath);
LOG.info("reading test config"); // Load container image configuration
Matcher config = RegexUtil.extractConfig( LOG.info("Reading test config");
new FileInputStream(Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt").toFile()), Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX)); String image = loadImageConfig(workDirectory);
String image="";
if(config==null) // Define paths to mount in the container
{ Path testPathHost = testTmpPathHost.resolve(workDirectory.getFileName()).resolve(testPath.getFileName());
config = RegexUtil.extractConfig( Path srcPathHost = testTmpPathHost.resolve(workDirectory.getFileName()).resolve(srcPath.getFileName());
new FileInputStream(Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt").toFile()), Pattern.compile(RegexUtil.TESTCONFIGREGEX)); Path resultPathHost = testTmpPathHost.resolve(workDirectory.getFileName()).resolve(resultPath.getFileName());
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()
);
// start test-container with professor given image and bind mounts for test, submission and result // Start container with mounts
dockerUtil.runContainer( dockerUtil.runContainer(
image, image,
new Bind(testPathHost.toAbsolutePath().toString(), new Volume("/data/test")), new Bind(testPathHost.toString(), new Volume("/data/test")),
new Bind(srcPathHost.toAbsolutePath().toString(), new Volume("/data/src")), new Bind(srcPathHost.toString(), new Volume("/data/src")),
new Bind(resultPathHost.toAbsolutePath().toString(), new Volume("/data/result")) new Bind(resultPathHost.toString(), new Volume("/data/result"))
); );
ResultSummary resultSummary = generateResult(assignmentId, resultPath, testPathHost); return generateResult(assignmentId, resultPath, testPathHost);
}
return resultSummary; 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) private ResultSummary generateResult(String assignmentId, Path resultPath, Path testPathHost)
throws IOException, StreamReadException, DatabindException, MalformedURLException { throws IOException, StreamReadException, DatabindException, MalformedURLException {
// define expected result file File resultFile = resultPath.resolve("result.json").toFile();
File resultFile = Paths.get(resultPath.toAbsolutePath().toString(), "result.json").toFile();
// 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 at " + 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...");
List<TestCompetencyProfile> testCompetencyProfiles=CompetencyAssessmentUtil.readTestCompetencyProfiles(testPathHost, CompetencyAssessmentUtil.TEST_COMPETENCY_MANIFEST_FILE_NAME); List<TestCompetencyProfile> testCompetencyProfiles = CompetencyAssessmentUtil.readTestCompetencyProfiles(testPathHost);
if(testCompetencyProfiles!=null) {
LOG.info("Found optional test competency profiles, generating agent profile data..."); if (testCompetencyProfiles != null) {
resultSummary.overallTestCompetencyProfile=CompetencyAssessmentUtil.packFloats(CompetencyAssessmentUtil.sumTestCompetencyProfiles(testCompetencyProfiles)); LOG.info("Found test competency profiles, generating profile data...");
resultSummary.successfulTestCompetencyProfile=CompetencyAssessmentUtil.packFloats(CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary, true)); 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 = testPathHost.resolve(CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME);
Path exerciseManifestFile = Paths.get(testPathHost.toAbsolutePath().toString(), CompetencyAssessmentUtil.EXERCISE_COMPETENCY_MANIFEST_FILE_NAME);
if (Files.exists(exerciseManifestFile)) { if (Files.exists(exerciseManifestFile)) {
LOG.info("Found optional exercise competency profiles, generating recommendations..."); LOG.info("Found exercise competency profiles, generating recommendations...");
resultSummary.recommendations = recommendNextExercises(assignmentId, testPathHost, testCompetencyProfiles, resultSummary); resultSummary.recommendations = recommendNextExercises(assignmentId, testPathHost, testCompetencyProfiles, resultSummary);
} }
} }
return resultSummary; return resultSummary;
} }
/*
* 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 {
// 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); String testRepoURL = RegexUtil.extractConfig(
new FileInputStream(Paths.get(assignmentBasePath, assignmentId + ".txt").toFile()),
Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX)).group(1);
int currentTopicIndex=0; List<ExerciseCompetencyProfile> exerciseCompetencyProfiles = CompetencyAssessmentUtil.readExerciseCompetencyProfiles(testPathHost);
float currentDifficulty=0.0f;
//build course topic order int currentTopicIndex = 0;
float currentDifficulty = 0.0f;
Map<String, Integer> topicOrder = new HashMap<>(); Map<String, Integer> topicOrder = new HashMap<>();
int order = 1; int order = 1;
// Determine currentTopicIndex and set currentDifficulty based on exercise profiles
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++);
} }
if (e.exerciseURL.equals(testRepoURL)) { if (e.exerciseURL.equals(testRepoURL)) {
currentTopicIndex = order; currentTopicIndex = order;
currentDifficulty = e.difficulty; currentDifficulty = e.difficulty; // Directly assign to currentDifficulty
} }
} }
//filter exercises according to success // Sum competencies for unsuccessful tests
float[] unsuccessful = CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary, false); float[] unsuccessful = CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary, false);
// Filter exercises based on topics and difficulty
List<ExerciseCompetencyProfile> filteredExercises = filterExercisesByTopicsAndDifficulty( List<ExerciseCompetencyProfile> filteredExercises = filterExercisesByTopicsAndDifficulty(
exerciseCompetencyProfiles, topicOrder, currentTopicIndex, testRepoURL, exerciseCompetencyProfiles, topicOrder, currentTopicIndex, testRepoURL, currentDifficulty, unsuccessful);
currentDifficulty, unsuccessful, resultSummary);
//compute recommendations // Generate recommendations without using lambda expressions
List<Recommendation> recommendedExercises = new ArrayList<>(); List<Recommendation> recommendedExercises = new ArrayList<>();
for (ExerciseCompetencyProfile exerciseProfile : filteredExercises) { for (ExerciseCompetencyProfile profile : filteredExercises) {
Recommendation recommendation = new Recommendation(exerciseProfile.exerciseTopicName, exerciseProfile.exerciseURL, exerciseProfile.exerciseName, float score = calculateScore(profile, unsuccessful, topicOrder, currentDifficulty);
exerciseProfile.difficulty, calculateScore(exerciseProfile, unsuccessful, topicOrder, currentDifficulty)); Recommendation recommendation = new Recommendation(
profile.exerciseTopicName,
profile.exerciseURL,
profile.exerciseName,
profile.difficulty,
score);
recommendedExercises.add(recommendation); 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()); // Sort the recommendations using Collections.sort and a comparator
Collections.sort(recommendedExercises, Recommendation.COMPARE_BY_SCORE);
return recommendedExercises; return recommendedExercises;
} }
public static List<ExerciseCompetencyProfile> filterExercisesByTopicsAndDifficulty(List<ExerciseCompetencyProfile> exerciseCompetencyProfiles, public static List<ExerciseCompetencyProfile> filterExercisesByTopicsAndDifficulty(List<ExerciseCompetencyProfile> profiles,
Map<String, Integer> topicOrder, int currentTopicIndex, String testRepoURL, float currentDifficulty, float[] unsuccessful, Map<String, Integer> topicOrder, int currentTopicIndex, String testRepoURL, float currentDifficulty, float[] unsuccessful) {
ResultSummary resultSummary) {
//filter out all advanced topics in any case List<ExerciseCompetencyProfile> filteredExercises = profiles.stream()
//option for later: include next topic if fullsuccess and current difficulty == max difficulty .filter(profile -> topicOrder.get(profile.exerciseTopicName) <= currentTopicIndex)
List<ExerciseCompetencyProfile> filteredExercises = exerciseCompetencyProfiles.stream()
.filter(testProfile -> topicOrder.get(testProfile.exerciseTopicName) <= currentTopicIndex)
.collect(Collectors.toList()); .collect(Collectors.toList());
//filter by difficulty according to success if (CompetencyAssessmentUtil.isFullSuccess(unsuccessful)) {
if (isFullSuccess(unsuccessful)) { filteredExercises = filteredExercises.stream()
filteredExercises = filteredExercises.stream().filter(profile -> profile.difficulty >= currentDifficulty && !testRepoURL.equals(profile.exerciseURL)).collect(Collectors.toList()); .filter(profile -> profile.difficulty >= currentDifficulty && !testRepoURL.equals(profile.exerciseURL))
.collect(Collectors.toList());
} else { } else {
filteredExercises = filteredExercises.stream().filter(profile -> profile.difficulty <= currentDifficulty).collect(Collectors.toList()); filteredExercises = filteredExercises.stream()
.filter(profile -> profile.difficulty <= currentDifficulty)
.collect(Collectors.toList());
} }
return filteredExercises; return filteredExercises;
} }
public static boolean isFullSuccess(float[] unsuccessful) { public static float calculateScore(ExerciseCompetencyProfile profile, float[] unsuccessful, Map<String, Integer> topicOrder, float currentDifficulty) {
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; float score = 1.0f;
//competency profile difference to not fully achieved competencies component for (int i = 0; i < profile.competencyAssessments.length - 1; i++) {
for (int i = 0; i < exerciseProfile.competencyAssessments.length-1; i++) { score += profile.competencyAssessments[i] * unsuccessful[i];
score += exerciseProfile.competencyAssessments[i] * unsuccessful[i];
} }
//difficulty component score *= profile.difficulty * (0.5f + Math.abs(currentDifficulty - profile.difficulty));
score = score * (exerciseProfile.difficulty*(0.5f+Math.abs(currentDifficulty-exerciseProfile.difficulty))); score *= topicOrder.getOrDefault(profile.exerciseTopicName, 1);
//topic component
score *= topicOrder.get(exerciseProfile.exerciseTopicName);
score = Math.round(score * 10.0f) / 10.0f; return 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