Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slight changes to the report json format #1189

Merged
merged 21 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/src/main/java/de/jplag/JPlagResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class JPlagResult {
private final int[] similarityDistribution; // 10-element array representing the similarity distribution of the detected matches.

private List<ClusteringResult<Submission>> clusteringResult;
private final int SIMILARITY_DISTRIBUTION_SIZE = 10;
private final int SIMILARITY_DISTRIBUTION_SIZE = 100;

public JPlagResult(List<JPlagComparison> comparisons, SubmissionSet submissions, long durationInMillis, JPlagOptions options) {
// sort by similarity (descending)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import de.jplag.JPlagResult;
import de.jplag.Submission;
import de.jplag.Token;
import de.jplag.options.SimilarityMetric;
import de.jplag.reporting.FilePathUtil;
import de.jplag.reporting.reportobject.model.ComparisonReport;
import de.jplag.reporting.reportobject.model.Match;
Expand Down Expand Up @@ -55,7 +56,8 @@ private void writeComparisons(String path, List<JPlagComparison> comparisons) {
String secondSubmissionId = submissionToIdFunction.apply(comparison.secondSubmission());
String fileName = generateComparisonName(firstSubmissionId, secondSubmissionId);
addToLookUp(firstSubmissionId, secondSubmissionId, fileName);
var comparisonReport = new ComparisonReport(firstSubmissionId, secondSubmissionId, comparison.similarity(),
var comparisonReport = new ComparisonReport(firstSubmissionId, secondSubmissionId,
Map.of(SimilarityMetric.AVG.name(), comparison.similarity(), SimilarityMetric.MAX.name(), comparison.maximalSimilarity()),
convertMatchesToReportMatches(comparison));
fileWriter.saveAsJSON(comparisonReport, path, fileName);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import de.jplag.reporting.jsonfactory.ToDiskWriter;
import de.jplag.reporting.reportobject.mapper.ClusteringResultMapper;
import de.jplag.reporting.reportobject.mapper.MetricMapper;
import de.jplag.reporting.reportobject.model.Metric;
import de.jplag.reporting.reportobject.model.OverviewReport;
import de.jplag.reporting.reportobject.model.SubmissionFileIndex;
import de.jplag.reporting.reportobject.model.Version;
Expand Down Expand Up @@ -175,7 +174,7 @@ private void writeOverview(JPlagResult result, String path) {

int totalComparisons = result.getAllComparisons().size();
int numberOfMaximumComparisons = result.getOptions().maximumNumberOfComparisons();
int shownComparisons = totalComparisons > numberOfMaximumComparisons ? numberOfMaximumComparisons : totalComparisons;
int shownComparisons = Math.min(totalComparisons, numberOfMaximumComparisons);
int missingComparisons = totalComparisons > numberOfMaximumComparisons ? (totalComparisons - numberOfMaximumComparisons) : 0;
logger.info("Total Comparisons: {}. Comparisons in Report: {}. Omitted Comparisons: {}.", totalComparisons, shownComparisons,
missingComparisons);
Expand All @@ -190,7 +189,8 @@ private void writeOverview(JPlagResult result, String path) {
result.getOptions().minimumTokenMatch(), // matchSensitivity
getDate(),// dateOfExecution
result.getDuration(), // executionTime
getMetrics(result),// metrics
MetricMapper.getDistributions(result), // distribution
new MetricMapper(submissionToIdFunction).getTopComparisons(result),// topComparisons
Kr0nox marked this conversation as resolved.
Show resolved Hide resolved
clusteringResultMapper.map(result), // clusters
totalComparisons); // totalComparisons

Expand Down Expand Up @@ -220,16 +220,6 @@ private Set<Submission> getSubmissions(List<JPlagComparison> comparisons) {
return submissions;
}

/**
* Gets the used metrics in a JPlag comparison. As Max Metric is included in every JPlag run, this always include Max
* Metric.
* @return A list contains Metric DTOs.
*/
private List<Metric> getMetrics(JPlagResult result) {
MetricMapper metricMapper = new MetricMapper(submissionToIdFunction);
return List.of(metricMapper.getAverageMetric(result), metricMapper.getMaxMetric(result));
}

private String getDate() {
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yy");
Date date = new Date();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

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;

/**
Expand All @@ -25,40 +23,35 @@ public MetricMapper(Function<Submission, String> submissionToIdFunction) {
this.submissionToIdFunction = submissionToIdFunction;
}

public Metric getAverageMetric(JPlagResult result) {
return new Metric(SimilarityMetric.AVG.name(), convertDistribution(result.getSimilarityDistribution()),
getTopComparisons(getComparisons(result)), Messages.getString("SimilarityMetric.Avg.Description"));
/**
* Generates a map of all distributions
* @param result Result containing distributions
* @return Map with key as name of metric and value as distribution
*/
public static Map<String, List<Integer>> getDistributions(JPlagResult result) {
return Map.of(SimilarityMetric.AVG.name(), convertDistribution(result.getSimilarityDistribution()), SimilarityMetric.MAX.name(),
convertDistribution(result.getMaxSimilarityDistribution()));
}

public Metric getMaxMetric(JPlagResult result) {
return new Metric(SimilarityMetric.MAX.name(), convertDistribution(result.getMaxSimilarityDistribution()),
getMaxSimilarityTopComparisons(getComparisons(result)), Messages.getString("SimilarityMetric.Max.Description"));
/**
* Generates a List of the top comparisons
* @param result Result containing comparisons
* @return List of top comparisons with similarities in all metrics
*/
public List<TopComparison> getTopComparisons(JPlagResult result) {
return result.getComparisons(result.getOptions().maximumNumberOfComparisons()).stream()
.map(comparison -> new TopComparison(submissionToIdFunction.apply(comparison.firstSubmission()),
submissionToIdFunction.apply(comparison.secondSubmission()), getComparisonMetricMap(comparison)))
.toList();
}

private List<JPlagComparison> getComparisons(JPlagResult result) {
int maxNumberOfComparisons = result.getOptions().maximumNumberOfComparisons();
return result.getComparisons(maxNumberOfComparisons);
private Map<String, Double> getComparisonMetricMap(JPlagComparison comparison) {
return Map.of(SimilarityMetric.AVG.name(), comparison.similarity(), SimilarityMetric.MAX.name(), comparison.maximalSimilarity());
}

private List<Integer> convertDistribution(int[] array) {
private static List<Integer> convertDistribution(int[] array) {
List<Integer> list = new ArrayList<>(Arrays.stream(array).boxed().toList());
Collections.reverse(list);
return list;
}

private List<TopComparison> getTopComparisons(List<JPlagComparison> comparisons, Function<JPlagComparison, Double> similarityExtractor) {
return comparisons.stream().sorted(Comparator.comparing(similarityExtractor).reversed())
.map(comparison -> new TopComparison(submissionToIdFunction.apply(comparison.firstSubmission()),
submissionToIdFunction.apply(comparison.secondSubmission()), similarityExtractor.apply(comparison)))
.toList();
}

private List<TopComparison> getTopComparisons(List<JPlagComparison> comparisons) {
return getTopComparisons(comparisons, JPlagComparison::similarity);
}

private List<TopComparison> getMaxSimilarityTopComparisons(List<JPlagComparison> comparisons) {
return getTopComparisons(comparisons, JPlagComparison::maximalSimilarity);
}

}
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package de.jplag.reporting.reportobject.model;

import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonProperty;

/**
* ReportViewer DTO for the comparison of two submissions.
* @param firstSubmissionId id of the first submission
* @param secondSubmissionId id of the second submission
* @param similarity average similarity. between 0.0 and 1.0.
* @param similarities map of metric names and corresponding similarities. between 0.0 and 1.0.
* @param matches the list of matches found in the comparison of the two submissions
*/
public record ComparisonReport(@JsonProperty("id1") String firstSubmissionId, @JsonProperty("id2") String secondSubmissionId,
@JsonProperty("similarity") double similarity, @JsonProperty("matches") List<Match> matches) {
@JsonProperty("similarities") Map<String, Double> similarities, @JsonProperty("matches") List<Match> matches) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ public record OverviewReport(

@JsonProperty("execution_time") long executionTime,

@JsonProperty("metrics") List<Metric> metrics,
@JsonProperty("distributions") Map<String, List<Integer>> distributions,

@JsonProperty("top_comparisons") List<TopComparison> topComparisons,

@JsonProperty("clusters") List<Cluster> clusters,

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package de.jplag.reporting.reportobject.model;

import java.util.Map;

import com.fasterxml.jackson.annotation.JsonProperty;

public record TopComparison(@JsonProperty("first_submission") String firstSubmission, @JsonProperty("second_submission") String secondSubmission,
@JsonProperty("similarity") double similarity) {
@JsonProperty("similarities") Map<String, Double> similarities) {
}
4 changes: 2 additions & 2 deletions core/src/test/java/de/jplag/BaseCodeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ protected void verifyResults(JPlagResult result) {
assertEquals(2, result.getNumberOfSubmissions());
assertEquals(1, result.getAllComparisons().size());
assertEquals(1, result.getAllComparisons().get(0).matches().size());
assertEquals(1, result.getSimilarityDistribution()[8]);
assertEquals(1, result.getSimilarityDistribution()[81]);
assertEquals(0.8125, result.getAllComparisons().get(0).similarity(), DELTA);
}

Expand Down Expand Up @@ -94,7 +94,7 @@ protected void verifySimpleSubdirectoryDuplicate(JPlagResult result, int submiss
assertEquals(submissions, result.getNumberOfSubmissions());
assertEquals(comparisons, result.getAllComparisons().size());
assertEquals(1, result.getAllComparisons().get(0).matches().size());
assertEquals(1, result.getSimilarityDistribution()[9]);
assertEquals(1, result.getSimilarityDistribution()[94]);
assertEquals(0.9473, result.getAllComparisons().get(0).similarity(), DELTA);
}

Expand Down
7 changes: 4 additions & 3 deletions core/src/test/java/de/jplag/BasicFunctionalityTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ void testSimpleDuplicate() throws ExitException {
assertEquals(2, result.getNumberOfSubmissions());
assertEquals(1, result.getAllComparisons().size());
assertEquals(1, result.getAllComparisons().get(0).matches().size());
assertEquals(1, result.getSimilarityDistribution()[6]);
assertEquals(1, result.getSimilarityDistribution()[66]);
Kr0nox marked this conversation as resolved.
Show resolved Hide resolved
assertEquals(0.666, result.getAllComparisons().get(0).similarity(), DELTA);
}

@Test
@DisplayName("test submissions with a custom minimum token match")
void testWithMinTokenMatch() throws ExitException {
var expectedDistribution = new int[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
var expectedDistribution = new int[100];
expectedDistribution[96] = 1;
JPlagResult result = runJPlag("SimpleDuplicate", it -> it.withMinimumTokenMatch(4));

assertEquals(2, result.getNumberOfSubmissions());
Expand Down Expand Up @@ -90,7 +91,7 @@ void testSingleFileSubmisssions() throws ExitException {

assertEquals(2, result.getNumberOfSubmissions());
assertEquals(1, result.getAllComparisons().size());
assertEquals(1, result.getSimilarityDistribution()[6]);
assertEquals(1, result.getSimilarityDistribution()[66]);
Kr0nox marked this conversation as resolved.
Show resolved Hide resolved
assertEquals(0.666, result.getAllComparisons().get(0).similarity(), DELTA);

var matches = result.getAllComparisons().get(0).matches();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
Expand All @@ -18,43 +19,42 @@
import de.jplag.reporting.reportobject.model.TopComparison;

public class MetricMapperTest {
private static final List<Integer> EXPECTED_DISTRIBUTION = List.of(29, 23, 19, 17, 13, 11, 7, 5, 3, 2);
private static final List<Integer> EXPECTED_AVG_DISTRIBUTION = List.of(1, 0, 0, 2, 3, 15, 5, 2, 16, 5, 2, 18, 3, 21, 2, 1, 5, 0, 14, 32, 25, 4, 2,
12, 3, 2, 5, 5, 0, 5, 1, 5, 2, 5, 4, 5, 3, 5, 18, 21, 30, 4, 3, 10, 2, 3, 17, 28, 4, 10, 2, 4, 3, 0, 2, 20, 4, 0, 19, 5, 25, 9, 4, 18, 1,
1, 1, 0, 31, 15, 35, 38, 40, 43, 45, 49, 50, 50, 50, 53, 60, 71, 73, 74, 80, 83, 87, 93, 95, 99, 102, 105, 106, 110, 113, 113, 117, 117,
122, 124);
private static final List<Integer> EXPECTED_MAX_DISTRIBUTION = List.of(130, 129, 124, 116, 114, 110, 110, 108, 103, 101, 99, 97, 96, 92, 82, 81,
70, 67, 64, 63, 59, 56, 52, 50, 50, 50, 49, 47, 43, 5, 6, 11, 4, 2, 3, 20, 37, 5, 0, 2, 33, 30, 19, 4, 5, 24, 40, 6, 3, 9, 2, 3, 18, 3, 5,
1, 4, 1, 0, 0, 5, 5, 14, 5, 42, 4, 18, 0, 0, 10, 4, 3, 17, 33, 4, 4, 3, 4, 39, 0, 20, 2, 4, 9, 0, 5, 0, 8, 23, 4, 2, 39, 3, 4, 1, 0, 3,
33, 2, 1);
Kr0nox marked this conversation as resolved.
Show resolved Hide resolved
private final MetricMapper metricMapper = new MetricMapper(Submission::getName);

@Test
public void test_getAverageMetric() {
public void test_getDistributions() {
// given
JPlagResult jPlagResult = createJPlagResult(MockMetric.AVG, distribution(EXPECTED_DISTRIBUTION),
comparison(submission("1"), submission("2"), .7), comparison(submission("3"), submission("4"), .3));
JPlagResult jPlagResult = createJPlagResult(distribution(EXPECTED_AVG_DISTRIBUTION), distribution(EXPECTED_MAX_DISTRIBUTION),
comparison(submission("1"), submission("2"), .7, .8), comparison(submission("3"), submission("4"), .3, .9));

// when
var result = metricMapper.getAverageMetric(jPlagResult);
Map<String, List<Integer>> result = MetricMapper.getDistributions(jPlagResult);

// then
Assertions.assertEquals("AVG", result.name());
Assertions.assertIterableEquals(EXPECTED_DISTRIBUTION, result.distribution());
Assertions.assertEquals(List.of(new TopComparison("1", "2", .7), new TopComparison("3", "4", .3)), result.topComparisons());
Assertions.assertEquals(
"Average of both program coverages. This is the default similarity which"
+ " works in most cases: Matches with a high average similarity indicate that the programs work " + "in a very similar way.",
result.description());
Assertions.assertEquals(Map.of("AVG", EXPECTED_AVG_DISTRIBUTION, "MAX", EXPECTED_MAX_DISTRIBUTION), result);
}

@Test
public void test_getMaxMetric() {
public void test_getTopComparisons() {
// given
JPlagResult jPlagResult = createJPlagResult(MockMetric.MAX, distribution(EXPECTED_DISTRIBUTION),
comparison(submission("00"), submission("01"), .7), comparison(submission("10"), submission("11"), .3));
JPlagResult jPlagResult = createJPlagResult(distribution(EXPECTED_AVG_DISTRIBUTION), distribution(EXPECTED_MAX_DISTRIBUTION),
comparison(submission("1"), submission("2"), .7, .8), comparison(submission("3"), submission("4"), .3, .9));

// when
var result = metricMapper.getMaxMetric(jPlagResult);
List<TopComparison> result = metricMapper.getTopComparisons(jPlagResult);

// then
Assertions.assertEquals("MAX", result.name());
Assertions.assertIterableEquals(EXPECTED_DISTRIBUTION, result.distribution());
Assertions.assertEquals(List.of(new TopComparison("00", "01", .7), new TopComparison("10", "11", .3)), result.topComparisons());
Assertions.assertEquals(
"Maximum of both program coverages. This ranking is especially useful if the programs are very "
+ "different in size. This can happen when dead code was inserted to disguise the origin of the plagiarized program.",
result.description());
List.of(new TopComparison("1", "2", Map.of("AVG", .7, "MAX", .8)), new TopComparison("3", "4", Map.of("AVG", .3, "MAX", .9))),
result);
}

private int[] distribution(List<Integer> expectedDistribution) {
Expand All @@ -67,19 +67,14 @@ private CreateSubmission submission(String name) {
return new CreateSubmission(name);
}

private Comparison comparison(CreateSubmission submission1, CreateSubmission submission2, double similarity) {
return new Comparison(submission1, submission2, similarity);
private Comparison comparison(CreateSubmission submission1, CreateSubmission submission2, double similarity, double maxSimilarity) {
return new Comparison(submission1, submission2, similarity, maxSimilarity);
}

private JPlagResult createJPlagResult(MockMetric metricToMock, int[] distribution, Comparison... createComparisonsDto) {
private JPlagResult createJPlagResult(int[] avgDistribution, int[] maxDistribution, Comparison... createComparisonsDto) {
JPlagResult jPlagResult = mock(JPlagResult.class);

if (metricToMock.equals(MockMetric.AVG)) {
doReturn(distribution).when(jPlagResult).getSimilarityDistribution();
} else if (metricToMock.equals(MockMetric.MAX)) {
doReturn(distribution).when(jPlagResult).getMaxSimilarityDistribution();

}
doReturn(avgDistribution).when(jPlagResult).getSimilarityDistribution();
doReturn(maxDistribution).when(jPlagResult).getMaxSimilarityDistribution();

JPlagOptions options = mock(JPlagOptions.class);
doReturn(createComparisonsDto.length).when(options).maximumNumberOfComparisons();
Expand All @@ -95,27 +90,19 @@ private JPlagResult createJPlagResult(MockMetric metricToMock, int[] distributio
JPlagComparison mockedComparison = mock(JPlagComparison.class);
doReturn(submission1).when(mockedComparison).firstSubmission();
doReturn(submission2).when(mockedComparison).secondSubmission();
if (metricToMock.equals(MockMetric.AVG)) {
doReturn(comparisonDto.similarity).when(mockedComparison).similarity();
} else if (metricToMock.equals(MockMetric.MAX)) {
doReturn(comparisonDto.similarity).when(mockedComparison).maximalSimilarity();
}
doReturn(comparisonDto.similarity).when(mockedComparison).similarity();
doReturn(comparisonDto.maxSimilarity).when(mockedComparison).maximalSimilarity();
comparisonList.add(mockedComparison);
}

doReturn(comparisonList).when(jPlagResult).getComparisons(anyInt());
return jPlagResult;
}

private enum MockMetric {
MAX,
AVG
}

private record Comparison(CreateSubmission submission1, CreateSubmission submission2, double similarity) {
private record Comparison(CreateSubmission submission1, CreateSubmission submission2, double similarity, double maxSimilarity) {
}

private record CreateSubmission(String name) {
}

}
}
Loading
Loading