All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.oliviercailloux.javagrade.graders.Eclipse Maven / Gradle / Ivy

The newest version!
package io.github.oliviercailloux.javagrade.graders;

import static com.google.common.base.Verify.verify;
import static io.github.oliviercailloux.grade.GitGrader.Predicates.compose;
import static io.github.oliviercailloux.grade.GitGrader.Predicates.contentMatches;
import static io.github.oliviercailloux.grade.GitGrader.Predicates.isFileNamed;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import io.github.oliviercailloux.git.filter.GitHistorySimple;
import io.github.oliviercailloux.gitjfs.GitPath;
import io.github.oliviercailloux.gitjfs.GitPathRoot;
import io.github.oliviercailloux.gitjfs.GitPathRootShaCached;
import io.github.oliviercailloux.grade.Criterion;
import io.github.oliviercailloux.grade.CriterionGradeWeight;
import io.github.oliviercailloux.grade.DeadlineGrader;
import io.github.oliviercailloux.grade.GitGeneralGrader;
import io.github.oliviercailloux.grade.GitGrader;
import io.github.oliviercailloux.grade.GitWork;
import io.github.oliviercailloux.grade.GradeUtils;
import io.github.oliviercailloux.grade.IGrade;
import io.github.oliviercailloux.grade.RepositoryFetcher;
import io.github.oliviercailloux.grade.WeightingGrade;
import io.github.oliviercailloux.grade.markers.Marks;
import io.github.oliviercailloux.grade.old.Mark;
import io.github.oliviercailloux.jaris.throwing.TFunction;
import io.github.oliviercailloux.jaris.throwing.TPredicate;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.ZonedDateTime;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jgit.diff.DiffEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// /minimax-ex/src/main/java/io/github/oliviercailloux/minimax/utils/ForwardingMutableGraph.java for
// unused import statement
public class Eclipse implements GitGrader {
  @SuppressWarnings("unused")
  private static final Logger LOGGER = LoggerFactory.getLogger(Eclipse.class);

  public static final String PREFIX = "eclipse";

  public static final ZonedDateTime DEADLINE =
      ZonedDateTime.parse("2021-01-14T23:00:00+01:00[Europe/Paris]");

  private GitHistorySimple history;

  private GitHistorySimple authoredHistory;

  private boolean excludeMine;

  public static void main(String[] args) throws Exception {
    final RepositoryFetcher fetcher = RepositoryFetcher.withPrefix(PREFIX);
    GitGeneralGrader.using(fetcher, DeadlineGrader.usingGitGrader(new Eclipse(), DEADLINE)).grade();
  }

  Eclipse() {
    excludeMine = true;
  }

  Eclipse setIncludeMine() {
    excludeMine = false;
    return this;
  }

  @Override
  public WeightingGrade grade(GitWork work) throws IOException {
    this.history = work.getHistory();
    if (excludeMine) {
      authoredHistory = history.filteredCommits(c -> !c.authorName().equals("Olivier Cailloux")
          && !c.authorName().equals("githublogin") && !c.authorName().equals("Author"));
    } else {
      authoredHistory = history;
    }
    final ImmutableSet authors = authoredHistory.graph().nodes().stream()
        .map(c -> c.getCommit().authorName()).distinct().collect(ImmutableSet.toImmutableSet());
    verify(authors.size() <= 1, authors.toString());
    LOGGER.debug("Considering whole history {} and own history {}.", history.graph().nodes(),
        authoredHistory.graph().nodes());

    final ImmutableSet.Builder gradeBuilder = ImmutableSet.builder();
    {
      final Mark hasCommit = Mark.binary(!authoredHistory.graph().nodes().isEmpty());
      gradeBuilder.add(CriterionGradeWeight.from(Criterion.given("At least one"), hasCommit, 1d));
    }
    LOGGER.info("Grading compile.");
    gradeBuilder.add(CriterionGradeWeight.from(Criterion.given("Compile"),
        GradeUtils.getBestGrade(authoredHistory.graph().nodes(), this::compileGrade, 1d), 2.5d));
    LOGGER.info("Grading warning.");
    gradeBuilder.add(CriterionGradeWeight.from(Criterion.given("Warning"),
        GradeUtils.getBestGrade(authoredHistory.graph().nodes(), this::warningGrade, 1d), 2.5d));
    LOGGER.info("Grading helper.");
    gradeBuilder.add(CriterionGradeWeight.from(Criterion.given("StrategyHelper → Helper"),
        GradeUtils.getBestGrade(authoredHistory.graph().nodes(), this::helperGrade, 1d), 3.5d));
    LOGGER.info("Grading courses.");
    gradeBuilder.add(CriterionGradeWeight.from(Criterion.given("courses.soc"),
        GradeUtils.getBestGrade(authoredHistory.graph().nodes(), this::coursesGrade, 1d), 3.5d));
    LOGGER.info("Grading number.");
    gradeBuilder.add(CriterionGradeWeight.from(Criterion.given("Number"),
        GradeUtils.getBestGrade(authoredHistory.graph().nodes(), this::numberGrade, 1d), 3.5d));
    LOGGER.info("Grading formatting.");
    gradeBuilder.add(CriterionGradeWeight.from(Criterion.given("Formatting"),
        GradeUtils.getBestGrade(authoredHistory.graph().nodes(), this::formattingGrade, 1d), 3.5d));

    return WeightingGrade.from(gradeBuilder.build());
  }

  void setHistory(GitHistorySimple history) {
    this.history = history;
    this.authoredHistory = history;
  }

  private IGrade compileGrade(Optional p) throws IOException {
    final CriterionGradeWeight c1 = CriterionGradeWeight.from(Criterion.given("Compile"),
        Mark.binary(p.isPresent() && compiles(p.get())), 7d);
    final CriterionGradeWeight c2 = CriterionGradeWeight.from(Criterion.given("Single change"),
        Mark.binary(p.isPresent() && singleChangeAbout(p.get(), "QuestioningConstraint.java")), 3d);
    return WeightingGrade.from(ImmutableList.of(c1, c2));

  }

  boolean compiles(GitPathRoot p) throws IOException {
    LOGGER.debug("Files matching.");
    final TFunction, IOException> filesMatching =
        Functions.filesMatching(isFileNamed("QuestioningConstraint.java"));
    final ImmutableSet matching = filesMatching.apply(p);
    LOGGER.debug("Files matching found: {}.", matching);
    final Pattern patternCompiles = Pattern.compile(
        ".*^(?\\h+)return[\\v\\h]+kind[\\v\\h]*;.*", Pattern.DOTALL | Pattern.MULTILINE);
    final TPredicate, IOException> singletonAndMatch =
        Predicates.singletonAndMatch(contentMatches(patternCompiles));
    final boolean tested = singletonAndMatch.test(matching);
    LOGGER.debug("Predicate known: {}.", tested);
    return tested;
    // return compose(filesMatching, singletonAndMatch).test(p);
  }

  boolean singleChangeAbout(GitPathRootShaCached p, String file) throws IOException {
    final Set predecessors = history.graph().predecessors(p);
    return (predecessors.size() == 1)
        && singleDiffAbout(Iterables.getOnlyElement(predecessors), p, file);
  }

  private boolean singleDiffAbout(GitPathRoot predecessor, GitPathRoot p, String file)
      throws IOException {
    final ImmutableSet diff = history.fs().diff(predecessor, p);
    return diff.size() == 1 && diffIsAboutFile(Iterables.getOnlyElement(diff), file);
  }

  private boolean diffIsAboutFile(DiffEntry singleDiff, String file) {
    return singleDiff.getOldPath().contains(file) && singleDiff.getNewPath().contains(file);
  }

  private IGrade warningGrade(Optional p) throws IOException {
    final CriterionGradeWeight c1 = CriterionGradeWeight.from(Criterion.given("Warning"),
        Mark.binary(p.isPresent() && warning(p.get())), 7d);
    final CriterionGradeWeight c2 = CriterionGradeWeight.from(Criterion.given("Single change"),
        Mark.binary(p.isPresent() && singleChangeAbout(p.get(), "ConstraintsOnWeights.java")), 3d);
    return WeightingGrade.from(ImmutableList.of(c1, c2));

  }

  boolean warning(GitPathRoot p) throws IOException {
    final Pattern pattern =
        Pattern.compile(".*^(?\\h+)builder[\\v\\h]*=[\\v\\h]*mp[\\v\\h]*;.*",
            Pattern.DOTALL | Pattern.MULTILINE);
    return compose(Functions.filesMatching(isFileNamed("ConstraintsOnWeights.java")),
        Predicates.singletonAndMatch(contentMatches(pattern))).test(p);
  }

  private IGrade helperGrade(Optional p) throws IOException {
    return Mark.binary(p.isPresent() && helper(p.get()));

  }

  private boolean helper(GitPathRoot p) throws IOException {
    return compose(Functions.filesMatching(isFileNamed("StrategyHelperTests.java")),
        Predicates
            .singletonAndMatch(contentMatches(Marks.extendAll("StrategyHelper[^T]")).negate()))
                .test(p);
  }

  private IGrade coursesGrade(Optional p) throws IOException {
    final CriterionGradeWeight c1 = CriterionGradeWeight.from(Criterion.given("courses.soc"),
        Mark.binary(p.isPresent() && coursesChange(p.get())), 7d);
    final CriterionGradeWeight c2 = CriterionGradeWeight.from(Criterion.given("Single change"),
        Mark.binary(p.isPresent() && singleChangeAbout(p.get(), "courses.soc")), 3d);
    return WeightingGrade.from(ImmutableList.of(c1, c2));

  }

  private boolean coursesChange(GitPathRoot p) throws IOException {
    return compose(Functions.filesMatching(isFileNamed("courses.soc")),
        Predicates
            .singletonAndMatch(contentMatches(Pattern.compile("^8[\\r\\n]1.+", Pattern.DOTALL))))
                .test(p);
  }

  private IGrade numberGrade(Optional p) throws IOException {
    final CriterionGradeWeight c1 = CriterionGradeWeight.from(Criterion.given("Number"),
        Mark.binary(p.isPresent() && numberChange(p.get())), 7d);
    final CriterionGradeWeight c2 = CriterionGradeWeight.from(Criterion.given("Single change"),
        Mark.binary(p.isPresent() && singleChangeAbout(p.get(), "Oracles m = 10, n = 6, 100.json")),
        3d);
    return WeightingGrade.from(ImmutableList.of(c1, c2));

  }

  private boolean numberChange(GitPathRoot p) throws IOException {
    return compose(Functions.filesMatching(isFileNamed("Oracles m = 10, n = 6, 100.json")),
        Predicates.singletonAndMatch(contentMatches(Marks.extendAll("0.8388174124160426"))))
            .test(p);
  }

  private IGrade formattingGrade(Optional p) throws IOException {
    return Mark.binary(p.isPresent() && formatted(p.get()));

  }

  boolean formatted(GitPathRoot p) throws IOException {
    return compose(Functions.filesMatching(isFileNamed("PreferenceInformation.java")),
        Predicates.singletonAndMatch(this::isFormatted)).test(p);
  }

  private boolean isFormatted(Path p) throws IOException {
    if (!Files.exists(p)) {
      return false;
    }
    final String content = Files.readString(p);
    final Pattern patternOne =
        Pattern.compile("(?.*)^(?\\h+)checkState\\h*\\(c\\h*==\\h*null\\);.*",
            Pattern.DOTALL | Pattern.MULTILINE);
    final Pattern patternTwo =
        Pattern.compile("(?.*)^(?\\h+)verify\\h*\\(v\\h*!=\\h*null\\);.*",
            Pattern.DOTALL | Pattern.MULTILINE);
    final Matcher matcherOne = patternOne.matcher(content);
    final Matcher matcherTwo = patternTwo.matcher(content);
    if (!matcherOne.matches() || !matcherTwo.matches()) {
      return false;
    }
    final String indentOne = matcherOne.group("indent");
    final String indentTwo = matcherTwo.group("indent");
    verify(!indentOne.isEmpty());
    verify(!indentTwo.isEmpty());
    return indentOne.equals(indentTwo);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy