Browse Source

Merge pull request #569 from nestabentum/reportViewer_multiRoot

Enable Multiroot for Report Viewer
Timur Sağlam 4 years ago
parent
commit
1c401de418

+ 2 - 1
jplag.cli/src/main/java/de/jplag/CLI.java

@@ -64,7 +64,8 @@ public final class CLI {
             JPlag program = new JPlag(options);
             logger.info("JPlag initialized");
             JPlagResult result = program.run();
-            ReportObjectFactory.createAndSaveReport(result, arguments.getString(RESULT_FOLDER.flagWithoutDash()));
+            ReportObjectFactory reportObjectFactory = new ReportObjectFactory();
+            reportObjectFactory.createAndSaveReport(result, arguments.getString(RESULT_FOLDER.flagWithoutDash()));
 
         } catch (ExitException exception) {
             logger.error(exception.getMessage(), exception);

+ 110 - 0
jplag/src/main/java/de/jplag/reporting/jsonfactory/ComparisonReportWriter.java

@@ -0,0 +1,110 @@
+package de.jplag.reporting.jsonfactory;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import de.jplag.*;
+import de.jplag.reporting.reportobject.model.ComparisonReport;
+import de.jplag.reporting.reportobject.model.Match;
+
+/**
+ * Writes {@link ComparisonReport}s of given {@link JPlagResult} to the disk under the specified path. Instantiated with
+ * a function that associates a submission to its id.
+ */
+public class ComparisonReportWriter {
+
+    private static final FileWriter FILE_WRITER = new FileWriter();
+    private final Function<Submission, String> submissionToIdFunction;
+    private final Map<String, Map<String, String>> submissionIdToComparisonFileName = new HashMap<>();
+
+    public ComparisonReportWriter(Function<Submission, String> submissionToIdFunction) {
+        this.submissionToIdFunction = submissionToIdFunction;
+    }
+
+    /**
+     * Generates detailed ComparisonReport DTO for each comparison in a JPlagResult and writes them to the disk as json
+     * files.
+     * @param jPlagResult The JPlagResult to generate the comparison reports from. contains information about a comparison
+     * @param path The path to write the comparison files to
+     * @return Nested map that associates each pair of submissions (by their ids) to their comparison file name. The
+     * comparison file name for submission with id id1 and id2 can be fetched by executing get two times:
+     * map.get(id1).get(id2). The nested map is symmetrical therefore, both map.get(id1).get(id2) and map.get(id2).get(id1)
+     * yield the same result.
+     */
+    public Map<String, Map<String, String>> writeComparisonReports(JPlagResult jPlagResult, String path) {
+        int numberOfComparisons = jPlagResult.getOptions().getMaximumNumberOfComparisons();
+        List<JPlagComparison> comparisons = jPlagResult.getComparisons(numberOfComparisons);
+        writeComparisons(jPlagResult, path, comparisons);
+        return submissionIdToComparisonFileName;
+    }
+
+    private void writeComparisons(JPlagResult jPlagResult, String path, List<JPlagComparison> comparisons) {
+        for (JPlagComparison comparison : comparisons) {
+            String firstSubmissionId = submissionToIdFunction.apply(comparison.getFirstSubmission());
+            String secondSubmissionId = submissionToIdFunction.apply(comparison.getSecondSubmission());
+            String fileName = generateComparisonName(firstSubmissionId, secondSubmissionId);
+            addToLookUp(firstSubmissionId, secondSubmissionId, fileName);
+            var comparisonReport = new ComparisonReport(firstSubmissionId, secondSubmissionId, comparison.similarity(),
+                    convertMatchesToReportMatches(jPlagResult, comparison));
+            FILE_WRITER.saveAsJSON(comparisonReport, path, fileName);
+        }
+    }
+
+    private void addToLookUp(String firstSubmissionId, String secondSubmissionId, String fileName) {
+        writeToMap(secondSubmissionId, firstSubmissionId, fileName);
+        writeToMap(firstSubmissionId, secondSubmissionId, fileName);
+    }
+
+    private void writeToMap(String id1, String id2, String comparisonFileName) {
+        submissionIdToComparisonFileName.putIfAbsent(id1, new HashMap<>());
+        submissionIdToComparisonFileName.get(id1).put(id2, comparisonFileName);
+    }
+
+    private String generateComparisonName(String firstSubmissionId, String secondSubmissionId) {
+        String name = concatenate(firstSubmissionId, secondSubmissionId);
+        String finalName = name;
+        long timesNameAlreadyExists = submissionIdToComparisonFileName.values().stream().filter(map -> map.containsValue(finalName)).count();
+        if (timesNameAlreadyExists > 0) {
+            name = concatenate(firstSubmissionId, secondSubmissionId, timesNameAlreadyExists + 1);
+        }
+        return name;
+
+    }
+
+    private String concatenate(String firstSubmissionId, String secondSubmissionId, long index) {
+        return firstSubmissionId.concat("-").concat(secondSubmissionId).concat(index > 0 ? "-" + index : "").concat(".json");
+    }
+
+    private String concatenate(String firstSubmissionId, String secondSubmissionId) {
+        return concatenate(firstSubmissionId, secondSubmissionId, 0);
+    }
+
+    private List<Match> convertMatchesToReportMatches(JPlagResult result, JPlagComparison comparison) {
+        return comparison.getMatches().stream()
+                .map(match -> convertMatchToReportMatch(comparison, match, result.getOptions().getLanguage().supportsColumns())).toList();
+    }
+
+    private Match convertMatchToReportMatch(JPlagComparison comparison, de.jplag.Match match, boolean languageSupportsColumnsAndLines) {
+        TokenList tokensFirst = comparison.getFirstSubmission().getTokenList();
+        TokenList tokensSecond = comparison.getSecondSubmission().getTokenList();
+        Token startTokenFirst = tokensFirst.getToken(match.startOfFirst());
+        Token endTokenFirst = tokensFirst.getToken(match.startOfFirst() + match.length() - 1);
+        Token startTokenSecond = tokensSecond.getToken(match.startOfSecond());
+        Token endTokenSecond = tokensSecond.getToken(match.startOfSecond() + match.length() - 1);
+
+        int startFirst = getPosition(languageSupportsColumnsAndLines, startTokenFirst);
+        int endFirst = getPosition(languageSupportsColumnsAndLines, endTokenFirst);
+        int startSecond = getPosition(languageSupportsColumnsAndLines, startTokenSecond);
+        int endSecond = getPosition(languageSupportsColumnsAndLines, endTokenSecond);
+        int tokens = match.length();
+
+        return new Match(startTokenFirst.getFile(), startTokenSecond.getFile(), startFirst, endFirst, startSecond, endSecond, tokens);
+    }
+
+    private int getPosition(boolean languageSupportsColumnsAndLines, Token token) {
+        return languageSupportsColumnsAndLines ? token.getLine() : token.getIndex();
+    }
+
+}

+ 32 - 0
jplag/src/main/java/de/jplag/reporting/jsonfactory/DirectoryCreator.java

@@ -0,0 +1,32 @@
+package de.jplag.reporting.jsonfactory;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Provides Methods for creating directories.
+ */
+public class DirectoryCreator {
+
+    /**
+     * Creates a directory.
+     * @param path The path under which the new directory ought to be created
+     * @param name The name of the new directory
+     * @return The created directory
+     */
+    public static File createDirectory(String path, String name) throws IOException {
+        File directory = new File(path.concat("/").concat(name));
+        if (!directory.exists() && !directory.mkdirs()) {
+            throw new IOException("Failed to create dir.");
+        }
+        return directory;
+    }
+
+    /**
+     * Create a directory with the given path
+     * @param path The path of the new directory
+     */
+    public static void createDirectory(String path) throws IOException {
+        createDirectory(path, "");
+    }
+}

+ 87 - 84
jplag/src/main/java/de/jplag/reporting/reportobject/ReportObjectFactory.java

@@ -1,15 +1,15 @@
 package de.jplag.reporting.reportobject;
 
+import static de.jplag.reporting.jsonfactory.DirectoryCreator.createDirectory;
+import static de.jplag.reporting.reportobject.mapper.SubmissionNameToIdMapper.buildSubmissionNameToIdMap;
+
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.StandardCopyOption;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import org.slf4j.Logger;
@@ -19,85 +19,66 @@ import de.jplag.JPlagComparison;
 import de.jplag.JPlagResult;
 import de.jplag.Language;
 import de.jplag.Submission;
+import de.jplag.reporting.jsonfactory.ComparisonReportWriter;
 import de.jplag.reporting.jsonfactory.FileWriter;
 import de.jplag.reporting.reportobject.mapper.ClusteringResultMapper;
-import de.jplag.reporting.reportobject.mapper.ComparisonReportMapper;
 import de.jplag.reporting.reportobject.mapper.MetricMapper;
 import de.jplag.reporting.reportobject.model.Metric;
 import de.jplag.reporting.reportobject.model.OverviewReport;
 
 /**
- * Factory class, responsible for converting a JPlagResult object to Overview and Comparison DTO classes.
+ * Factory class, responsible for converting a JPlagResult object to Overview and Comparison DTO classes and writing it
+ * to the disk.
  */
 public class ReportObjectFactory {
     private static final Logger logger = LoggerFactory.getLogger(ReportObjectFactory.class);
 
-    private static final ClusteringResultMapper clusteringResultMapper = new ClusteringResultMapper();
-    private static final MetricMapper metricMapper = new MetricMapper();
-    private static final ComparisonReportMapper comparisonReportMapper = new ComparisonReportMapper();
     private static final FileWriter fileWriter = new FileWriter();
     public static final String OVERVIEW_FILE_NAME = "overview.json";
     public static final String SUBMISSIONS_FOLDER = "submissions";
+    private Map<String, String> submissionNameToIdMap;
+    private Function<Submission, String> submissionToIdFunction;
+    private Map<String, Map<String, String>> submissionNameToNameToComparisonFileName;
 
     /**
+     * Creates all necessary report viewer files and writes them to the disk.
      * @param result The JPlagResult to be converted into a report.
      * @param path The Path to save the report to
      */
-    public static void createAndSaveReport(JPlagResult result, String path) {
-        createDirectory(path);
-        writeOverview(result, path);
-        copySubmissionFilesToReport(path, result);
-        comparisonReportMapper.writeComparisonReports(result, path);
-    }
-
-    private static File createDirectory(String path, String name) {
-        File directory = new File(path.concat("/").concat(name));
-        if (!directory.exists() && !directory.mkdirs()) {
-            logger.error("Failed to create dir.");
-        }
-        return directory;
-    }
+    public void createAndSaveReport(JPlagResult result, String path) {
 
-    private static void createDirectory(String path) {
-        createDirectory(path, "");
-    }
+        buildSubmissionToIdMap(result);
+        try {
+            createDirectory(path);
+            copySubmissionFilesToReport(path, result);
 
-    private static void writeOverview(JPlagResult result, String path) {
-        List<JPlagComparison> comparisons = getComparisons(result);
-        OverviewReport overviewReport = new OverviewReport();
+            writeComparisons(result, path);
+            writeOverview(result, path);
 
-        // TODO: Consider to treat entries that were checked differently from old entries with prior work.
-        List<String> folders = new ArrayList<>();
-        folders.addAll(result.getOptions().getSubmissionDirectories());
-        folders.addAll(result.getOptions().getOldSubmissionDirectories());
-        overviewReport.setSubmissionFolderPath(folders);
-
-        String baseCodePath = result.getOptions().hasBaseCode() ? result.getOptions().getBaseCodeSubmissionName().orElse("") : "";
-        overviewReport.setBaseCodeFolderPath(baseCodePath);
-
-        overviewReport.setLanguage(result.getOptions().getLanguage().getName());
-        overviewReport.setFileExtensions(List.of(result.getOptions().getFileSuffixes()));
-        overviewReport.setSubmissionIds(extractSubmissionNames(comparisons));
-        overviewReport.setFailedSubmissionNames(List.of());  // No number of failed submissions
-        overviewReport.setExcludedFiles(result.getOptions().getExcludedFiles());
-        overviewReport.setMatchSensitivity(result.getOptions().getMinimumTokenMatch());
-        overviewReport.setDateOfExecution(getDate());
-        overviewReport.setExecutionTime(result.getDuration());
-        overviewReport.setComparisonNames(getComparisonNames(comparisons));
-        overviewReport.setMetrics(getMetrics(result));
-        overviewReport.setClusters(clusteringResultMapper.map(result));
+        } catch (IOException e) {
+            logger.error("Could not create directory " + path + " for report viewer generation", e);
+        }
 
-        fileWriter.saveAsJSON(overviewReport, path, OVERVIEW_FILE_NAME);
+    }
 
+    private void buildSubmissionToIdMap(JPlagResult result) {
+        submissionNameToIdMap = buildSubmissionNameToIdMap(result);
+        submissionToIdFunction = (Submission submission) -> submissionNameToIdMap.get(submission.getName());
     }
 
-    private static void copySubmissionFilesToReport(String path, JPlagResult result) {
+    private void copySubmissionFilesToReport(String path, JPlagResult result) {
         List<JPlagComparison> comparisons = result.getComparisons(result.getOptions().getMaximumNumberOfComparisons());
         var submissions = getSubmissions(comparisons);
-        var submissionsPath = createDirectory(path, SUBMISSIONS_FOLDER);
+        var submissionsPath = createSubmissionsDirectory(path);
+        if (submissionsPath == null) {
+            return;
+        }
         Language language = result.getOptions().getLanguage();
         for (var submission : submissions) {
-            File directory = createDirectory(submissionsPath.getPath(), submission.getName());
+            File directory = createSubmissionDirectory(path, submissionsPath, submission);
+            if (directory == null) {
+                continue;
+            }
             for (var file : submission.getFiles()) {
                 var fileToCopy = language.useViewFiles() ? new File(file.getPath() + language.viewFileSuffix()) : file;
                 try {
@@ -109,40 +90,61 @@ public class ReportObjectFactory {
         }
     }
 
-    private static Set<Submission> getSubmissions(List<JPlagComparison> comparisons) {
-        var submissions = comparisons.stream().map(JPlagComparison::getFirstSubmission).collect(Collectors.toSet());
-        Set<Submission> secondSubmissions = comparisons.stream().map(JPlagComparison::getSecondSubmission).collect(Collectors.toSet());
-        submissions.addAll(secondSubmissions);
-        return submissions;
+    private File createSubmissionDirectory(String path, File submissionsPath, Submission submission) {
+        try {
+            return createDirectory(submissionsPath.getPath(), submissionToIdFunction.apply(submission));
+        } catch (IOException e) {
+            logger.error("Could not create directory " + path + " for report viewer generation", e);
+            return null;
+        }
     }
 
-    private static List<JPlagComparison> getComparisons(JPlagResult result) {
-        int numberOfComparisons = result.getOptions().getMaximumNumberOfComparisons();
-        return result.getComparisons(numberOfComparisons);
+    private File createSubmissionsDirectory(String path) {
+        try {
+            return createDirectory(path, SUBMISSIONS_FOLDER);
+        } catch (IOException e) {
+            logger.error("Could not create directory " + path + " for report viewer generation", e);
+            return null;
+        }
     }
 
-    /**
-     * Gets the names of all submissions.
-     * @return A list containing all submission names.
-     */
-    private static List<String> extractSubmissionNames(List<JPlagComparison> comparisons) {
-        HashSet<String> names = new HashSet<>();
-        comparisons.forEach(comparison -> {
-            names.add(comparison.getFirstSubmission().getName());
-            names.add(comparison.getSecondSubmission().getName());
-        });
-        return new ArrayList<>(names);
+    private void writeComparisons(JPlagResult result, String path) {
+        ComparisonReportWriter comparisonReportWriter = new ComparisonReportWriter(submissionToIdFunction);
+        submissionNameToNameToComparisonFileName = comparisonReportWriter.writeComparisonReports(result, path);
     }
 
-    /**
-     * Gets the names of all comparison.
-     * @return A list containing all comparisons.
-     */
-    private static List<String> getComparisonNames(List<JPlagComparison> comparisons) {
-        List<String> names = new ArrayList<>();
-        comparisons.forEach(
-                comparison -> names.add(String.join("-", comparison.getFirstSubmission().getName(), comparison.getSecondSubmission().getName())));
-        return names;
+    private void writeOverview(JPlagResult result, String path) {
+
+        List<String> folders = new ArrayList<>();
+        folders.addAll(result.getOptions().getSubmissionDirectories());
+        folders.addAll(result.getOptions().getOldSubmissionDirectories());
+
+        String baseCodePath = result.getOptions().hasBaseCode() ? result.getOptions().getBaseCodeSubmissionName().orElse("") : "";
+        ClusteringResultMapper clusteringResultMapper = new ClusteringResultMapper(submissionToIdFunction);
+
+        OverviewReport overviewReport = new OverviewReport(folders, // submissionFolderPath
+                baseCodePath, // baseCodeFolderPath
+                result.getOptions().getLanguage().getName(), // language
+                List.of(result.getOptions().getFileSuffixes()), // fileExtensions
+                submissionNameToIdMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)), // submissionIds
+                submissionNameToNameToComparisonFileName, // result.getOptions().getMinimumTokenMatch(),
+                List.of(), // failedSubmissionNames
+                result.getOptions().getExcludedFiles(), // excludedFiles
+                result.getOptions().getMinimumTokenMatch(), // matchSensitivity
+                getDate(),// dateOfExecution
+                result.getDuration(), // executionTime
+                getMetrics(result),// metrics
+                clusteringResultMapper.map(result)); // clusters
+
+        fileWriter.saveAsJSON(overviewReport, path, OVERVIEW_FILE_NAME);
+
+    }
+
+    private Set<Submission> getSubmissions(List<JPlagComparison> comparisons) {
+        var submissions = comparisons.stream().map(JPlagComparison::getFirstSubmission).collect(Collectors.toSet());
+        Set<Submission> secondSubmissions = comparisons.stream().map(JPlagComparison::getSecondSubmission).collect(Collectors.toSet());
+        submissions.addAll(secondSubmissions);
+        return submissions;
     }
 
     /**
@@ -150,11 +152,12 @@ public class ReportObjectFactory {
      * Metric.
      * @return A list contains Metric DTOs.
      */
-    private static List<Metric> getMetrics(JPlagResult result) {
+    private List<Metric> getMetrics(JPlagResult result) {
+        MetricMapper metricMapper = new MetricMapper(submissionToIdFunction);
         return List.of(metricMapper.getAverageMetric(result), metricMapper.getMaxMetric(result));
     }
 
-    private static String getDate() {
+    private String getDate() {
         SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yy");
         Date date = new Date();
         return dateFormat.format(date);

+ 8 - 1
jplag/src/main/java/de/jplag/reporting/reportobject/mapper/ClusteringResultMapper.java

@@ -2,6 +2,7 @@ package de.jplag.reporting.reportobject.mapper;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.function.Function;
 
 import de.jplag.JPlagResult;
 import de.jplag.Submission;
@@ -12,6 +13,12 @@ import de.jplag.reporting.reportobject.model.Cluster;
  * Extracts and maps the clusters from the JPlagResult to the corresponding JSON DTO
  */
 public class ClusteringResultMapper {
+    private final Function<Submission, String> submissionToIdFunction;
+
+    public ClusteringResultMapper(Function<Submission, String> submissionToIdFunction) {
+        this.submissionToIdFunction = submissionToIdFunction;
+    }
+
     public List<Cluster> map(JPlagResult result) {
         var clusteringResult = result.getClusteringResult();
         return clusteringResult.stream().map(ClusteringResult::getClusters).flatMap(Collection::stream).map(this::convertCluster).toList();
@@ -20,7 +27,7 @@ public class ClusteringResultMapper {
     private Cluster convertCluster(de.jplag.clustering.Cluster<Submission> from) {
         var strength = from.getCommunityStrength();
         var avgSimilarity = from.getAverageSimilarity();
-        var member = from.getMembers().stream().map(Submission::getName).toList();
+        var member = from.getMembers().stream().map(submissionToIdFunction).toList();
         return new Cluster(avgSimilarity, strength, member);
     }
 }

+ 0 - 60
jplag/src/main/java/de/jplag/reporting/reportobject/mapper/ComparisonReportMapper.java

@@ -1,60 +0,0 @@
-package de.jplag.reporting.reportobject.mapper;
-
-import java.util.List;
-
-import de.jplag.*;
-import de.jplag.reporting.jsonfactory.FileWriter;
-import de.jplag.reporting.reportobject.model.ComparisonReport;
-import de.jplag.reporting.reportobject.model.Match;
-
-public class ComparisonReportMapper {
-
-    private static final FileWriter FILE_WRITER = new FileWriter();
-
-    /**
-     * Generates detailed ComparisonReport DTO for each comparison in a JPlagResult.
-     * @param jPlagResult The JPlagResult to generate the comparison reports from. contains information about a comparison
-     * between two submission. The JPlagResult is used to extract the information on matches between two submissions.
-     */
-    public void writeComparisonReports(JPlagResult jPlagResult, String path) {
-        int numberOfComparisons = jPlagResult.getOptions().getMaximumNumberOfComparisons();
-        List<JPlagComparison> comparisons = jPlagResult.getComparisons(numberOfComparisons);
-        writeComparisons(jPlagResult, path, comparisons);
-    }
-
-    private void writeComparisons(JPlagResult jPlagResult, String path, List<JPlagComparison> comparisons) {
-        for (JPlagComparison comparison : comparisons) {
-            var comparisonReport = new ComparisonReport(comparison.getFirstSubmission().getName(), comparison.getSecondSubmission().getName(),
-                    comparison.similarity(), convertMatchesToReportMatches(jPlagResult, comparison));
-            String fileName = comparisonReport.firstSubmissionId().concat("-").concat(comparisonReport.secondSubmissionId()).concat(".json");
-            FILE_WRITER.saveAsJSON(comparisonReport, path, fileName);
-        }
-    }
-
-    private List<Match> convertMatchesToReportMatches(JPlagResult result, JPlagComparison comparison) {
-        return comparison.getMatches().stream()
-                .map(match -> convertMatchToReportMatch(comparison, match, result.getOptions().getLanguage().supportsColumns())).toList();
-    }
-
-    private Match convertMatchToReportMatch(JPlagComparison comparison, de.jplag.Match match, boolean languageSupportsColumnsAndLines) {
-        TokenList tokensFirst = comparison.getFirstSubmission().getTokenList();
-        TokenList tokensSecond = comparison.getSecondSubmission().getTokenList();
-        Token startTokenFirst = tokensFirst.getToken(match.startOfFirst());
-        Token endTokenFirst = tokensFirst.getToken(match.startOfFirst() + match.length() - 1);
-        Token startTokenSecond = tokensSecond.getToken(match.startOfSecond());
-        Token endTokenSecond = tokensSecond.getToken(match.startOfSecond() + match.length() - 1);
-
-        int startFirst = getPosition(languageSupportsColumnsAndLines, startTokenFirst);
-        int endFirst = getPosition(languageSupportsColumnsAndLines, endTokenFirst);
-        int startSecond = getPosition(languageSupportsColumnsAndLines, startTokenSecond);
-        int endSecond = getPosition(languageSupportsColumnsAndLines, endTokenSecond);
-        int tokens = match.length();
-
-        return new Match(startTokenFirst.getFile(), startTokenSecond.getFile(), startFirst, endFirst, startSecond, endSecond, tokens);
-    }
-
-    private int getPosition(boolean languageSupportsColumnsAndLines, Token token) {
-        return languageSupportsColumnsAndLines ? token.getLine() : token.getIndex();
-    }
-
-}

+ 13 - 6
jplag/src/main/java/de/jplag/reporting/reportobject/mapper/MetricMapper.java

@@ -9,6 +9,7 @@ import java.util.stream.Collectors;
 import de.jplag.JPlagComparison;
 import de.jplag.JPlagResult;
 import de.jplag.Messages;
+import de.jplag.Submission;
 import de.jplag.options.SimilarityMetric;
 import de.jplag.reporting.reportobject.model.Metric;
 import de.jplag.reporting.reportobject.model.TopComparison;
@@ -17,6 +18,12 @@ import de.jplag.reporting.reportobject.model.TopComparison;
  * Extracts and maps metrics from the JPlagResult to the corresponding JSON DTO
  */
 public class MetricMapper {
+    private final Function<Submission, String> submissionToIdFunction;
+
+    public MetricMapper(Function<Submission, String> submissionToIdFunction) {
+        this.submissionToIdFunction = submissionToIdFunction;
+    }
+
     public Metric getAverageMetric(JPlagResult result) {
         return new Metric(SimilarityMetric.AVG.name(), intArrayToList(result.getSimilarityDistribution()), getTopComparisons(getComparisons(result)),
                 Messages.getString("SimilarityMetric.Avg.Description"));
@@ -32,22 +39,22 @@ public class MetricMapper {
         return result.getComparisons(maxNumberOfComparisons);
     }
 
-    private static List<Integer> intArrayToList(int[] array) {
+    private List<Integer> intArrayToList(int[] array) {
         return Arrays.stream(array).boxed().collect(Collectors.toList());
     }
 
-    private static List<TopComparison> getTopComparisons(List<JPlagComparison> comparisons, Function<JPlagComparison, Float> similarityExtractor) {
+    private List<TopComparison> getTopComparisons(List<JPlagComparison> comparisons, Function<JPlagComparison, Float> similarityExtractor) {
         return comparisons.stream().sorted(Comparator.comparing(similarityExtractor).reversed())
-                .map(comparison -> new TopComparison(comparison.getFirstSubmission().getName(), comparison.getSecondSubmission().getName(),
-                        similarityExtractor.apply(comparison)))
+                .map(comparison -> new TopComparison(submissionToIdFunction.apply(comparison.getFirstSubmission()),
+                        submissionToIdFunction.apply(comparison.getSecondSubmission()), similarityExtractor.apply(comparison)))
                 .toList();
     }
 
-    private static List<TopComparison> getTopComparisons(List<JPlagComparison> comparisons) {
+    private List<TopComparison> getTopComparisons(List<JPlagComparison> comparisons) {
         return getTopComparisons(comparisons, JPlagComparison::similarity);
     }
 
-    private static List<TopComparison> getMaxSimilarityTopComparisons(List<JPlagComparison> comparisons) {
+    private List<TopComparison> getMaxSimilarityTopComparisons(List<JPlagComparison> comparisons) {
         return getTopComparisons(comparisons, JPlagComparison::maximalSimilarity);
     }
 

+ 43 - 0
jplag/src/main/java/de/jplag/reporting/reportobject/mapper/SubmissionNameToIdMapper.java

@@ -0,0 +1,43 @@
+package de.jplag.reporting.reportobject.mapper;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import de.jplag.JPlagComparison;
+import de.jplag.JPlagResult;
+import de.jplag.Submission;
+
+/**
+ * Responsible for creating a mapping of all Submissions to their respective report viewer ids. This mapping is achieved
+ * by associating the {@link Submission#getName()} (JPlag's internal Submission uid) to a report viewer id. Currently,
+ * the sanitized version of the {@link Submission#getName()} serves as report viewer id.
+ */
+public class SubmissionNameToIdMapper {
+
+    private static final String FILE_SEPARATOR_REPLACEMENT = "_";
+
+    /**
+     * Builds a map that associates a Submission by its JPlag Id ({@link Submission#getName()}) to its report viewer id.
+     * @return A Map of containing an entry [name of submission -> report viewer id of submission] for each submission of
+     * the submission set.
+     */
+    public static Map<String, String> buildSubmissionNameToIdMap(JPlagResult result) {
+        HashMap<String, String> idToName = new HashMap<>();
+        getComparisons(result).forEach(comparison -> {
+            idToName.put(comparison.getFirstSubmission().getName(), sanitizeNameOf(comparison.getFirstSubmission()));
+            idToName.put(comparison.getSecondSubmission().getName(), sanitizeNameOf(comparison.getSecondSubmission()));
+        });
+        return idToName;
+    }
+
+    private static String sanitizeNameOf(Submission comparison) {
+        return comparison.getName().replace(File.separator, FILE_SEPARATOR_REPLACEMENT);
+    }
+
+    private static List<JPlagComparison> getComparisons(JPlagResult result) {
+        int numberOfComparisons = result.getOptions().getMaximumNumberOfComparisons();
+        return result.getComparisons(numberOfComparisons);
+    }
+}

+ 15 - 154
jplag/src/main/java/de/jplag/reporting/reportobject/model/OverviewReport.java

@@ -1,174 +1,35 @@
 package de.jplag.reporting.reportobject.model;
 
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 
-public class OverviewReport {
+public record OverviewReport(
 
-    @JsonProperty("submission_folder_path")
-    private List<String> submissionFolderPath;
+        @JsonProperty("submission_folder_path") List<String> submissionFolderPath,
 
-    @JsonProperty("base_code_folder_path")
-    private String baseCodeFolderPath;
+        @JsonProperty("base_code_folder_path") String baseCodeFolderPath,
 
-    @JsonProperty("language")
-    private String language;
+        @JsonProperty("language") String language,
 
-    @JsonProperty("file_extensions")
-    private List<String> fileExtensions;
+        @JsonProperty("file_extensions") List<String> fileExtensions,
 
-    @JsonProperty("submission_ids")
-    private List<String> submissionIds;
+        @JsonProperty("submission_id_to_display_name") Map<String, String> submissionIds,
+        @JsonProperty("submission_ids_to_comparison_file_name") Map<String, Map<String, String>> submissionIdsToComparisonFileName,
 
-    @JsonProperty("failed_submission_names")
-    private List<String> failedSubmissionNames;
+        @JsonProperty("failed_submission_names") List<String> failedSubmissionNames,
 
-    @JsonProperty("excluded_files")
-    private Set<String> excludedFiles;
+        @JsonProperty("excluded_files") Set<String> excludedFiles,
 
-    @JsonProperty("match_sensitivity")
-    private int matchSensitivity;
+        @JsonProperty("match_sensitivity") int matchSensitivity,
 
-    @JsonProperty("date_of_execution")
-    private String dateOfExecution;
+        @JsonProperty("date_of_execution") String dateOfExecution,
 
-    @JsonProperty("execution_time")
-    private long executionTime;
+        @JsonProperty("execution_time") long executionTime,
 
-    @JsonProperty("comparison_names")
-    private List<String> comparisonNames;
+        @JsonProperty("metrics") List<Metric> metrics,
 
-    @JsonProperty("metrics")
-    private List<Metric> metrics;
-
-    @JsonProperty("clusters")
-    private List<Cluster> clusters;
-
-    public OverviewReport() {
-        submissionFolderPath = List.of();
-        baseCodeFolderPath = "";
-        language = "";
-        fileExtensions = List.of();
-        submissionIds = List.of();
-        failedSubmissionNames = List.of();
-        excludedFiles = Set.of();
-        matchSensitivity = 0;
-        dateOfExecution = "";
-        executionTime = 0;
-        clusters = List.of();
-    }
-
-    /*******************************
-     * GETTERS
-     *******************************/
-
-    public List<String> getSubmissionFolderPath() {
-        return submissionFolderPath;
-    }
-
-    public String getBaseCodeFolderPath() {
-        return baseCodeFolderPath;
-    }
-
-    public String getLanguage() {
-        return language;
-    }
-
-    public List<String> getFileExtensions() {
-        return fileExtensions;
-    }
-
-    public List<String> getSubmissionIds() {
-        return submissionIds;
-    }
-
-    public List<String> getFailedSubmissionNames() {
-        return failedSubmissionNames;
-    }
-
-    public Set<String> getExcludedFiles() {
-        return excludedFiles;
-    }
-
-    public int getMatchSensitivity() {
-        return matchSensitivity;
-    }
-
-    public String getDateOfExecution() {
-        return dateOfExecution;
-    }
-
-    public long getExecutionTime() {
-        return executionTime;
-    }
-
-    public List<String> getComparisonNames() {
-        return comparisonNames;
-    }
-
-    public List<Metric> getMetrics() {
-        return metrics;
-    }
-
-    public List<Cluster> getClusters() {
-        return clusters;
-    }
-
-    /*******************************
-     * SETTERS
-     *******************************/
-
-    public void setSubmissionFolderPath(List<String> submissionFolderPath) {
-        this.submissionFolderPath = submissionFolderPath;
-    }
-
-    public void setBaseCodeFolderPath(String baseCodeFolderPath) {
-        this.baseCodeFolderPath = baseCodeFolderPath;
-    }
-
-    public void setLanguage(String language) {
-        this.language = language;
-    }
-
-    public void setFileExtensions(List<String> fileExtensions) {
-        this.fileExtensions = List.copyOf(fileExtensions);
-    }
-
-    public void setSubmissionIds(List<String> submissionIds) {
-        this.submissionIds = List.copyOf(submissionIds);
-    }
-
-    public void setFailedSubmissionNames(List<String> failedSubmissionNames) {
-        this.failedSubmissionNames = List.copyOf(failedSubmissionNames);
-    }
-
-    public void setExcludedFiles(Set<String> excludedFiles) {
-        this.excludedFiles = Set.copyOf(excludedFiles);
-    }
-
-    public void setMatchSensitivity(int matchSensitivity) {
-        this.matchSensitivity = matchSensitivity;
-    }
-
-    public void setDateOfExecution(String dateOfExecution) {
-        this.dateOfExecution = dateOfExecution;
-    }
-
-    public void setExecutionTime(long executionTime) {
-        this.executionTime = executionTime;
-    }
-
-    public void setComparisonNames(List<String> comparisonNames) {
-        this.comparisonNames = List.copyOf(comparisonNames);
-    }
-
-    public void setMetrics(List<Metric> metrics) {
-        this.metrics = List.copyOf(metrics);
-    }
-
-    public void setClusters(List<Cluster> clusters) {
-        this.clusters = List.copyOf(clusters);
-    }
+        @JsonProperty("clusters") List<Cluster> clusters) {
 }

+ 1 - 1
jplag/src/test/java/de/jplag/reporting/reportobject/mapper/ClusteringResultMapperTest.java

@@ -15,7 +15,7 @@ import de.jplag.clustering.Cluster;
 import de.jplag.clustering.ClusteringResult;
 
 public class ClusteringResultMapperTest {
-    private final ClusteringResultMapper clusteringResultMapper = new ClusteringResultMapper();
+    private final ClusteringResultMapper clusteringResultMapper = new ClusteringResultMapper(Submission::getName);
 
     @Test
     public void test() {

+ 55 - 0
jplag/src/test/java/de/jplag/reporting/reportobject/mapper/ComparisonReportWriterTest.java

@@ -0,0 +1,55 @@
+package de.jplag.reporting.reportobject.mapper;
+
+import java.util.Map;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import de.jplag.JPlagResult;
+import de.jplag.Submission;
+import de.jplag.TestBase;
+import de.jplag.exceptions.ExitException;
+import de.jplag.reporting.jsonfactory.ComparisonReportWriter;
+
+public class ComparisonReportWriterTest extends TestBase {
+
+    @Test
+    public void firsLevelOfLookupMapComplete() throws ExitException {
+        JPlagResult result = runJPlagWithDefaultOptions("PartialPlagiarism");
+        var mapper = new ComparisonReportWriter(Submission::getName);
+
+        Map<String, Map<String, String>> stringMapMap = mapper.writeComparisonReports(result, "");
+
+        firstLevelOfMapContains(stringMapMap, "A", "B", "C", "D", "E");
+    }
+
+    @Test
+    public void secondLevelOfLookupMapComplete() throws ExitException {
+        JPlagResult result = runJPlagWithDefaultOptions("PartialPlagiarism");
+        var mapper = new ComparisonReportWriter(Submission::getName);
+
+        Map<String, Map<String, String>> stringMapMap = mapper.writeComparisonReports(result, "");
+
+        secondLevelOfMapContains(stringMapMap, "A", "B", "C", "D", "E");
+        secondLevelOfMapContains(stringMapMap, "B", "A", "C", "D", "E");
+        secondLevelOfMapContains(stringMapMap, "C", "B", "A", "D", "E");
+        secondLevelOfMapContains(stringMapMap, "D", "B", "C", "A", "E");
+        secondLevelOfMapContains(stringMapMap, "E", "B", "C", "D", "A");
+    }
+
+    private void secondLevelOfMapContains(Map<String, Map<String, String>> stringMapMap, String firstLevelSubmission,
+            String... secondLevelSubmissions) {
+        for (String secondLevelSubmission : secondLevelSubmissions) {
+            Assertions.assertNotNull(stringMapMap.get(firstLevelSubmission).get(secondLevelSubmission));
+            Assertions.assertFalse(stringMapMap.get(firstLevelSubmission).get(secondLevelSubmission).isEmpty());
+
+        }
+
+    }
+
+    private void firstLevelOfMapContains(Map<String, Map<String, String>> stringMapMap, String... names) {
+        for (String name : names) {
+            Assertions.assertNotNull(stringMapMap.get(name));
+        }
+    }
+}

+ 1 - 1
jplag/src/test/java/de/jplag/reporting/reportobject/mapper/MetricMapperTest.java

@@ -15,7 +15,7 @@ import de.jplag.options.JPlagOptions;
 import de.jplag.reporting.reportobject.model.TopComparison;
 
 public class MetricMapperTest {
-    private final MetricMapper metricMapper = new MetricMapper();
+    private final MetricMapper metricMapper = new MetricMapper(Submission::getName);
 
     @Test
     public void test_getAverageMetric() {

+ 13 - 32
report-viewer/src/components/ComparisonsTable.vue

@@ -18,59 +18,37 @@
         comparison.matchPercentage
       "
       class="selectable"
+      @click="
+        navigateToComparisonView(
+          comparison.firstSubmissionId,
+          comparison.secondSubmissionId
+        )
+      "
     >
-      <td
-        @click="
-          navigateToComparisonView(
-            comparison.firstSubmissionId,
-            comparison.secondSubmissionId
-          )
-        "
-      >
-        {{ index + 1 }}.
-      </td>
+      <td>{{ index + 1 }}.</td>
       <td
         :class="{
           'anonymous-style': isAnonymous(comparison.firstSubmissionId),
         }"
-        @click="
-          navigateToComparisonView(
-            comparison.firstSubmissionId,
-            comparison.secondSubmissionId
-          )
-        "
       >
         {{
           isAnonymous(comparison.firstSubmissionId)
             ? "Hidden"
-            : comparison.firstSubmissionId
+            : displayName(comparison.firstSubmissionId)
         }}
       </td>
-      <td
-        @click="
-          navigateToComparisonView(
-            comparison.firstSubmissionId,
-            comparison.secondSubmissionId
-          )
-        "
-      >
+      <td>
         <img alt=">>" src="@/assets/double_arrow_black_18dp.svg" />
       </td>
       <td
         :class="{
           'anonymous-style': isAnonymous(comparison.secondSubmissionId),
         }"
-        @click="
-          navigateToComparisonView(
-            comparison.firstSubmissionId,
-            comparison.secondSubmissionId
-          )
-        "
       >
         {{
           isAnonymous(comparison.secondSubmissionId)
             ? "Hidden"
-            : comparison.secondSubmissionId
+            : displayName(comparison.secondSubmissionId)
         }}
       </td>
       <td>{{ formattedMatchPercentage(comparison.matchPercentage) }}</td>
@@ -138,6 +116,8 @@ export default defineComponent({
     let formattedMatchPercentage = (num: number) => num.toFixed(2);
     const dialog: Ref<Array<boolean>> = ref([]);
     props.topComparisons.forEach(() => dialog.value.push(false));
+    const displayName = (submissionId: string) =>
+      store.getters.submissionDisplayName(submissionId);
 
     const toggleDialog = (index: number) => {
       dialog.value[index] = true;
@@ -223,6 +203,7 @@ export default defineComponent({
       clustersWithParticipatingMatches,
       dialog,
 
+      displayName,
       isAnonymous,
       getClustersFor,
       toggleDialog,

+ 12 - 11
report-viewer/src/components/IDsList.vue

@@ -6,32 +6,33 @@
     <div class="column-list">
       <p v-for="(id, index) in ids" :key="index"
          :class="{'selected' : !store.state.anonymous.has(id) && store.state.anonymous.size !== 0 }"
-         @click="$emit('idSent', [id])">
-        {{ id }}
+         @click="emitIdsSent([id])">
+        {{ store.getters.submissionDisplayName(id) }}
       </p>
     </div>
-    <button @click="$emit('idSent', ids)">Hide/Show all</button>
+    <button @click="emitIdsSent(ids)">Hide/Show all</button>
   </div>
 </template>
 
-<script>
+<script lang="ts">
 import {defineComponent} from "vue";
-import store from "@/store/store.ts"
+import store from "@/store/store"
 
 export default defineComponent({
+  emits: ["idSent"],
   name: "IDsList",
   props: {
     ids: {
-      type: Array,
+      type: Array<string>,
       required: true
     }
   },
-  setup(props) {
-    // eslint-disable-next-line vue/no-setup-props-destructure
-    const savedIds = props.ids
+  setup(_, {emit}) {
+    const emitIdsSent = (ids: string[]) => {
+      emit('idSent', ids);}
     return {
-      savedIds,
-      store
+      store,
+      emitIdsSent
     }
   }
 })

+ 7 - 7
report-viewer/src/model/Overview.ts

@@ -7,7 +7,7 @@ export class Overview {
   private readonly _language: string;
   private readonly _fileExtensions: Array<string>;
   private readonly _matchSensitivity: number;
-  private readonly _submissionIds: Array<string>;
+  private readonly _submissionIdsToComparisonFileName: Map<string,Map<string,string>>;
   private readonly _dateOfExecution: string;
   private readonly _durationOfExecution: number;
   private readonly _metrics: Array<Metric>;
@@ -19,22 +19,25 @@ export class Overview {
     language: string,
     fileExtensions: Array<string>,
     matchSensitivity: number,
-    submissionIds: Array<string>,
     dateOfExecution: string,
     durationOfExecution: number,
     metrics: Array<Metric>,
-    clusters: Array<Cluster>
+    clusters: Array<Cluster>,
+    submissionIdsToComparisonFileName: Map<string,Map<string,string>>
   ) {
     this._submissionFolderPath = submissionFolderPath;
     this._baseCodeFolderPath = baseCodeFolderPath;
     this._language = language;
     this._fileExtensions = fileExtensions;
     this._matchSensitivity = matchSensitivity;
-    this._submissionIds = submissionIds;
     this._dateOfExecution = dateOfExecution;
     this._durationOfExecution = durationOfExecution;
     this._metrics = metrics;
     this._clusters = clusters;
+    this. _submissionIdsToComparisonFileName = submissionIdsToComparisonFileName;
+  }
+  get submissionIdsToComparisonFileName(): Map<string,Map<string,string>> {
+    return this.submissionIdsToComparisonFileName;
   }
 
   get submissionFolderPath(): Array<string> {
@@ -57,9 +60,6 @@ export class Overview {
     return this._matchSensitivity;
   }
 
-  get submissionIds(): Array<string> {
-    return this._submissionIds;
-  }
 
   get dateOfExecution(): string {
     return this._dateOfExecution;

+ 27 - 5
report-viewer/src/model/factories/OverviewFactory.ts

@@ -2,15 +2,18 @@ import { Overview } from "../Overview";
 import { Metric } from "../Metric";
 import { ComparisonListElement } from "../ComparisonListElement";
 import { Cluster } from "@/model/Cluster";
+import store from "@/store/store";
 
 export class OverviewFactory {
+
   static getOverview(json: Record<string, unknown>): Overview {
     const submissionFolder = json.submission_folder_path as Array<string>;
     const baseCodeFolder = "";
     const language = json.language as string;
     const fileExtensions = json.file_extensions as Array<string>;
     const matchSensitivity = json.match_sensitivity as number;
-    const submissions = json.submission_ids as Array<string>;
+    const jsonSubmissions = json.submission_id_to_display_name as Map<string, string>;
+    const map = new Map<string, string>(Object.entries(jsonSubmissions));
     const dateOfExecution = json.date_of_execution as string;
     const duration = json.execution_time as number as number;
     const metrics = [] as Array<Metric>;
@@ -37,7 +40,7 @@ export class OverviewFactory {
         description: metric.description as string,
       });
     });
-
+    store.commit("saveSubmissionNames", map);
     if (json.clusters) {
       (json.clusters as Array<unknown>).forEach((jsonCluster) => {
         const cluster = jsonCluster as Record<string, unknown>;
@@ -49,18 +52,37 @@ export class OverviewFactory {
         clusters.push(newCluster);
       });
     }
-
+    
+    OverviewFactory.saveSubmissionsToComparisonNameMap(json);
     return new Overview(
       submissionFolder,
       baseCodeFolder,
       language,
       fileExtensions,
       matchSensitivity,
-      submissions,
       dateOfExecution,
       duration,
       metrics,
-      clusters
+      clusters,
+      new Map()
     );
   }
+
+  private static  saveSubmissionsToComparisonNameMap(json: Record<string, unknown>){
+    const submissionIdsToComparisonName =
+    json.submission_ids_to_comparison_file_name as Map<
+      string,
+      Map<string, string>
+    >;
+  const test: Array<Array<string | object>> = Object.entries(
+    submissionIdsToComparisonName
+  );
+  const comparisonMap = new Map<string, Map<string, string>>(
+  );
+  for (const [key, value] of test) {
+    comparisonMap.set(key as string, new Map(Object.entries(value as object)));
+  }
+
+  store.commit("saveComparisonFileLookup", comparisonMap);
+  }
 }

+ 36 - 0
report-viewer/src/store/store.ts

@@ -30,6 +30,9 @@ interface State {
    * Files string if single mode is used.
    */
   fileString: string;
+
+  fileIdToDisplayName: Map<string, string>;
+  submissionIdsToComparisonFileName: Map<string, Map<string, string>>;
 }
 
 interface File {
@@ -50,6 +53,7 @@ interface LoadConfiguration {
 
 const store = createStore<State>({
   state: {
+    submissionIdsToComparisonFileName: new Map<string, Map<string, string>>(),
     /**
      * The set of ids to be hidden.
      */
@@ -75,6 +79,7 @@ const store = createStore<State>({
      * Files string if single mode is used.
      */
     fileString: "",
+    fileIdToDisplayName: new Map(),
   },
   getters: {
     filesOfSubmission: (state) => (name: string) => {
@@ -83,6 +88,28 @@ const store = createStore<State>({
         value,
       }));
     },
+    submissionDisplayName: (state) => (id: string) => {
+      return state.fileIdToDisplayName.get(id);
+    },
+    getSubmissionIds(state): Array<string> {
+      return Array.from(state.fileIdToDisplayName.keys());
+    },
+    getComparisonFileName:
+      (state) => (submissionId: string, fileId: string) => {
+        return state.submissionIdsToComparisonFileName
+          .get(submissionId)
+          ?.get(fileId);
+      },
+      getComparisonFileForSubmissions: (state, getters) => (submissionId1: string, submissionId2: string) => {
+        const expectedFileName = getters.getComparisonFileName(submissionId1, submissionId2);
+        const index = Object.keys(store.state.files).find(
+          (name) =>
+             name.endsWith(expectedFileName)
+        );
+        return index != undefined
+          ? store.state.files[index]
+          : undefined
+      }
   },
   mutations: {
     addAnonymous(state: State, id) {
@@ -90,6 +117,12 @@ const store = createStore<State>({
         state.anonymous.add(id[i]);
       }
     },
+    saveComparisonFileLookup(
+      state: State,
+      map: Map<string, Map<string, string>>
+    ) {
+      state.submissionIdsToComparisonFileName = map;
+    },
     removeAnonymous(state, id) {
       for (let i = 0; i < id.length; i++) {
         state.anonymous.delete(id[i]);
@@ -101,6 +134,9 @@ const store = createStore<State>({
     saveFile(state, file: File) {
       state.files[file.fileName] = file.data;
     },
+    saveSubmissionNames(state, names: Map<string, string>) {
+      state.fileIdToDisplayName = names;
+    },
     saveSubmissionFile(state, submissionFile: SubmissionFile) {
       if (!state.submissions[submissionFile.name]) {
         state.submissions[submissionFile.name] = new Map();

+ 14 - 32
report-viewer/src/views/ComparisonView.vue

@@ -23,12 +23,12 @@
       </div>
       <TextInformation
         :anonymous="isAnonymous(firstId)"
-        :value="firstId"
+        :value="store.getters.submissionDisplayName(firstId)"
         label="Submission 1"
       />
       <TextInformation
         :anonymous="store.state.anonymous.has(secondId)"
-        :value="secondId"
+        :value="store.getters.submissionDisplayName(secondId)"
         label="Submission 2"
       />
       <TextInformation :value="comparison.match_percentage" label="Match %" />
@@ -85,12 +85,6 @@ export default defineComponent({
   setup(props) {
     const store = useStore();
     const router = useRouter();
-    /**
-     * Name of the comparison file. Comparison files should be named {ID1}-{ID2}
-     * @type {string}
-     */
-    const fileName1 = props.firstId.concat("-").concat(props.secondId);
-    const fileName2 = props.firstId.concat("-").concat(props.secondId);
 
     let comparison;
     //getting the comparison file based on the used mode (zip, local, single)
@@ -98,42 +92,31 @@ export default defineComponent({
       try {
         comparison = ComparisonFactory.getComparison(
           // eslint-disable-next-line @typescript-eslint/no-var-requires
-          require(`../files/${fileName1}.json`)
+          require(`../files/${store.getters.getComparisonFileName(
+            props.firstId,
+            props.secondId
+          )}.json`)
         );
       } catch (exception) {
-        try {
-          comparison = ComparisonFactory.getComparison(
-            // eslint-disable-next-line @typescript-eslint/no-var-requires
-            require(`../files/${fileName2}.json`)
-          );
-        } catch (exception) {
-          router.back();
-        }
+        router.back();
       }
     } else if (store.state.zip) {
-      const getComparisonFileFor = (id1: string, id2: string) => {
-        const index = Object.keys(store.state.files).find(
-          (name) =>
-            name.endsWith(id1.concat("-").concat(id2).concat(".json")) ||
-            name.endsWith(id2.concat("-").concat(id1).concat(".json"))
-        );
-        return index != undefined
-          ? store.state.files[index]
-          : console.log("Could not find comparison file."); // TODO introduce error page to navigate to
-      };
-
-      let comparisonFile = getComparisonFileFor(props.firstId, props.secondId);
+      let comparisonFile = store.getters.getComparisonFileForSubmissions(
+        props.firstId,
+        props.secondId
+      );
       if (comparisonFile) {
         comparison = ComparisonFactory.getComparison(
           JSON.parse(comparisonFile)
         );
+      } else {
+        console.log("Could not find comparison file."); // TODO introduce error page to navigate to
       }
     } else if (store.state.single) {
       comparison = ComparisonFactory.getComparison(
         JSON.parse(store.state.fileString)
       );
     }
-
     if (!comparison) {
       console.warn("Could not build comparison file");
       return;
@@ -219,6 +202,7 @@ export default defineComponent({
       filesOfFirst,
       filesOfSecond,
       hideLeftPanel,
+      store,
 
       toggleCollapseFirst,
       toggleCollapseSecond,
@@ -227,8 +211,6 @@ export default defineComponent({
       showMatch,
       togglePanel,
       isAnonymous,
-
-      store,
     };
   },
 });

+ 27 - 27
report-viewer/src/views/OverviewView.vue

@@ -4,7 +4,7 @@
 <template>
   <div class="container">
     <div class="column-container" style="width: 30%">
-        <h1>JPlag Report</h1>
+      <h1>JPlag Report</h1>
       <p class="section-title">Main Info:</p>
       <div id="basicInfo">
         <TextInformation
@@ -35,11 +35,11 @@
         />
         <TextInformation
           :has-additional-info="true"
-          :value="overview.submissionIds.length"
+          :value="store.getters.getSubmissionIds.size"
           additional-info-title="Submission IDs:"
           label="Submissions"
         >
-          <IDsList :ids="overview.submissionIds" @id-sent="handleId" />
+          <IDsList :ids="store.getters.getSubmissionIds" @id-sent="handleId" />
         </TextInformation>
         <TextInformation
           :value="overview.dateOfExecution"
@@ -56,19 +56,19 @@
     </div>
 
     <div class="column-container" style="width: 35%">
-        <div id="metrics">
+      <div id="metrics">
         <p class="section-title">Metric:</p>
-          <div id="metrics-list">
-            <MetricButton
-              v-for="(metric, index) in overview.metrics"
-              :id="metric.metricName"
-              :key="metric.metricName"
-              :is-selected="selectedMetric[index]"
-              :metric="metric"
-              @click="selectMetric(index)"
-            />
-          </div>
+        <div id="metrics-list">
+          <MetricButton
+            v-for="(metric, index) in overview.metrics"
+            :id="metric.metricName"
+            :key="metric.metricName"
+            :is-selected="selectedMetric[index]"
+            :metric="metric"
+            @click="selectMetric(index)"
+          />
         </div>
+      </div>
       <p class="section-title">Distribution:</p>
       <DistributionDiagram
         :distribution="distributions[selectedMetricIndex]"
@@ -78,13 +78,13 @@
     <div class="column-container" style="width: 35%">
       <p class="section-title">Top Comparisons:</p>
       <div id="comparisonsList">
-      <ComparisonsTable
-        :clusters="overview.clusters"
-        :top-comparisons="topComps[selectedMetricIndex]"
-      />
+        <ComparisonsTable
+          :clusters="overview.clusters"
+          :top-comparisons="topComps[selectedMetricIndex]"
+        />
+      </div>
     </div>
   </div>
-  </div>
 </template>
 
 <script lang="ts">
@@ -145,26 +145,26 @@ export default defineComponent({
      * Handles the selection of an Id to anonymize.
      * If all submission ids are provided as parameter it hides or displays them based on their previous state.
      * If a single id is provided it hides all of the other ids except for the chosen one.
-     * @param id
+     * @param ids
      */
-    const handleId = (id: string) => {
-      if (id.length === overview.submissionIds.length) {
+    const handleId = (ids: Array<string>) => {
+      if (ids.length === store.getters.getSubmissionIds.length) {
         if (store.state.anonymous.size > 0) {
           store.commit("resetAnonymous");
         } else {
-          store.commit("addAnonymous", id);
+          store.commit("addAnonymous", ids);
         }
       } else {
-        if (store.state.anonymous.has(id[0])) {
-          store.commit("removeAnonymous", id);
+        if (store.state.anonymous.has(ids[0])) {
+          store.commit("removeAnonymous", ids);
         } else {
           if (store.state.anonymous.size === 0) {
             store.commit(
               "addAnonymous",
-              overview.submissionIds.filter((s) => s !== id[0])
+              store.getters.getSubmissionIds.filter((s: string) => s !== ids[0])
             );
           } else {
-            store.commit("addAnonymous", id);
+            store.commit("addAnonymous", ids);
           }
         }
       }