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

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

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

import static io.github.oliviercailloux.grade.GitGrader.Functions.resolve;
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.isRefBranch;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.graph.ImmutableGraph;
import io.github.oliviercailloux.git.filter.GitHistorySimple;
import io.github.oliviercailloux.gitjfs.GitPathRootRef;
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.WeightingGrader;
import io.github.oliviercailloux.grade.WeightingGrader.CriterionGraderWeight;
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.Path;
import java.time.ZonedDateTime;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class GitBranching implements GitGrader {
  @SuppressWarnings("unused")
  private static final Logger LOGGER = LoggerFactory.getLogger(GitBranching.class);

  public static final String PREFIX = "git-branching";

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

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

  GitBranching() {}

  @Override
  public WeightingGrade grade(GitWork work) throws IOException {
    final GitHistorySimple history = work.getHistory();
    final ImmutableSet refs = history.fs().refs();

    final ImmutableSet.Builder gradeBuilder = ImmutableSet.builder();

    {
      final Mark hasCommit = Mark.binary(!history.graph().nodes().isEmpty());
      gradeBuilder.add(CriterionGradeWeight.from(Criterion.given("At least one"), hasCommit, 1d));
    }

    final Function> graphSiblings =
        r -> getSingleParent(history, r).map(p -> history.graph().successors(p))
            .orElse(ImmutableSet.of(r));
    final TFunction, IOException> siblings =
        r -> graphSiblings.apply(r);
    final TPredicate hasAFewSiblings =
        compose(siblings, s -> 2 <= s.size() && s.size() <= 3);
    final CriterionGraderWeight graderTopoSiblings = CriterionGraderWeight
        .given(Criterion.given("Father has 2 or 3 children"), hasAFewSiblings, 2d);
    final ImmutableGraph graph = history.graph();
    // final TPredicate aBranch = compose(history::getRefsTo,
    // anyMatch(isBranch("br1")));
    final TPredicate aBranch =
        compose(p -> GradeUtils.getRefsTo(refs, p), anyMatch(isRefBranch("br1")));
    {
      final Pattern helloPattern = Marks.extendWhite("hello\\h+world");
      final CriterionGraderWeight graderBr1 =
          CriterionGraderWeight.given(Criterion.given("Branch br1"), aBranch, 2d);
      final CriterionGraderWeight graderContent =
          CriterionGraderWeight.given(Criterion.given("first.txt content"),
              compose(resolve("first.txt"), contentMatches(helloPattern)), 1d);

      final IGrade grade = GradeUtils.getBestGrade(graph.nodes(),
          WeightingGrader
              .getGrader(ImmutableList.of(graderTopoSiblings, graderBr1, graderContent))::getGrade,
          1d);

      final CriterionGradeWeight aGrade =
          CriterionGradeWeight.from(Criterion.given("Commit A"), grade, 5d);
      gradeBuilder.add(aGrade);
    }
    {
      final Pattern coucouPattern = Marks.extendWhite("coucou\\h+monde");
      final CriterionGraderWeight graderContent =
          CriterionGraderWeight.given(Criterion.given("first.txt content"),
              compose(resolve("first.txt"), contentMatches(coucouPattern)), 1d);

      final IGrade grade = GradeUtils.getBestGrade(graph.nodes(),
          WeightingGrader.getGrader(ImmutableList.of(graderTopoSiblings, graderContent))::getGrade,
          1d);

      final CriterionGradeWeight bGrade =
          CriterionGradeWeight.from(Criterion.given("Commit B"), grade, 3d);
      gradeBuilder.add(bGrade);
    }
    final TFunction, IOException> fatherSiblings =
        r -> getSingleParent(history, r).map(graphSiblings).orElse(ImmutableSet.of());
    final TPredicate hasCTopo =
        compose(fatherSiblings, s -> 2 <= s.size() && s.size() <= 3);
    final TPredicate cBranch =
        compose(p -> GradeUtils.getRefsTo(refs, p), anyMatch(isRefBranch("br2")));
    {
      final CriterionGraderWeight graderTopoC = CriterionGraderWeight
          .given(Criterion.given("Grand-father has 2 or 3 children"), hasCTopo, 2d);
      final CriterionGraderWeight graderBr2 =
          CriterionGraderWeight.given(Criterion.given("Branch br2"), cBranch, 2d);
      final CriterionGraderWeight graderContent = CriterionGraderWeight.given(
          Criterion.given("content"),
          compose(resolve("a/b/c/x/z/some file.txt"), contentMatches(Marks.extendWhite("2021"))),
          1d);

      final IGrade grade =
          GradeUtils.getBestGrade(graph.nodes(),
              WeightingGrader
                  .getGrader(ImmutableList.of(graderTopoC, graderBr2, graderContent))::getGrade,
              1d);

      final CriterionGradeWeight cGrade =
          CriterionGradeWeight.from(Criterion.given("Commit C"), grade, 5d);
      gradeBuilder.add(cGrade);
    }
    {
      /**
       * Topo D: has exactly two parents; one which is a C (right branch OR C Topo) and one which is
       * is an A (right branch or A topo). Thus the parents must include at least one A, at least
       * one C, and no nothing.
       */
      final TPredicate isA = aBranch.or(hasAFewSiblings);
      final TPredicate isC = cBranch.or(hasCTopo);
      final TFunction,
          IOException> parents = r -> ImmutableSet.copyOf(graph.predecessors(r));
      final TPredicate, IOException> aAndC =
          s -> s.size() == 2 && (isA.test(s.asList().get(0)) && isC.test(s.asList().get(1)));
      final TPredicate, IOException> cAndA =
          s -> s.size() == 2 && (isC.test(s.asList().get(0)) && isA.test(s.asList().get(1)));
      final TPredicate twoRightParents =
          compose(parents, aAndC.or(cAndA));
      final TPredicate dBranch =
          compose(p -> GradeUtils.getRefsTo(refs, p), anyMatch(isRefBranch("br3")));

      final CriterionGraderWeight graderTopo = CriterionGraderWeight
          .given(Criterion.given("Has apparent A and C parents"), twoRightParents, 2d);
      final CriterionGraderWeight graderBr3 =
          CriterionGraderWeight.given(Criterion.given("Branch br3"), dBranch, 2d);
      final CriterionGraderWeight graderContentSomeFile =
          CriterionGraderWeight.given(Criterion.given("some file"), compose(
              resolve("a/b/c/x/z/some file.txt"), contentMatches(Marks.extendWhite("2021"))), 1d);
      final TPredicate merged =
          contentMatches(Marks.extendAll("<<<<<<<")).negate();
      final TPredicate matchesApprox =
          contentMatches(Marks.extendAll("hello\\h+world"))
              .or(contentMatches(Marks.extendAll("coucou\\h+monde")));
      final CriterionGraderWeight graderContentFirstApprox =
          CriterionGraderWeight.given(Criterion.given("approx"),
              compose(resolve("first.txt"), matchesApprox.and(merged)), 1d);
      final CriterionGraderWeight graderContentFirstExact =
          CriterionGraderWeight.given(Criterion.given("exact"),
              compose(resolve("first.txt"),
                  contentMatches(Pattern.compile("hello world\\v+coucou monde\\v*")).and(merged)),
              1d);
      final CriterionGraderWeight graderContentFirst =
          CriterionGraderWeight.given(Criterion.given("content"),
              WeightingGrader.getGrader(
                  ImmutableList.of(graderContentFirstApprox, graderContentFirstExact))::getGrade,
              1d);
      final CriterionGraderWeight graderContent =
          CriterionGraderWeight.given(Criterion.given("content"),
              WeightingGrader
                  .getGrader(ImmutableList.of(graderContentSomeFile, graderContentFirst))::getGrade,
              2d);

      final TFunction, IGrade, IOException> grader = WeightingGrader
          .getGrader(ImmutableList.of(graderTopo, graderBr3, graderContent))::getGrade;
      final IGrade grade = GradeUtils.getBestGrade(graph.nodes(), grader, 1d);

      final CriterionGradeWeight dGrade =
          CriterionGradeWeight.from(Criterion.given("Commit D"), grade, 6d);
      gradeBuilder.add(dGrade);
    }

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

  private TPredicate, IOException>
      anyMatch(TPredicate predicate) {
    final TPredicate, IOException> anyMatchPredicate = s -> {
      for (GitPathRootRef r : s) {
        if (predicate.test(r)) {
          return true;
        }
      }
      return false;
    };
    return anyMatchPredicate;
  }

  private Optional getSingleParent(GitHistorySimple history,
      GitPathRootShaCached r) {
    final Set parents =
        history.graph().predecessors(r).stream().collect(ImmutableSet.toImmutableSet());
    if (parents.size() == 1) {
      return Optional.of(Iterables.getOnlyElement(parents));
    }
    return Optional.empty();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy