From 9d955d252a5fd697d5755e3b4fb3e2b2701f4651 Mon Sep 17 00:00:00 2001 From: Martin van Zijl Date: Thu, 15 Nov 2018 11:36:05 +1300 Subject: [PATCH] Add statistics API. Fixes issue #330 --- .../java/org/kohsuke/github/GHRepository.java | 326 ++++++++++++++++++ .../java/org/kohsuke/github/Requester.java | 7 + 2 files changed, 333 insertions(+) diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java index a937836b27..d06b808b58 100644 --- a/src/main/java/org/kohsuke/github/GHRepository.java +++ b/src/main/java/org/kohsuke/github/GHRepository.java @@ -24,6 +24,8 @@ 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.infradna.tool.bridge_method_injector.WithBridgeMethods; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.apache.commons.lang3.StringUtils; @@ -1578,6 +1580,330 @@ 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 getContributorStats() throws IOException { + return new PagedIterable() { + public PagedIterator _iterator(int pageSize) { + return new PagedIterator(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 weeks; + + @Override + public URL getHtmlUrl() throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + + public GitHub getRoot() { + return root; + } + + public GHUser getAuthor() { + return author; + } + + /** + * @return The total number of commits authored by the contributor. + */ + public int getTotal() { + return total; + } + + public List 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; + } + } + + /*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 getCommitActivity() throws IOException { + return new PagedIterable() { + public PagedIterator _iterator(int pageSize) { + return new PagedIterator(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 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 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 getCodeFrequency() throws IOException { + // Map to ArrayLists first, since there are no field names in the + // returned JSON. + InputStream stream = root.retrieve().asStream(getApiTailUrl("stats/code_frequency")); + + ObjectMapper mapper = new ObjectMapper(); + TypeReference > > typeRef = + new TypeReference > >() {}; + ArrayList > list = mapper.readValue(stream, typeRef); + + // Convert to proper objects. + ArrayList returnList = new ArrayList(); + for(ArrayList item: list) + { + CodeFrequency cf = new CodeFrequency(item); + returnList.add(cf); + } + + return returnList; + } + + public static class CodeFrequency { + private int week; + private int additions; + private int deletions; + + private CodeFrequency(ArrayList 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 all; + private List 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 getAllCommits() { + return all; + } + + /** + * @return The list of commit counts for the owner, for the + * last 52 weeks. + */ + public List 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 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 > > typeRef = + new TypeReference > >() {}; + ArrayList > list = mapper.readValue(stream, typeRef); + + // Convert to proper objects. + ArrayList returnList = new ArrayList(); + for(ArrayList 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 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. * diff --git a/src/main/java/org/kohsuke/github/Requester.java b/src/main/java/org/kohsuke/github/Requester.java index 4670e311e5..ff8f12243d 100644 --- a/src/main/java/org/kohsuke/github/Requester.java +++ b/src/main/java/org/kohsuke/github/Requester.java @@ -622,6 +622,13 @@ private T parse(Class type, T instance, int timeouts) throws IOException return type.cast(Array.newInstance(type.getComponentType(),0)); } + // Response code 202 means the statistics are still being cached. + // See https://developer.github.com/v3/repos/statistics/#a-word-about-caching + if (responseCode == 202) { + LOGGER.log(INFO, "The statistics are still being generated. Please try again in 5 seconds."); + return null; + } + r = new InputStreamReader(wrapStream(uc.getInputStream()), "UTF-8"); String data = IOUtils.toString(r); if (type!=null)