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

Add statistics API. #477

Merged
merged 12 commits into from
Oct 5, 2019
365 changes: 365 additions & 0 deletions src/main/java/org/kohsuke/github/GHRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
package org.kohsuke.github;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.lang3.StringUtils;
Expand Down Expand Up @@ -51,6 +54,7 @@
import java.util.WeakHashMap;

import static java.util.Arrays.*;
import java.util.NoSuchElementException;
import static org.kohsuke.github.Previews.*;

/**
Expand Down Expand Up @@ -1578,6 +1582,367 @@ public boolean equals(Object obj) {
}
}

/**
* Get contributors list with additions, deletions, and commit count.
* See https://developer.github.com/v3/repos/statistics/#get-contributors-list-with-additions-deletions-and-commit-counts
*/
public PagedIterable<ContributorStats> getContributorStats() throws IOException {
return new PagedIterable<ContributorStats>() {
public PagedIterator<ContributorStats> _iterator(int pageSize) {
return new PagedIterator<ContributorStats>(root.retrieve().asIterator(getApiTailUrl("stats/contributors"), ContributorStats[].class, pageSize)) {
@Override
protected void wrapUp(ContributorStats[] page) {
for (ContributorStats c : page) {
c.wrapUp(root);
}
}
};
}
};
}

public static class ContributorStats extends GHObject {
/*package almost final*/ private GitHub root;
private GHUser author;
private int total;
private List<Week> weeks;

@Override
public URL getHtmlUrl() throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}

public GitHub getRoot() {
return root;
}

/**
* @return The author described by these statistics.
*/
public GHUser getAuthor() {
return author;
}

/**
* @return The total number of commits authored by the contributor.
*/
public int getTotal() {
return total;
}

/**
* Convenience method to look up week with particular timestamp.
* @param timestamp The timestamp to look for.
* @return The week starting with the given timestamp. Throws an
* exception if it is not found.
* @throws NoSuchElementException
*/
public Week getWeek(long timestamp) throws NoSuchElementException {
// maybe store the weeks in a map to make this more efficient?
for (Week week: weeks) {
if (week.getWeekTimestamp() == timestamp) {
return week;
}
}

// this is safer than returning null
throw new NoSuchElementException();
}

/**
* @return The total number of commits authored by the contributor.
*/
public List<Week> getWeeks() {
return weeks;
}

public static class Week {
private long w;
private int a;
private int d;
private int c;

/**
* @return Start of the week, as a UNIX timestamp.
*/
public long getWeekTimestamp() {
return w;
}

/**
* @return The number of additions for the week.
*/
public int getNumberOfAdditions() {
return a;
}

/**
* @return The number of deletions for the week.
*/
public int getNumberOfDeletions() {
return d;
}

/**
* @return The number of commits for the week.
*/
public int getNumberOfCommits() {
return c;
}

@Override
public String toString() {
return String.format("Week starting %d - Additions: %d, Deletions: %d, Commits: %d", w, a, d, c);
}
}

/*package*/ ContributorStats wrapUp(GitHub root) {
this.root = root;
return this;
}
}

/**
* Get the last year of commit activity data.
* See https://developer.github.com/v3/repos/statistics/#get-the-last-year-of-commit-activity-data
*/
public PagedIterable<CommitActivity> getCommitActivity() throws IOException {
return new PagedIterable<CommitActivity>() {
public PagedIterator<CommitActivity> _iterator(int pageSize) {
return new PagedIterator<CommitActivity>(root.retrieve().asIterator(getApiTailUrl("stats/commit_activity"), CommitActivity[].class, pageSize)) {
@Override
protected void wrapUp(CommitActivity[] page) {
for (CommitActivity c : page) {
c.wrapUp(root);
}
}
};
}
};
}

public static class CommitActivity extends GHObject {
/*package almost final*/ private GitHub root;
private List<Integer> days;
private int total;
private long week;

/**
* @return The number of commits for each day of the week.
* 0 = Sunday, 1 = Monday, etc.
*/
public List<Integer> getDays() {
return days;
}

/**
* @return The total number of commits for the week.
*/
public int getTotal() {
return total;
}

/**
* @return The start of the week as a UNIX timestamp.
*/
public long getWeek() {
return week;
}

/*package*/ CommitActivity wrapUp(GitHub root) {
this.root = root;
return this;
}

public GitHub getRoot() {
return root;
}

@Override
public URL getHtmlUrl() throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
}

/**
* Get the number of additions and deletions per week.
* See https://developer.github.com/v3/repos/statistics/#get-the-number-of-additions-and-deletions-per-week
*/
public List<CodeFrequency> getCodeFrequency() throws IOException {
// Map to ArrayLists first, since there are no field names in the
// returned JSON.
try {
InputStream stream = root.retrieve().asStream(getApiTailUrl("stats/code_frequency"));

ObjectMapper mapper = new ObjectMapper();
TypeReference<ArrayList<ArrayList<Integer> > > typeRef =
new TypeReference<ArrayList< ArrayList<Integer> > >() {};
ArrayList<ArrayList <Integer> > list = mapper.readValue(stream, typeRef);

// Convert to proper objects.
ArrayList<CodeFrequency> returnList = new ArrayList<CodeFrequency>();
for(ArrayList<Integer> item: list)
{
CodeFrequency cf = new CodeFrequency(item);
returnList.add(cf);
}

return returnList;
} catch (MismatchedInputException e) {
// This sometimes happens when retrieving code frequency statistics
// for a repository for the first time. It is probably still being
// generated, so return null.
return null;
}
}

public static class CodeFrequency {
private int week;
private int additions;
private int deletions;

private CodeFrequency(ArrayList<Integer> item) {
week = item.get(0);
additions = item.get(1);
deletions = item.get(2);
}

/**
* @return The start of the week as a UNIX timestamp.
*/
public int getWeekTimestamp() {
return week;
}

/**
* @return The number of additions for the week.
*/
public long getAdditions() {
return additions;
}

/**
* @return The number of deletions for the week.
* NOTE: This will be a NEGATIVE number.
*/
public long getDeletions() {
return deletions;
}

@Override
public String toString() {
return "Week starting " + getWeekTimestamp() + " has " + getAdditions() +
" additions and " + Math.abs(getDeletions()) + " deletions";
}
}

/**
* Get the weekly commit count for the repository owner and everyone else.
* See https://developer.github.com/v3/repos/statistics/#get-the-weekly-commit-count-for-the-repository-owner-and-everyone-else
*/
public Participation getParticipation() throws IOException {
return root.retrieve().to(getApiTailUrl("stats/participation"), Participation.class);
}

public static class Participation extends GHObject {
/*package almost final*/ private GitHub root;
private List<Integer> all;
private List<Integer> owner;

@Override
public URL getHtmlUrl() throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}

public GitHub getRoot() {
return root;
}

/**
* @return The list of commit counts for everyone combined, for the
* last 52 weeks.
*/
public List<Integer> getAllCommits() {
return all;
}

/**
* @return The list of commit counts for the owner, for the
* last 52 weeks.
*/
public List<Integer> getOwnerCommits() {
return owner;
}

/*package*/ Participation wrapUp(GitHub root) {
this.root = root;
return this;
}
}

/**
* Get the number of commits per hour in each day.
* See https://developer.github.com/v3/repos/statistics/#get-the-number-of-commits-per-hour-in-each-day
*/
public List<PunchCardItem> getPunchCard() throws IOException {
// Map to ArrayLists first, since there are no field names in the
// returned JSON.
InputStream stream = root.retrieve().asStream(getApiTailUrl("stats/punch_card"));

ObjectMapper mapper = new ObjectMapper();
TypeReference<ArrayList<ArrayList<Integer> > > typeRef =
new TypeReference<ArrayList< ArrayList<Integer> > >() {};
ArrayList<ArrayList <Integer> > list = mapper.readValue(stream, typeRef);

// Convert to proper objects.
ArrayList<PunchCardItem> returnList = new ArrayList<PunchCardItem>();
for(ArrayList<Integer> item: list) {
PunchCardItem pci = new PunchCardItem(item);
returnList.add(pci);
}

return returnList;
}

public static class PunchCardItem {
private int dayOfWeek;
private int hourOfDay;
private int numberOfCommits;

private PunchCardItem(ArrayList<Integer> item) {
dayOfWeek = item.get(0);
hourOfDay = item.get(1);
numberOfCommits = item.get(2);
}

/**
* @return The day of the week.
* 0 = Sunday, 1 = Monday, etc.
*/
public int getDayOfWeek() {
return dayOfWeek;
}

/**
* @return The hour of the day from 0 to 23.
*/
public long getHourOfDay() {
return hourOfDay;
}

/**
* @return The number of commits for the day and hour.
*/
public long getNumberOfCommits() {
return numberOfCommits;
}

public String toString() {
return "Day " + getDayOfWeek() + " Hour " + getHourOfDay() + ": " +
getNumberOfCommits() + " commits";
}
}

/**
* Render a Markdown document.
*
Expand Down
Loading