Skip to content

Commit

Permalink
Plagiarism checks: Update to JPlag 4.0 and support plagiarism checks …
Browse files Browse the repository at this point in the history
…for Swift and Kotlin (#5695)
  • Loading branch information
Stephan Krusche authored Oct 4, 2022
1 parent 723ddcc commit c166b4c
Show file tree
Hide file tree
Showing 40 changed files with 323 additions and 238 deletions.
23 changes: 22 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,28 @@ dependencies {
implementation "com.offbytwo.jenkins:jenkins-client:0.3.8"
implementation "org.gitlab4j:gitlab4j-api:5.0.1"

implementation "de.jplag:jplag:3.0.0"
implementation "de.jplag:jplag:${jplag_version}"
implementation "de.jplag:java:${jplag_version}"
implementation "de.jplag:kotlin:${jplag_version}"
implementation "de.jplag:cpp:${jplag_version}"
implementation "de.jplag:swift:${jplag_version}"
implementation "de.jplag:java:${jplag_version}"
implementation "de.jplag:python-3:${jplag_version}"
implementation "de.jplag:text:${jplag_version}"


implementation "org.slf4j:jcl-over-slf4j:${slf4j_version}"
implementation "org.slf4j:jul-to-slf4j:${slf4j_version}"
implementation ("org.slf4j:slf4j-api") {
version {
strictly "${slf4j_version}"
}
}
implementation "org.apache.logging.log4j:log4j-to-slf4j:2.17.2"
// implementation "org.apache.logging.log4j:log4j-to-slf4j:2.19.0"
// implementation "ch.qos.logback:logback-classic:1.4.1"




// https://search.maven.org/artifact/org.eclipse.jgit/org.eclipse.jgit
Expand Down
6 changes: 3 additions & 3 deletions docs/user/exercises/programming-exercise-setup.inc
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,17 @@ Instructors can still use those templates to generate programming exercises and
+----------------------+----------------------+----------------------+------------------+--------------+--------------+------------------------------+--------------------------------------------+
| C | no | yes | yes | no | no | no | no |
+----------------------+----------------------+----------------------+------------------+--------------+--------------+------------------------------+--------------------------------------------+
| C (FACT framework) | no | no | no | no | no | no | no |
| C (FACT framework) | no | no | yes | no | no | no | no |
+----------------------+----------------------+----------------------+------------------+--------------+--------------+------------------------------+--------------------------------------------+
| Haskell | yes | no | no | no | no | yes | no |
+----------------------+----------------------+----------------------+------------------+--------------+--------------+------------------------------+--------------------------------------------+
| Kotlin | yes | no | no | yes | no | no | yes |
| Kotlin | yes | no | yes | yes | no | no | yes |
+----------------------+----------------------+----------------------+------------------+--------------+--------------+------------------------------+--------------------------------------------+
| VHDL | no | no | no | no | no | no | no |
+----------------------+----------------------+----------------------+------------------+--------------+--------------+------------------------------+--------------------------------------------+
| Assembler | no | no | no | no | no | no | no |
+----------------------+----------------------+----------------------+------------------+--------------+--------------+------------------------------+--------------------------------------------+
| Swift | no | no | no | no | no | no | no |
| Swift | no | no | yes | no | no | no | no |
+----------------------+----------------------+----------------------+------------------+--------------+--------------+------------------------------+--------------------------------------------+
| OCaml | no | no | no | no | no | yes | no |
+----------------------+----------------------+----------------------+------------------+--------------+--------------+------------------------------+--------------------------------------------+
Expand Down
2 changes: 2 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ mockito_version=4.8.0
fasterxml_version=2.13.4
jgit_version=6.3.0.202209071007-r
checkstyle_version=10.3.3
jplag_version=4.0.0
slf4j_version=1.7.36

# gradle plugin version
jib_plugin_version=2.0.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import static de.tum.in.www1.artemis.service.util.RoundingUtil.roundScoreSpecifiedByCourseSettings;

import org.apache.commons.lang.NotImplementedException;
import org.apache.commons.lang3.NotImplementedException;

import de.tum.in.www1.artemis.repository.GradingScaleRepository;
import de.tum.in.www1.artemis.web.rest.dto.BonusExampleDTO;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.tum.in.www1.artemis.domain.plagiarism;

import java.io.File;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
Expand All @@ -14,6 +15,7 @@

import de.jplag.JPlagComparison;
import de.tum.in.www1.artemis.domain.DomainObject;
import de.tum.in.www1.artemis.domain.Exercise;
import de.tum.in.www1.artemis.domain.plagiarism.text.TextSubmissionElement;

/**
Expand Down Expand Up @@ -63,7 +65,7 @@ public class PlagiarismComparison<E extends PlagiarismSubmissionElement> extends
protected Set<PlagiarismMatch> matches;

/**
* Similarity of the compared submissions (between 0 and 1).
* Similarity of the compared submissions in percentage (between 0 and 100).
*/
@Column(name = "similarity")
private double similarity;
Expand All @@ -79,14 +81,17 @@ public class PlagiarismComparison<E extends PlagiarismSubmissionElement> extends
*
* @param jplagComparison JPlag comparison to map to the new PlagiarismComparison instance
* @return a new instance with the content of the JPlagComparison
* @param exercise the exercise to which the comparison belongs, either Text or Programming
* @param submissionDirectory the directory to which all student submissions have been downloaded / stored
*/
public static PlagiarismComparison<TextSubmissionElement> fromJPlagComparison(JPlagComparison jplagComparison) {
public static PlagiarismComparison<TextSubmissionElement> fromJPlagComparison(JPlagComparison jplagComparison, Exercise exercise, File submissionDirectory) {
PlagiarismComparison<TextSubmissionElement> comparison = new PlagiarismComparison<>();

comparison.setSubmissionA(PlagiarismSubmission.fromJPlagSubmission(jplagComparison.getFirstSubmission()));
comparison.setSubmissionB(PlagiarismSubmission.fromJPlagSubmission(jplagComparison.getSecondSubmission()));
comparison.setMatches(jplagComparison.getMatches().stream().map(PlagiarismMatch::fromJPlagMatch).collect(Collectors.toSet()));
comparison.setSimilarity(jplagComparison.similarity());
comparison.setSubmissionA(PlagiarismSubmission.fromJPlagSubmission(jplagComparison.firstSubmission(), exercise, submissionDirectory));
comparison.setSubmissionB(PlagiarismSubmission.fromJPlagSubmission(jplagComparison.secondSubmission(), exercise, submissionDirectory));
comparison.setMatches(jplagComparison.matches().stream().map(PlagiarismMatch::fromJPlagMatch).collect(Collectors.toSet()));
// Note: JPlag returns a value between 0 and 1, we assume and store a value between 0 and 100 (percentage) in the database
comparison.setSimilarity(jplagComparison.similarity() * 100);
comparison.setStatus(PlagiarismStatus.NONE);

return comparison;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ public class PlagiarismMatch {
*/
public static PlagiarismMatch fromJPlagMatch(Match jplagMatch) {
PlagiarismMatch match = new PlagiarismMatch();
match.setStartA(jplagMatch.getStartOfFirst());
match.setStartB(jplagMatch.getStartOfSecond());
match.setLength(jplagMatch.getLength());
match.setStartA(jplagMatch.startOfFirst());
match.setStartB(jplagMatch.startOfSecond());
match.setLength(jplagMatch.length());
return match;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package de.tum.in.www1.artemis.domain.plagiarism;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import javax.persistence.*;

Expand All @@ -15,6 +15,7 @@

import de.jplag.Submission;
import de.tum.in.www1.artemis.domain.DomainObject;
import de.tum.in.www1.artemis.domain.Exercise;
import de.tum.in.www1.artemis.domain.modeling.ModelingSubmission;
import de.tum.in.www1.artemis.domain.participation.StudentParticipation;
import de.tum.in.www1.artemis.domain.plagiarism.modeling.ModelingSubmissionElement;
Expand Down Expand Up @@ -84,9 +85,11 @@ public class PlagiarismSubmission<E extends PlagiarismSubmissionElement> extends
* Create a new PlagiarismSubmission instance from an existing JPlag Submission
*
* @param jplagSubmission the JPlag Submission to create the PlagiarismSubmission from
* @param exercise the exercise to which the comparison belongs, either Text or Programming
* @param submissionDirectory the directory to which all student submissions have been downloaded / stored
* @return a new PlagiarismSubmission instance
*/
public static PlagiarismSubmission<TextSubmissionElement> fromJPlagSubmission(Submission jplagSubmission) {
public static PlagiarismSubmission<TextSubmissionElement> fromJPlagSubmission(Submission jplagSubmission, Exercise exercise, File submissionDirectory) {
PlagiarismSubmission<TextSubmissionElement> submission = new PlagiarismSubmission<>();

String[] submissionIdAndStudentLogin = jplagSubmission.getName().split("[-.]");
Expand All @@ -106,8 +109,8 @@ public static PlagiarismSubmission<TextSubmissionElement> fromJPlagSubmission(Su
}

submission.setStudentLogin(studentLogin);
submission.setElements(StreamSupport.stream(jplagSubmission.getTokenList().allTokens().spliterator(), false).filter(Objects::nonNull)
.map(token -> TextSubmissionElement.fromJPlagToken(token, submission)).collect(Collectors.toCollection(ArrayList::new)));
submission.setElements(jplagSubmission.getTokenList().stream().filter(Objects::nonNull)
.map(token -> TextSubmissionElement.fromJPlagToken(token, submission, exercise, submissionDirectory)).collect(Collectors.toCollection(ArrayList::new)));
submission.setSubmissionId(submissionId);
submission.setSize(jplagSubmission.getNumberOfTokens());
submission.setScore(null); // TODO
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package de.tum.in.www1.artemis.domain.plagiarism.text;

import java.util.Comparator;

import javax.persistence.Entity;

import de.jplag.JPlagComparison;
import org.apache.commons.lang3.ArrayUtils;

import de.jplag.JPlagResult;
import de.tum.in.www1.artemis.domain.Exercise;
import de.tum.in.www1.artemis.domain.plagiarism.PlagiarismComparison;
import de.tum.in.www1.artemis.domain.plagiarism.PlagiarismResult;

Expand All @@ -18,17 +18,23 @@ public class TextPlagiarismResult extends PlagiarismResult<TextSubmissionElement
/**
* converts the given JPlagResult into a TextPlagiarismResult, only uses the 500 most interesting comparisons based on the highest similarity
* @param result the JPlagResult contains comparisons
* @param exercise the exercise to which the result should belong, either Text or Programming
*/
public void convertJPlagResult(JPlagResult result) {
public void convertJPlagResult(JPlagResult result, Exercise exercise) {
// sort and limit the number of comparisons to 500
var comparisons = result.getComparisons().stream().sorted(Comparator.comparingDouble(JPlagComparison::similarity).reversed()).limit(500).toList();
var comparisons = result.getComparisons(500);
// only convert those 500 comparisons to save memory and cpu power
for (var jPlagComparison : comparisons) {
var comparison = PlagiarismComparison.fromJPlagComparison(jPlagComparison);
var comparison = PlagiarismComparison.fromJPlagComparison(jPlagComparison, exercise, result.getOptions().submissionDirectories().iterator().next());
comparison.setPlagiarismResult(this);
this.comparisons.add(comparison);
}
this.duration = result.getDuration();
this.setSimilarityDistribution(result.getSimilarityDistribution());
// NOTE: there seems to be an issue in JPlag 4.0 that the similarity distribution is reversed, either in the implementation or in the documentation.
// we use it like this: 0: [0% - 10%), 1: [10% - 20%), 2: [20% - 30%), ..., 9: [90% - 100%] so we reverse it
var similarityDistribution = result.getSimilarityDistribution();
ArrayUtils.reverse(similarityDistribution);
this.setSimilarityDistribution(similarityDistribution);
this.setExercise(exercise);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package de.tum.in.www1.artemis.domain.plagiarism.text;

import java.io.File;

import javax.persistence.Column;
import javax.persistence.Entity;

import de.jplag.Token;
import de.tum.in.www1.artemis.domain.Exercise;
import de.tum.in.www1.artemis.domain.ProgrammingExercise;
import de.tum.in.www1.artemis.domain.plagiarism.PlagiarismSubmission;
import de.tum.in.www1.artemis.domain.plagiarism.PlagiarismSubmissionElement;

Expand All @@ -18,6 +22,7 @@ public class TextSubmissionElement extends PlagiarismSubmissionElement {

private String file;

// TODO: remove this column, we don't really need it
@Column(name = "token_type")
private int type;

Expand All @@ -28,15 +33,28 @@ public class TextSubmissionElement extends PlagiarismSubmissionElement {
*
* @param token the JPlag Token to create the TextSubmissionElement from
* @param plagiarismSubmission the PlagiarismSubmission the TextSubmissionElement belongs to
* @param exercise the exercise to which the element belongs, either Text or Programming
* @param submissionDirectory the directory to which all student submissions have been downloaded / stored
* @return a new TextSubmissionElement instance
*/
public static TextSubmissionElement fromJPlagToken(Token token, PlagiarismSubmission<TextSubmissionElement> plagiarismSubmission) {
public static TextSubmissionElement fromJPlagToken(Token token, PlagiarismSubmission<TextSubmissionElement> plagiarismSubmission, Exercise exercise, File submissionDirectory) {
TextSubmissionElement textSubmissionElement = new TextSubmissionElement();

textSubmissionElement.setColumn(token.getColumn());
textSubmissionElement.setLine(token.getLine());
textSubmissionElement.setFile(token.file);
textSubmissionElement.setType(token.type);
if (exercise instanceof ProgrammingExercise) {
// Note: for text submissions 'file' must be null
// Note: we want to get the relative path within the repository and not the absolute path
var submissionDirectoryAbsoluteFile = submissionDirectory.getAbsoluteFile();
var tokenAbsoluteFile = token.getFile().getAbsoluteFile();
var filePath = submissionDirectoryAbsoluteFile.toPath().relativize(tokenAbsoluteFile.toPath());
// remove the first element, because it is the parent folder in which the whole repo was saved
var fileStringWithinRepository = filePath.toString();
if (filePath.getNameCount() > 1) {
fileStringWithinRepository = filePath.subpath(1, filePath.getNameCount()).toString();
}
textSubmissionElement.setFile(fileStringWithinRepository);
}
textSubmissionElement.setLength(token.getLength());
textSubmissionElement.setPlagiarismSubmission(plagiarismSubmission);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import javax.annotation.Nullable;

import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import java.util.List;
import java.util.Optional;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import javax.annotation.Nullable;

import org.apache.commons.lang.mutable.MutableInt;
import org.apache.commons.lang3.mutable.MutableInt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package de.tum.in.www1.artemis.service;

import org.apache.commons.lang.NotImplementedException;
import org.apache.commons.lang3.NotImplementedException;
import org.springframework.stereotype.Service;

import de.tum.in.www1.artemis.domain.*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.HiddenFileFilter;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jgit.api.*;
import org.eclipse.jgit.api.errors.CanceledException;
import org.eclipse.jgit.api.errors.GitAPIException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ public BambooProgrammingLanguageFeatureService() {
programmingLanguageFeatures.put(PYTHON, new ProgrammingLanguageFeature(PYTHON, true, false, true, false, false, List.of()));
programmingLanguageFeatures.put(C, new ProgrammingLanguageFeature(C, false, true, true, false, false, List.of(FACT, GCC)));
programmingLanguageFeatures.put(HASKELL, new ProgrammingLanguageFeature(HASKELL, true, false, false, false, true, List.of()));
programmingLanguageFeatures.put(KOTLIN, new ProgrammingLanguageFeature(KOTLIN, true, false, false, true, false, List.of()));
programmingLanguageFeatures.put(KOTLIN, new ProgrammingLanguageFeature(KOTLIN, true, false, true, true, false, List.of()));
programmingLanguageFeatures.put(VHDL, new ProgrammingLanguageFeature(VHDL, false, false, false, false, false, List.of()));
programmingLanguageFeatures.put(ASSEMBLER, new ProgrammingLanguageFeature(ASSEMBLER, false, false, false, false, false, List.of()));
programmingLanguageFeatures.put(SWIFT, new ProgrammingLanguageFeature(SWIFT, false, true, false, true, false, List.of(PLAIN, XCODE)));
programmingLanguageFeatures.put(SWIFT, new ProgrammingLanguageFeature(SWIFT, false, true, true, true, false, List.of(PLAIN, XCODE)));
programmingLanguageFeatures.put(OCAML, new ProgrammingLanguageFeature(OCAML, false, false, false, false, true, List.of()));
}
}
Loading

0 comments on commit c166b4c

Please sign in to comment.