Просмотр исходного кода

Use Original SubmissionFiles in Report DTOs

Nesta Bentum 4 лет назад
Родитель
Сommit
bbd74ab3bf

+ 9 - 3
jplag/src/main/java/de/jplag/reporting/jsonfactory/JsonWriter.java → jplag/src/main/java/de/jplag/reporting/jsonfactory/FileWriter.java

@@ -8,12 +8,18 @@ import org.slf4j.LoggerFactory;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 
-public class JsonWriter {
-    private static final Logger logger = LoggerFactory.getLogger(JsonWriter.class);
+public class FileWriter {
+    private static final Logger logger = LoggerFactory.getLogger(FileWriter.class);
 
     private static final ObjectMapper objectMapper = new ObjectMapper();
 
-    public void saveFile(Object fileToSave, String folderPath, String fileName) {
+    /**
+     * Saves the provided object to the provided path under the provided name
+     * @param fileToSave The object to save
+     * @param folderPath The path to save the object to
+     * @param fileName The name to save the object under
+     */
+    public void saveAsJSON(Object fileToSave, String folderPath, String fileName) {
         try {
             objectMapper.writeValue(Path.of(folderPath, fileName).toFile(), fileToSave);
         } catch (IOException e) {

+ 52 - 14
jplag/src/main/java/de/jplag/reporting/reportobject/ReportObjectFactory.java

@@ -1,14 +1,19 @@
 package de.jplag.reporting.reportobject;
 
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import de.jplag.JPlagComparison;
 import de.jplag.JPlagResult;
-import de.jplag.reporting.jsonfactory.JsonWriter;
+import de.jplag.Submission;
+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;
@@ -19,26 +24,25 @@ import de.jplag.reporting.reportobject.model.OverviewReport;
  * Factory class, responsible for converting a JPlagResult object to Overview and Comparison DTO classes.
  */
 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 JsonWriter jsonWriter = new JsonWriter();
+    private static final FileWriter fileWriter = new FileWriter();
 
     /**
-     * Converts a JPlagResult to a JPlagReport.
      * @return JPlagReport for the given JPlagResult.
+     * @param result The JPlagResult to be converted into a report.
+     * @param path The Path to save the report to
      */
     public static void saveReport(JPlagResult result, String path) {
-        OverviewReport overviewReport = generateOverviewReport(result);
-        jsonWriter.saveFile(overviewReport, path, "overview.json");
+        writeOverview(result, path);
+        copySubmissionFilesToReport(path, result);
         comparisonReportMapper.writeComparisonReports(result, path);
     }
 
-    /**
-     * Generates an Overview DTO of a JPlagResult.
-     */
-    private static OverviewReport generateOverviewReport(JPlagResult result) {
+    private static void writeOverview(JPlagResult result, String path) {
         List<JPlagComparison> comparisons = getComparisons(result);
         OverviewReport overviewReport = new OverviewReport();
 
@@ -63,7 +67,41 @@ public class ReportObjectFactory {
         overviewReport.setMetrics(getMetrics(result));
         overviewReport.setClusters(clusteringResultMapper.map(result));
 
-        return overviewReport;
+        fileWriter.saveAsJSON(overviewReport, path, "overview.json");
+
+    }
+
+    private static void copySubmissionFilesToReport(String path, JPlagResult result) {
+        List<JPlagComparison> comparisons = result.getComparisons(result.getOptions().getMaximumNumberOfComparisons());
+        var submissions = getSubmissions(comparisons);
+        var submissionsPath = createDirectory(path, "Submissions");
+        for (var submission : submissions) {
+            File directory = createDirectory(submissionsPath.getPath(), submission.getName());
+            for (var file : submission.getFiles()) {
+                try {
+                    Files.copy(file.toPath(), (new File(directory, file.getName())).toPath());
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+    }
+
+    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 static File createDirectory(String path, String name) {
+        File directory = new File(path.concat("/").concat(name));
+        if (!directory.exists()) {
+            if (!directory.mkdirs()) {
+                logger.error("Failed to create dir.");
+            }
+        }
+        return directory;
     }
 
     private static List<JPlagComparison> getComparisons(JPlagResult result) {

+ 11 - 47
jplag/src/main/java/de/jplag/reporting/reportobject/mapper/ComparisonReportMapper.java

@@ -1,63 +1,34 @@
 package de.jplag.reporting.reportobject.mapper;
 
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import de.jplag.JPlagComparison;
-import de.jplag.JPlagResult;
-import de.jplag.Token;
-import de.jplag.TokenList;
-import de.jplag.reporting.jsonfactory.JsonWriter;
+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 Logger logger = LoggerFactory.getLogger(ComparisonReportMapper.class);
-    private static final JsonWriter jsonWriter = new JsonWriter();
+    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 - including their files. These files are not saved in
-     * plain text though, they are saved as numbers. these numbers are indices to the second DTO of the
-     * ComparisonReportMapperResult, the lineLookUpTable.
+     * @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 maxNumOfComparisons = jPlagResult.getOptions().getMaximumNumberOfComparisons();
-
-        List<JPlagComparison> comparisons = jPlagResult.getComparisons(maxNumOfComparisons);
-
-        var allSubmissions = comparisons.stream().map(JPlagComparison::getFirstSubmission).collect(Collectors.toSet());
-        for (var submission : allSubmissions) {
-
-            File directory = new File(path.concat("/").concat(submission.getName()));
-            if (!directory.exists()) {
-                if (!directory.mkdirs()) {
-                    logger.error("Failed to create dir.");
-                }
-            }
-            for (var file : submission.getFiles()) {
-
-                try {
-                    Files.copy(file.toPath(), (new File(directory, file.getName())).toPath());
-                } catch (IOException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-        }
+        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");
-            jsonWriter.saveFile(comparisonReport, path, fileName);
+            FILE_WRITER.saveAsJSON(comparisonReport, path, fileName);
         }
     }
 
@@ -67,13 +38,6 @@ public class ComparisonReportMapper {
                 .collect(Collectors.toList());
     }
 
-    /**
-     * Converts a JPlag Match object to a Match DTO.
-     * @param comparison The comparison from which the match originates.
-     * @param match The match to be converted.
-     * @param usesIndex Indicates whether the language uses indexes.
-     * @return A Match DTO.
-     */
     private Match convertMatchToReportMatch(JPlagComparison comparison, de.jplag.Match match, Boolean usesIndex) {
         TokenList tokensFirst = comparison.getFirstSubmission().getTokenList();
         TokenList tokensSecond = comparison.getSecondSubmission().getTokenList();

+ 0 - 12
jplag/src/main/java/de/jplag/reporting/reportobject/model/FilesOfSubmission.java

@@ -1,12 +0,0 @@
-package de.jplag.reporting.reportobject.model;
-
-import java.util.List;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-public record FilesOfSubmission(@JsonProperty("file_name") String fileName, @JsonProperty("lines") List<Long> lines) {
-    public FilesOfSubmission(String fileName, List<Long> lines) {
-        this.fileName = fileName;
-        this.lines = List.copyOf(lines);
-    }
-}

+ 6 - 0
package-lock.json

@@ -0,0 +1,6 @@
+{
+  "name": "JPlag",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {}
+}

+ 2 - 1
report-viewer/package.json

@@ -14,7 +14,8 @@
     "core-js": "^3.24.1",
     "gitart-vue-dialog": "^2.4.0",
     "highlight.js": "^11.6.0",
-    "jszip": "^3.10.1",
+    "jszip": "^3.10.0",
+    "node-polyfill-webpack-plugin": "^2.0.0",
     "vue": "^3.2.37",
     "vue-chart-3": "^3.1.8",
     "vue-draggable-next": "^2.1.1",

+ 19 - 11
report-viewer/src/model/factories/ComparisonFactory.ts

@@ -2,14 +2,22 @@ import { Comparison } from "../Comparison";
 import { Match } from "../Match";
 import { SubmissionFile } from "../SubmissionFile";
 import { MatchInSingleFile } from "../MatchInSingleFile";
+import store from "@/store/store";
 
 export class ComparisonFactory {
   static getComparison(json: Record<string, unknown>): Comparison {
-    const filesOfFirst = this.convertToFilesByName(
-      json.files_of_first_submission as Array<Record<string, unknown>>
+    const filesOfFirstSubmission = store.getters.filesOfSubmission(
+      json.first_submission_id
     );
-    const filesOfSecond = this.convertToFilesByName(
-      json.files_of_second_submission as Array<Record<string, unknown>>
+    const filesOfSecondSubmission = store.getters.filesOfSubmission(
+      json.second_submission_id
+    );
+
+    const filesOfFirstConverted = this.convertToFilesByName(
+      filesOfFirstSubmission
+    );
+    const filesOfSecondConverted = this.convertToFilesByName(
+      filesOfSecondSubmission
     );
 
     const matches = json.matches as Array<Record<string, unknown>>;
@@ -26,8 +34,8 @@ export class ComparisonFactory {
       json.second_submission_id as string,
       json.match_percentage as number
     );
-    comparison.filesOfFirstSubmission = filesOfFirst;
-    comparison.filesOfSecondSubmission = filesOfSecond;
+    comparison.filesOfFirstSubmission = filesOfFirstConverted;
+    comparison.filesOfSecondSubmission = filesOfSecondConverted;
     comparison.colors = colors;
     comparison.allMatches = coloredMatches;
     comparison.matchesInFirstSubmission = matchesInFirst;
@@ -37,18 +45,18 @@ export class ComparisonFactory {
   }
 
   private static convertToFilesByName(
-    files: Array<Record<string, unknown>>
+    files: Array<{ name: string; value: string }>
   ): Map<string, SubmissionFile> {
     const map = new Map<string, SubmissionFile>();
     files.forEach((val) => {
-      if (!map.get(val.file_name as string)) {
-        map.set(val.file_name as string, {
+      if (!map.get(val.name)) {
+        map.set(val.name as string, {
           lines: [],
           collapsed: false,
         });
       }
-      map.set(val.file_name as string, {
-        lines: val.lines as Array<string>,
+      map.set(val.name as string, {
+        lines: val.value.split(/\r?\n/),
         collapsed: false,
       });
     });

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

@@ -13,6 +13,7 @@ interface State {
    * Stored files if zip mode is used. Stores the files as key - file name, value - file string
    */
   files: Record<string, string>;
+  submissions: Record<string, Map<string, string>>;
   /**
    * Indicates whether local mode is used.
    */
@@ -35,6 +36,10 @@ interface File {
   fileName: string;
   data: string;
 }
+interface SubmissionFile {
+  name: string;
+  file: File;
+}
 
 interface LoadConfiguration {
   local: boolean;
@@ -53,6 +58,7 @@ const store = createStore<State>({
      * Stored files if zip mode is used. Stores the files as key - file name, value - file string
      */
     files: {},
+    submissions: {},
     /**
      * Indicates whether local mode is used.
      */
@@ -70,6 +76,14 @@ const store = createStore<State>({
      */
     fileString: "",
   },
+  getters: {
+    filesOfSubmission: (state) => (name: string) => {
+      return Array.from(state.submissions[name], ([name, value]) => ({
+        name,
+        value,
+      }));
+    },
+  },
   mutations: {
     addAnonymous(state: State, id) {
       for (let i = 0; i < id.length; i++) {
@@ -87,6 +101,15 @@ const store = createStore<State>({
     saveFile(state, file: File) {
       state.files[file.fileName] = file.data;
     },
+    saveSubmissionFile(state, submissionFile: SubmissionFile) {
+      if (!state.submissions[submissionFile.name]) {
+        state.submissions[submissionFile.name] = new Map();
+      }
+      state.submissions[submissionFile.name].set(
+        submissionFile.file.fileName,
+        submissionFile.file.data
+      );
+    },
     setLoadingType(state, payload: LoadConfiguration) {
       state.local = payload.local;
       state.zip = payload.zip;

+ 15 - 5
report-viewer/src/views/FileUploadView.vue

@@ -24,6 +24,7 @@ import jszip from "jszip";
 import router from "@/router";
 import store from "@/store/store";
 import { getFileExtension } from "@/utils/Utils";
+import path from "path";
 
 export default defineComponent({
   name: "FileUploadView",
@@ -43,7 +44,6 @@ export default defineComponent({
         name: "OverviewView",
       });
     };
-
     const navigateToComparisonView = (firstId: string, secondId: string) => {
       router.push({
         name: "ComparisonView",
@@ -60,10 +60,20 @@ export default defineComponent({
     const handleZipFile = (file: File) => {
       jszip.loadAsync(file).then(async (zip) => {
         for (const fileName of Object.keys(zip.files)) {
-          console.log(fileName);
-          await zip.files[fileName].async("string").then((data) => {
-            store.commit("saveFile", { fileName: fileName, data: data });
-          });
+          if (fileName.match(/Submissions\/(.+)\/(.+)/)) {
+            const filePath = path.parse(fileName);
+            const submissionName = filePath.dir.split("/")[2];
+            await zip.files[fileName].async("string").then((data) => {
+              store.commit("saveSubmissionFile", {
+                name: submissionName,
+                file: { fileName: filePath.base, data: data },
+              });
+            });
+          } else {
+            await zip.files[fileName].async("string").then((data) => {
+              store.commit("saveFile", { fileName: fileName, data: data });
+            });
+          }
         }
         store.commit("setLoadingType", {
           local: false,

+ 14 - 3
report-viewer/vue.config.js

@@ -1,4 +1,15 @@
-module.exports = {
-    publicPath: '/JPlag'
-};
+const { defineConfig } = require("@vue/cli-service");
+const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
+module.exports = defineConfig({
+  publicPath: "/JPlag",
 
+  transpileDependencies: true,
+  configureWebpack: {
+    plugins: [new NodePolyfillPlugin()],
+    optimization: {
+      splitChunks: {
+        chunks: "all",
+      },
+    },
+  },
+});