Skip to content

Commit 92e04b7

Browse files
committed
Updated SolutionParser to match with the new ZIP structure of moodle submissions ZIP. Light theme is now the default theme. Added submission time to Solution.
SimilarSolutionPair's first Solution is now always the one that was submitted first.
1 parent 0efb455 commit 92e04b7

File tree

9 files changed

+93
-62
lines changed

9 files changed

+93
-62
lines changed

src/main/java/ee/ut/similaritydetector/backend/Analyser.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,13 @@ private void compareSolutions(Exercise exercise) {
157157
if (similarity > exercise.getSimilarityThreshold()) {
158158
solution1.addSimilarSolution(solution2);
159159
solution2.addSimilarSolution(solution1);
160-
similarSolutionPairs.add(new SimilarSolutionPair(similarity, solution1, solution2));
160+
SimilarSolutionPair newSolutionPair;
161+
if (solution1.getSubmissionTime().isBefore(solution2.getSubmissionTime())) {
162+
newSolutionPair = new SimilarSolutionPair(similarity, solution1, solution2);
163+
} else {
164+
newSolutionPair = new SimilarSolutionPair(similarity, solution2, solution1);
165+
}
166+
similarSolutionPairs.add(newSolutionPair);
161167
System.out.println("\t" + String.format("%.1f%%", similarity * 100) + "\t" +
162168
solution1.getAuthor() + ", " + solution2.getAuthor() );
163169
}

src/main/java/ee/ut/similaritydetector/backend/Exercise.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ public List<Solution> getSolutions() {
2626
return solutions;
2727
}
2828

29+
public void replaceSolution(int index, Solution newSolution) {
30+
solutions.set(index, newSolution);
31+
}
32+
2933
public void addSolution(Solution solution) {
3034
solutions.add(solution);
3135
}

src/main/java/ee/ut/similaritydetector/backend/Solution.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,26 @@
66
import java.nio.charset.StandardCharsets;
77
import java.nio.file.Files;
88
import java.nio.file.Path;
9-
import java.util.ArrayList;
10-
import java.util.HashSet;
11-
import java.util.List;
12-
import java.util.Set;
9+
import java.time.LocalDateTime;
10+
import java.util.*;
1311

1412
public class Solution {
1513

1614
private final String author;
1715
private final String exerciseName;
1816
private final File sourceCodeFile;
1917
private final Set<Solution> similarSolutions;
18+
private final LocalDateTime submissionTime;
2019
private File preprocessedCodeFile;
2120
private File sourceCodeHTMLLight;
2221
private File sourceCodeHTMLDark;
2322

24-
public Solution(String author, String exerciseName, File sourceCodeFile) {
23+
public Solution(String author, String exerciseName, File sourceCodeFile, LocalDateTime submissionTime) {
2524
this.author = author;
2625
this.exerciseName = exerciseName;
2726
this.sourceCodeFile = sourceCodeFile;
2827
similarSolutions = new HashSet<>();
28+
this.submissionTime = submissionTime;
2929
}
3030

3131
public Set<Solution> getSimilarSolutions() {
@@ -64,6 +64,10 @@ public String getExerciseName() {
6464
return exerciseName;
6565
}
6666

67+
public LocalDateTime getSubmissionTime() {
68+
return submissionTime;
69+
}
70+
6771
/**
6872
* Reads the source code file lines as a list of strings.
6973
*
@@ -143,5 +147,4 @@ public void generateSyntaxHighlightedHTML() {
143147
sourceCodeHTMLLight = new File(lightHTMLPath);
144148
}
145149

146-
147150
}

src/main/java/ee/ut/similaritydetector/backend/SolutionParser.java

Lines changed: 61 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@
88
import java.io.IOException;
99
import java.nio.charset.StandardCharsets;
1010
import java.nio.file.InvalidPathException;
11+
import java.time.LocalDateTime;
12+
import java.time.format.DateTimeFormatter;
13+
import java.time.format.DateTimeFormatterBuilder;
1114
import java.util.ArrayList;
1215
import java.util.Enumeration;
1316
import java.util.List;
17+
import java.util.Optional;
1418
import java.util.regex.Matcher;
1519
import java.util.regex.Pattern;
1620
import java.util.zip.ZipEntry;
@@ -20,15 +24,15 @@
2024
public class SolutionParser {
2125

2226
public static final String outputDirectoryPath = "analyser_resources/";
23-
private final File contentDirectory;
27+
private final File solutionsDirectory;
2428
private final File outputDirectory;
2529
private final boolean preprocessSourceCode;
2630
private final boolean anonymousResults;
2731

2832
private final Analyser analyser;
2933

30-
public SolutionParser(File contentDirectory, boolean preprocessSourceCode, boolean anonymousResults, Analyser analyser) {
31-
this.contentDirectory = contentDirectory;
34+
public SolutionParser(File solutionsDirectory, boolean preprocessSourceCode, boolean anonymousResults, Analyser analyser) {
35+
this.solutionsDirectory = solutionsDirectory;
3236
this.outputDirectory = new File(outputDirectoryPath);
3337
System.out.println(outputDirectory.getAbsolutePath());
3438
this.preprocessSourceCode = preprocessSourceCode;
@@ -49,11 +53,11 @@ public List<Exercise> parseSolutions() {
4953

5054
// Counts the total number of solutions for progress tracking
5155
try {
52-
ZipFile zipFile = new ZipFile(contentDirectory);
56+
ZipFile zipFile = new ZipFile(solutionsDirectory);
5357
Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
5458
while (zipEntries.hasMoreElements()) {
5559
ZipEntry entry = zipEntries.nextElement();
56-
if (!entry.isDirectory()) {
60+
if (!entry.isDirectory() && entry.getName().endsWith(".py")) {
5761
numSolutions++;
5862
}
5963
}
@@ -62,41 +66,55 @@ public List<Exercise> parseSolutions() {
6266
}
6367
// File unzipping adapted from: https://www.baeldung.com/java-compress-and-uncompress [11.03.2021]
6468
byte[] buffer = new byte[1024];
65-
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(contentDirectory), StandardCharsets.UTF_8)) {
69+
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(solutionsDirectory), StandardCharsets.UTF_8)) {
6670
ZipEntry zipEntry = zis.getNextEntry();
6771
while (zipEntry != null) {
6872
File newFile = newFile(outputDirectory, zipEntry);
6973
if (zipEntry.isDirectory()) {
7074
if (!newFile.isDirectory() && !newFile.mkdirs()) {
7175
throw new IOException("Failed to create directory " + newFile);
7276
}
73-
} else {
74-
// fix for Windows-created archives
75-
File parent = newFile.getParentFile();
76-
if (!parent.isDirectory() && !parent.mkdirs()) {
77-
throw new IOException("Failed to create directory " + parent);
78-
}
79-
// write file content
80-
FileOutputStream fos = new FileOutputStream(newFile);
81-
int len;
82-
while ((len = zis.read(buffer)) > 0) {
83-
fos.write(buffer, 0, len);
84-
}
85-
fos.close();
86-
// parse the solution from unzipped file
87-
try {
88-
Solution solution = parseSolution(newFile);
89-
Exercise exercise = exercises.stream().filter(e -> e.getName().equals(solution.getExerciseName())).findAny().orElse(null);
90-
if (exercise != null) {
91-
exercise.addSolution(solution);
92-
} else {
93-
exercises.add(new Exercise(solution.getExerciseName(), solution));
77+
}
78+
// If entry is a file
79+
else {
80+
if (zipEntry.getName().endsWith(".py")) {
81+
// fix for Windows-created archives
82+
File parent = newFile.getParentFile();
83+
if (!parent.isDirectory() && !parent.mkdirs()) {
84+
throw new IOException("Failed to create directory " + parent);
85+
}
86+
// write file content
87+
FileOutputStream fos = new FileOutputStream(newFile);
88+
int len;
89+
while ((len = zis.read(buffer)) > 0) {
90+
fos.write(buffer, 0, len);
91+
}
92+
fos.close();
93+
// parse the solution from unzipped file
94+
try {
95+
Solution newSolution = parseSolution(newFile);
96+
Exercise exercise = exercises.stream().filter(e -> e.getName().equals(newSolution.getExerciseName())).findAny().orElse(null);
97+
if (exercise != null) {
98+
Optional<Solution> existingSolution = exercise.getSolutions().stream().filter(sol ->
99+
sol.getAuthor().equals(newSolution.getAuthor())).findAny();
100+
// Replace with latest submission of solution if a solution is already present for the author
101+
if (existingSolution.isPresent()) {
102+
if (existingSolution.get().getSubmissionTime().isBefore(newSolution.getSubmissionTime())) {
103+
System.out.println(newSolution.getAuthor() + " - " + existingSolution.get().getSubmissionTime() + " - " + newSolution.getSubmissionTime());
104+
exercise.replaceSolution(exercise.getSolutions().indexOf(existingSolution.get()), newSolution);
105+
}
106+
} else {
107+
exercise.addSolution(newSolution);
108+
}
109+
} else {
110+
exercises.add(new Exercise(newSolution.getExerciseName(), newSolution));
111+
}
112+
} catch (Exception e) {
113+
e.printStackTrace();
94114
}
95-
} catch (Exception e) {
96-
e.printStackTrace();
115+
parsedSolutions++;
116+
analyser.updateProcessingProgress(parsedSolutions, numSolutions);
97117
}
98-
parsedSolutions ++;
99-
analyser.updateProcessingProgress(parsedSolutions, numSolutions);
100118
}
101119
zipEntry = zis.getNextEntry();
102120
}
@@ -133,20 +151,24 @@ public static File newFile(File destinationDir, ZipEntry zipEntry) throws IOExce
133151
*/
134152
private Solution parseSolution(File sourceCodeFile) throws Exception {
135153
Solution solution;
136-
Pattern solutionFolderPattern = Pattern.compile("(.+)_(.+)");
137-
Matcher matcher = solutionFolderPattern.matcher(sourceCodeFile.getParentFile().getName());
154+
Pattern authorFolderPattern = Pattern.compile("(.+ ?) ([0-9]+) (.+)");
155+
//Pattern solutionFolderPattern = Pattern.compile("(.+)_(.+)");
156+
Matcher matcher = authorFolderPattern.matcher(sourceCodeFile.getParentFile().getParentFile().getName());
138157
if (matcher.find()) {
139158
String author;
140159
if (anonymousResults)
141-
author = matcher.group(1);
142-
else
143160
author = matcher.group(2);
144-
solution = new Solution(author, sourceCodeFile.getName(), sourceCodeFile);
161+
else
162+
author = matcher.group(1);
163+
LocalDateTime submissionTime = LocalDateTime.parse(
164+
sourceCodeFile.getParentFile().getName(),
165+
DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss"));
166+
solution = new Solution(author, sourceCodeFile.getName(), sourceCodeFile, submissionTime);
145167
} else {
146-
throw new InvalidPathException(sourceCodeFile.getParentFile().getName(), " is an invalid file path.");
168+
throw new InvalidPathException(sourceCodeFile.getParentFile().getName(), "Invalid solution file path");
147169
}
148170
if (preprocessSourceCode) {
149-
preprocessSourceCode2(sourceCodeFile.getAbsolutePath());
171+
preprocessSourceCode(sourceCodeFile.getAbsolutePath());
150172
String sourceCodePath = sourceCodeFile.getAbsolutePath();
151173
File preProcessedCodeFile = new File(sourceCodePath.substring(0, sourceCodePath.length() - 3) + "_preprocessed.py");
152174
solution.setPreprocessedCodeFile(preProcessedCodeFile);
@@ -161,7 +183,7 @@ private Solution parseSolution(File sourceCodeFile) throws Exception {
161183
* @param filePath the source code file's path
162184
* @throws Exception if the preprocessing fails
163185
*/
164-
public void preprocessSourceCode2(String filePath) throws Exception {
186+
public void preprocessSourceCode(String filePath) throws Exception {
165187
final String preprocessorScript = "/ee/ut/similaritydetector/python/Preprocessor.py";
166188

167189
PythonInterpreter interpreter = new PythonInterpreter();

src/main/java/ee/ut/similaritydetector/ui/SimilarityDetectorLauncher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public void loadMainView(Stage stage) throws IOException {
4848
// Icon from: https://icons-for-free.com/spy-131964785010048699/ [25.03.2021]
4949
stage.getIcons().add(new Image(getClass().getResourceAsStream("/ee/ut/similaritydetector/img/app_icon.png")));
5050
stage.show();
51-
UserData.getInstance().setDarkMode(true);
51+
UserData.getInstance().setDarkMode(false);
5252
stage.setOnCloseRequest(this::showExitConfirmationAlert);
5353
MainViewController.stage = stage;
5454
}

src/main/java/ee/ut/similaritydetector/ui/controllers/MenuBarController.java

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,11 @@ public MenuItem getCloseAllTabsMenuItem() {
4949
private void initialize() {
5050
lightTheme.setOnAction(event -> Platform.runLater(this::activateLightTheme));
5151
darkTheme.setOnAction(event -> Platform.runLater(this::activateDarkTheme));
52-
// When scenes are switched then persists theme
53-
if (MainViewController.stage != null) {
54-
if (UserData.getInstance().isDarkMode()) {
55-
darkTheme.setSelected(true);
56-
} else {
57-
lightTheme.setSelected(true);
58-
}
59-
}
60-
// When application is started
61-
else {
62-
Platform.runLater(this::activateDarkTheme);
52+
// When scenes are switched then persists theme selection
53+
if (UserData.getInstance().isDarkMode()) {
54+
darkTheme.setSelected(true);
55+
} else {
56+
lightTheme.setSelected(true);
6357
}
6458
}
6559

@@ -99,7 +93,7 @@ private void activateLightTheme() {
9993
* Persists the current theme through scene changes.
10094
*/
10195
public void persistCurrentTheme() {
102-
if (darkTheme.isSelected()) {
96+
if (UserData.getInstance().isDarkMode()) {
10397
activateDarkTheme();
10498
} else {
10599
activateLightTheme();

src/main/java/ee/ut/similaritydetector/ui/utils/UserData.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public UserData(boolean isDarkMode) {
1515

1616
public static UserData getInstance() {
1717
if (instance == null) {
18-
instance = new UserData(true);
18+
instance = new UserData(false);
1919
}
2020
return instance;
2121
}

src/main/resources/ee/ut/similaritydetector/fxml/menu_bar.fxml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
<ToggleGroup fx:id="themeGroup" />
2929
</toggleGroup>
3030
</RadioMenuItem>
31-
<RadioMenuItem fx:id="darkTheme" mnemonicParsing="false" selected="true" text="Dark" toggleGroup="$themeGroup" />
31+
<RadioMenuItem fx:id="darkTheme" mnemonicParsing="false" text="Dark" toggleGroup="$themeGroup" />
3232
</items>
3333
</Menu>
3434
<Menu fx:id="helpMenu" mnemonicParsing="false" text="Help">

src/main/resources/ee/ut/similaritydetector/python/Preprocessor.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,6 @@ def preprocess_source_code(source_code):
5353
preprocessed_code_file.write(preprocessed_code)
5454
# If the source code is syntactically incorrect
5555
except IndentationError:
56-
sys.stderr.write("Syntactically incorrect program: " + source_code_file.name + "\n")
56+
sys.stderr.write("[Preprocessing] Syntactically incorrect program: " + source_code_file.name + "\n")
57+
except Exception:
58+
sys.stderr.write("[Preprocessing] Unexpected error: " + source_code_file.name + "\n")

0 commit comments

Comments
 (0)