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

feat(spoon): Seperated Qodana and Spoon mining #814

Merged
merged 1 commit into from
Jul 10, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.github.martinwitt.laughing_train.domain.entity;

import java.io.Serializable;

public class AnalyzerStatus implements Serializable {

private String analyzerName;
private Status status;
private int numberOfIssues;

AnalyzerStatus(String analyzerName, Status status, int numberOfIssues) {
this.analyzerName = analyzerName;
this.status = status;
this.numberOfIssues = numberOfIssues;
}

public static AnalyzerStatus success(String analyzerName, int numberOfIssues) {
return new AnalyzerStatus(analyzerName, Status.SUCCESS, numberOfIssues);
}

public static AnalyzerStatus failure(String analyzerName, int numberOfIssues) {
return new AnalyzerStatus(analyzerName, Status.FAILURE, numberOfIssues);
}

enum Status {
SUCCESS,
FAILURE
}

/**
* @return the analyzerName
*/
public String getAnalyzerName() {
return analyzerName;
}

/**
* @param analyzerName the analyzerName to set
*/
public void setAnalyzerName(String analyzerName) {
this.analyzerName = analyzerName;
}

/**
* @return the status
*/
public Status getStatus() {
return status;
}

/**
* @param status the status to set
*/
public void setStatus(Status status) {
this.status = status;
}

/**
* @return the numberOfIssues
*/
public int getNumberOfIssues() {
return numberOfIssues;
}

/**
* @param numberOfIssues the numberOfIssues to set
*/
public void setNumberOfIssues(int numberOfIssues) {
this.numberOfIssues = numberOfIssues;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.github.martinwitt.laughing_train.domain.entity;

import java.io.Serializable;
import java.util.List;

public class GitHubCommit implements Serializable {

private String commitHash;
private List<AnalyzerStatus> analyzerStatuses;

/**
* @param commitHash
* @param localDateTime
* @param analyzerStatuses
*/
public GitHubCommit(String commitHash, List<AnalyzerStatus> analyzerStatuses) {
this.commitHash = commitHash;
this.analyzerStatuses = analyzerStatuses;
}
/**
* @return the commitHash
*/
public String getCommitHash() {
return commitHash;
}
/**
* @param commitHash the commitHash to set
*/
public void setCommitHash(String commitHash) {
this.commitHash = commitHash;
}

/**
* @return the analyzerStatuses
*/
public List<AnalyzerStatus> getAnalyzerStatuses() {
return analyzerStatuses;
}
/**
* @param analyzerStatuses the analyzerStatuses to set
*/
public void setAnalyzerStatuses(List<AnalyzerStatus> analyzerStatuses) {
this.analyzerStatuses = analyzerStatuses;
}

public void addAnalyzerStatus(AnalyzerStatus analyzerStatus) {
this.analyzerStatuses.add(analyzerStatus);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ public class Project implements Serializable {
private String projectName;
private String projectUrl;
private List<String> commitHashes;
private List<GitHubCommit> commits;

public Project(String projectName, String projectUrl) {
this.projectName = Objects.requireNonNull(projectName);
this.projectUrl = Objects.requireNonNull(projectUrl);
commitHashes = new ArrayList<>();
commits = new ArrayList<>();
}

/**
Expand Down Expand Up @@ -44,6 +46,17 @@ public boolean removeCommitHash(String commitHash) {
return commitHashes.remove(commitHash);
}

/**
* @return the commits
*/
public List<GitHubCommit> getCommits() {
return commits;
}

public boolean addCommitHash(GitHubCommit commit) {
return commits.add(commit);
}

/**
* @return the commitHashes
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import io.github.martinwitt.laughing_train.api.graphql.dto.ProjectGraphQLDto;
import io.github.martinwitt.laughing_train.domain.entity.Project;
import io.github.martinwitt.laughing_train.domain.entity.ProjectConfig;
import io.github.martinwitt.laughing_train.mining.PeriodicMiner;
import io.github.martinwitt.laughing_train.mining.QodanaPeriodicMiner;
import io.github.martinwitt.laughing_train.persistence.repository.ProjectConfigRepository;
import io.github.martinwitt.laughing_train.persistence.repository.ProjectRepository;
import io.github.martinwitt.laughing_train.services.ProjectConfigService;
Expand Down Expand Up @@ -40,7 +40,7 @@ public class ProjectGraphQL {
ProjectConfigRepository projectConfigRepository;

@Inject
PeriodicMiner periodicMiner;
QodanaPeriodicMiner periodicMiner;

@Query("getProjects")
@Description("Gets all projects from the database")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@
import com.google.common.flogger.FluentLogger;
import io.github.martinwitt.laughing_train.data.ProjectRequest;
import io.github.martinwitt.laughing_train.data.ProjectResult;
import io.github.martinwitt.laughing_train.data.ProjectResult.Success;
import io.github.martinwitt.laughing_train.data.QodanaResult;
import io.github.martinwitt.laughing_train.data.request.AnalyzerRequest;
import io.github.martinwitt.laughing_train.data.result.CodeAnalyzerResult;
import io.github.martinwitt.laughing_train.domain.entity.AnalyzerResult;
import io.github.martinwitt.laughing_train.domain.entity.AnalyzerStatus;
import io.github.martinwitt.laughing_train.domain.entity.GitHubCommit;
import io.github.martinwitt.laughing_train.domain.entity.Project;
import io.github.martinwitt.laughing_train.persistence.repository.ProjectRepository;
import io.github.martinwitt.laughing_train.services.ProjectService;
import io.github.martinwitt.laughing_train.services.QodanaService;
import io.github.martinwitt.laughing_train.services.SpoonAnalyzerService;
import io.micrometer.core.instrument.MeterRegistry;
import io.quarkus.runtime.StartupEvent;
import io.vertx.core.Vertx;
Expand All @@ -30,39 +29,37 @@
import org.kohsuke.github.GitHub;

@ApplicationScoped
public class PeriodicMiner {
public class QodanaPeriodicMiner {

static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final String ANALYZER_NAME = "Qodana";

final MiningPrinter miningPrinter;
final Vertx vertx;
final SearchProjectService searchProjectService;
final ProjectRepository projectRepository;
final QodanaService qodanaService;
final ProjectService projectService;
final SpoonAnalyzerService spoonAnalyzerService;
MeterRegistry registry;

private final Random random = new Random();
private Queue<Project> queue = new ArrayDeque<>();

public PeriodicMiner(
public QodanaPeriodicMiner(
MeterRegistry registry,
Vertx vertx,
SearchProjectService searchProjectService,
ProjectRepository projectRepository,
QodanaService qodanaService,
ProjectService projectService,
MiningPrinter miningPrinter,
SpoonAnalyzerService spoonAnalyzerService) {
MiningPrinter miningPrinter) {
this.registry = registry;
this.vertx = vertx;
this.searchProjectService = searchProjectService;
this.projectRepository = projectRepository;
this.qodanaService = qodanaService;
this.projectService = projectService;
this.miningPrinter = miningPrinter;
this.spoonAnalyzerService = spoonAnalyzerService;
}

private Project getRandomProject() throws IOException {
Expand Down Expand Up @@ -91,43 +88,27 @@ private void mineRandomRepo() {
mineRandomRepo();
return;
}

if (checkoutResult instanceof ProjectResult.Success success) {
String commitHash = success.project().commitHash();
if (isAlreadyMined(success, commitHash)) {
if (isAlreadyMined(success, commitHash, ANALYZER_NAME)) {
logger.atInfo().log(
"Project %s already analyzed with commit hash %s", success.project(), commitHash);
tryDeleteProject(success);
mineRandomRepo();
}
logger.atInfo().log("Successfully checked out project %s", success.project());
var qodanaResult = analyzeProject(success);
var spoonResult = analyzeProjectWithSpoon(success);
List<AnalyzerResult> results = new ArrayList<>();
if (qodanaResult instanceof QodanaResult.Failure failure) {
logger.atWarning().log("Failed to analyze project %s", failure.message());
tryDeleteProject(success);
}
if (spoonResult instanceof CodeAnalyzerResult.Failure error) {
logger.atWarning().log("Failed to analyze project with spoon %s", error.message());
tryDeleteProject(success);
}
if (spoonResult instanceof CodeAnalyzerResult.Success successResult) {
results.addAll(successResult.results());
}
if (qodanaResult instanceof QodanaResult.Success successResult) {
logger.atInfo().log("Successfully analyzed project %s", success.project());
results.addAll(successResult.result());
}
if (results.isEmpty()) {
logger.atWarning().log("No results for project %s", success.project());
saveResults(results, project);
tryDeleteProject(success);
mineRandomRepo();
return;
}
saveResults(results, project);
addOrUpdateCommitHash(success);
tryDeleteProject(success);
addOrUpdateCommitHash(success, qodanaResult);
}
} catch (Exception e) {
logger.atWarning().withCause(e).log("Failed to mine random repo");
Expand All @@ -139,18 +120,6 @@ private void mineRandomRepo() {
}
}

private CodeAnalyzerResult analyzeProjectWithSpoon(Success success) {
logger.atInfo().log("Analyzing project %s with spoon", success.project());
CodeAnalyzerResult analyze = spoonAnalyzerService.analyze(new AnalyzerRequest.WithProject(success.project()));
logger.atInfo().log("Successfully analyzed project %s with spoon", success.project());
return analyze;
}

private boolean isAlreadyMined(ProjectResult.Success success, String commitHash) {
return projectRepository.findByProjectUrl(success.project().url()).stream()
.anyMatch(it -> !it.getCommitHashes().contains(commitHash));
}

private ProjectResult checkoutProject(Project project) throws IOException {
return projectService.handleProjectRequest(new ProjectRequest.WithUrl(project.getProjectUrl()));
}
Expand All @@ -168,22 +137,6 @@ private void tryDeleteProject(ProjectResult.Success project) {
}
}

private void addOrUpdateCommitHash(ProjectResult.Success projectResult) {
String name = projectResult.project().name();
String commitHash = projectResult.project().commitHash();
var list = projectRepository.findByProjectName(name);
if (list.isEmpty()) {
Project newProject = new Project(name, projectResult.project().url());
newProject.addCommitHash(commitHash);
projectRepository.create(newProject);
} else {
logger.atInfo().log("Updating commit hash for %s", name);
var oldProject = list.get(0);
oldProject.addCommitHash(commitHash);
projectRepository.save(oldProject);
}
}

private void saveResults(List<AnalyzerResult> results, Project project) {
try {
String content = printFormattedResults(project, results);
Expand Down Expand Up @@ -227,4 +180,52 @@ public void addToQueue(Project project) {
}
queue.add(project);
}

private void addOrUpdateCommitHash(ProjectResult.Success projectResult, QodanaResult spoonResult) {
String name = projectResult.project().name();
String commitHash = projectResult.project().commitHash();
var list = projectRepository.findByProjectUrl(projectResult.project().url());
AnalyzerStatus analyzerStatus = getAnalyzerStatus(spoonResult);
if (list.isEmpty()) {
Project newProject = new Project(name, projectResult.project().url());
newProject.addCommitHash(commitHash);
var commits = newProject.getCommits();
commits.stream()
.filter(v -> v.getCommitHash().equals(commitHash))
.findFirst()
.ifPresent(v -> {
v.addAnalyzerStatus(analyzerStatus);
});
projectRepository.create(newProject);
} else {
logger.atInfo().log("Updating commit hash for %s", name);
var oldProject = list.get(0);
oldProject.addCommitHash(commitHash);
var commits = oldProject.getCommits();
GitHubCommit gitHubCommit = new GitHubCommit(commitHash, new ArrayList<>());
commits.add(gitHubCommit);
gitHubCommit.addAnalyzerStatus(analyzerStatus);
oldProject.addCommitHash(gitHubCommit);
projectRepository.save(oldProject);
}
}

private AnalyzerStatus getAnalyzerStatus(QodanaResult spoonResult) {
AnalyzerStatus analyzerStatus = null;
if (spoonResult instanceof QodanaResult.Success success) {
analyzerStatus =
AnalyzerStatus.success(ANALYZER_NAME, success.result().size());
} else if (spoonResult instanceof QodanaResult.Failure failure) {
analyzerStatus = AnalyzerStatus.failure(ANALYZER_NAME, 0);
}
return analyzerStatus;
}

private boolean isAlreadyMined(ProjectResult.Success success, String commitHash, String analyzerName) {
return projectRepository.findByProjectUrl(success.project().url()).stream()
.flatMap(v -> v.getCommits().stream())
.filter(v -> v.getCommitHash().equals(commitHash))
.flatMap(v -> v.getAnalyzerStatuses().stream())
.anyMatch(v -> v.getAnalyzerName().equals(analyzerName));
}
}
Loading