package de.hftstuttgart.dtabackend.utils; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; 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 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; public class CompetencyAssessmentUtil { 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; } }