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

io.github.oliviercailloux.grade.GitGeneralGrader Maven / Gradle / Ivy

The newest version!
package io.github.oliviercailloux.grade;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Verify.verify;
import static io.github.oliviercailloux.jaris.exceptions.Unchecker.IO_UNCHECKER;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import io.github.oliviercailloux.git.GitHubHistory;
import io.github.oliviercailloux.git.factory.GitCloner;
import io.github.oliviercailloux.git.filter.GitHistory;
import io.github.oliviercailloux.git.filter.GitHistorySimple;
import io.github.oliviercailloux.git.github.model.GitHubToken;
import io.github.oliviercailloux.git.github.model.GitHubUsername;
import io.github.oliviercailloux.git.github.model.RepositoryCoordinatesWithPrefix;
import io.github.oliviercailloux.git.github.services.GitHubFetcherQL;
import io.github.oliviercailloux.gitjfs.GitFileSystem;
import io.github.oliviercailloux.gitjfs.GitFileSystemProvider;
import io.github.oliviercailloux.gitjfs.GitPathRoot;
import io.github.oliviercailloux.gitjfs.GitPathRootShaCached;
import io.github.oliviercailloux.grade.format.json.JsonGrade;
import io.github.oliviercailloux.javagrade.testers.JavaMarkHelper;
import io.github.oliviercailloux.json.JsonbUtils;
import io.github.oliviercailloux.utils.Utils;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Set;
import java.util.stream.Collectors;
import name.falgout.jeffrey.throwing.stream.ThrowingStream;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * An instance of this should have a deadline and a grader.
 *
 * Then this is a grader given a GitWork: a GitOnlineUsername (renaming of GitHubUsername) and a
 * GitFileSystemHistory.
 *
 * Note that out of the repository coordinates comes the GitWork.
 */
public class GitGeneralGrader {
  @SuppressWarnings("unused")
  private static final Logger LOGGER = LoggerFactory.getLogger(GitGeneralGrader.class);

  public static GitGeneralGrader using(RepositoryFetcher repositoryFetcher,
      DeadlineGrader deadlineGrader) {
    return new GitGeneralGrader(repositoryFetcher.fetch(), deadlineGrader,
        Path.of("grades " + repositoryFetcher.getPrefix() + ".json"));
  }

  private final Set repositories;
  private boolean excludeCommitsByGitHub;
  private ImmutableSet excludedAuthors;
  private boolean fromDir;
  private final DeadlineGrader deadlineGrader;
  private Path out;

  private GitGeneralGrader(Set repositories,
      DeadlineGrader deadlineGrader, Path out) {
    this.repositories = checkNotNull(repositories);
    this.excludeCommitsByGitHub = false;
    this.excludedAuthors = ImmutableSet.of();
    this.fromDir = false;
    this.deadlineGrader = checkNotNull(deadlineGrader);
    this.out = checkNotNull(out);
  }

  public Path getOut() {
    return out;
  }

  public void setOut(Path out) {
    this.out = out;
  }

  public GitGeneralGrader setExcludeCommitsByGitHub(boolean excludeCommitsByGitHub) {
    this.excludeCommitsByGitHub = excludeCommitsByGitHub;
    return this;
  }

  public GitGeneralGrader setExcludeCommitsByAuthors(Set excludedAuthors) {
    this.excludedAuthors = ImmutableSet.copyOf(excludedAuthors);
    return this;
  }

  public GitGeneralGrader setFromDir(boolean fromDir) {
    this.fromDir = fromDir;
    return this;
  }

  public void grade() throws IOException {
    final ImmutableMap.Builder builder = ImmutableMap.builder();
    for (RepositoryCoordinatesWithPrefix repository : repositories) {
      final String username = repository.getUsername();
      final IGrade grade = grade(repository);
      builder.put(username, grade);
    }
    final ImmutableMap grades = builder.build();
    Files.writeString(out, JsonbUtils.toJsonObject(grades, JsonGrade.asAdapter()).toString());
    // Files.writeString(Path.of("grades.html"), XmlUtils.asString(HtmlGrades.asHtmlGrades(grades,
    // "Grades", 20d)));
    // final ImmutableSet unames = grades.keySet();
    // final Set stds = unames.stream().map((String u) -> StudentOnGitHub.with(u))
    // .collect(ImmutableSet.toImmutableSet());
    // Files.writeString(Path.of("grades.csv"),
    // CsvGrades.asCsv(Maps.asMap(stds, s -> grades.get(s.getGitHubUsername().getUsername()))));
    LOGGER.debug("Grades: {}.", grades);
  }

  IGrade grade(RepositoryCoordinatesWithPrefix coordinates) throws IOException {
    final Path dir = Utils.getTempDirectory().resolve(coordinates.getRepositoryName());
    try (FileRepository repository = getFileRepo(coordinates, dir);
        GitFileSystem gitFs =
            GitFileSystemProvider.instance().newFileSystemFromRepository(repository)) {
      final GitHistory pushHistory;
      {
        final GitHubHistory gitHubHistory;
        try (GitHubFetcherQL fetcher = GitHubFetcherQL.using(GitHubToken.getRealInstance())) {
          gitHubHistory = fetcher.getReversedGitHubHistory(coordinates);
        }
        if (!gitHubHistory.getPatchedPushCommits().nodes().isEmpty()) {
          LOGGER.warn("Patched: {}.", gitHubHistory.getPatchedPushCommits());
        }
        pushHistory = gitHubHistory.getConsistentPushHistory();
        verify(pushHistory.getGraph().equals(Utils.asImmutableGraph(gitFs.graph(),
            IO_UNCHECKER.wrapFunction(r -> r.getCommit().id()))));
        LOGGER.debug("Push history: {}.", pushHistory);
      }

      final GitHistorySimple history = GitHistorySimple.create(gitFs, pushHistory.getTimestamps());
      final GitWork work = GitWork.given(GitHubUsername.given(coordinates.getUsername()), history);
      return grade(work);
    }
  }

  private FileRepository getFileRepo(RepositoryCoordinatesWithPrefix coordinates, Path dir)
      throws IOException {
    if (fromDir) {
      return (FileRepository) new FileRepositoryBuilder().setWorkTree(dir.toFile()).build();
    }
    return GitCloner.create().download(coordinates.asGitUri(), dir);
  }

  IGrade grade(GitWork work) throws IOException {
    final GitHistorySimple manual;
    final ImmutableSet excludedByGitHub;
    if (excludeCommitsByGitHub) {
      final ThrowingStream stream =
          ThrowingStream.of(work.getHistory().graph().nodes().stream(), IOException.class);
      manual = work.getHistory().filteredCommits(c -> !JavaMarkHelper.committerIsGitHub(c));
      excludedByGitHub =
          stream.filter(JavaMarkHelper::committerIsGitHub).collect(ImmutableSet.toImmutableSet());
    } else {
      manual = work.getHistory();
      excludedByGitHub = ImmutableSet.of();
    }

    final GitHistorySimple filteredHistory =
        manual.filteredCommits(c -> !excludedAuthors.contains(c.authorName()));
    final IGrade grade = deadlineGrader.grade(GitWork.given(work.getAuthor(), filteredHistory));
    final String spaceBefore = grade.getComment().isEmpty() ? "" : " ";
    final String added = excludedByGitHub.isEmpty() ? ""
        : spaceBefore + "(Ignored commits by GitHub: " + excludedByGitHub.stream()
            .map(r -> r.getStaticCommitId().getName().toString()).collect(Collectors.joining(", "))
            + ")";
    return grade.withComment(grade.getComment() + added);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy