From 45c0d6ed8c9c3afc4d09200358ee2d53f06023e2 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Wed, 20 Apr 2011 07:53:15 -0400
Subject: [PATCH] Tag diamonds and sliding date scale on activity graph.

---
 contrib/com/codecommit/wicket/ShapeMarker.java  |    6 +
 src/com/gitblit/wicket/pages/SummaryPage.html   |   13 +-
 src/com/gitblit/utils/TimeUtils.java            |   34 ++++++++
 gitblit.properties                              |    6 
 src/com/gitblit/wicket/GitBlitWebApp.properties |    3 
 contrib/com/codecommit/wicket/MarkerType.java   |    4 
 src/com/gitblit/tests/JGitUtilsTest.java        |    8 ++
 src/com/gitblit/wicket/models/Metric.java       |    2 
 src/com/gitblit/wicket/pages/SummaryPage.java   |   46 +++++++---
 contrib/com/codecommit/wicket/LineStyle.java    |    7 +
 src/com/gitblit/utils/JGitUtils.java            |   79 +++++++++++++++++--
 11 files changed, 169 insertions(+), 39 deletions(-)

diff --git a/contrib/com/codecommit/wicket/LineStyle.java b/contrib/com/codecommit/wicket/LineStyle.java
index 0b1eade..ef53c5b 100644
--- a/contrib/com/codecommit/wicket/LineStyle.java
+++ b/contrib/com/codecommit/wicket/LineStyle.java
@@ -3,10 +3,15 @@
  */
 package com.codecommit.wicket;
 
+import java.io.Serializable;
+
 /**
  * @author Daniel Spiewak
  */
-public class LineStyle implements ILineStyle {
+public class LineStyle implements ILineStyle, Serializable {
+
+	private static final long serialVersionUID = 1L;
+	
 	private int blankLength = -1;
 	private int segmentLength = -1;
 	private int thickness = -1;
diff --git a/contrib/com/codecommit/wicket/MarkerType.java b/contrib/com/codecommit/wicket/MarkerType.java
index d14ff54..2b22794 100644
--- a/contrib/com/codecommit/wicket/MarkerType.java
+++ b/contrib/com/codecommit/wicket/MarkerType.java
@@ -3,10 +3,12 @@
  */
 package com.codecommit.wicket;
 
+import java.io.Serializable;
+
 /**
  * @author Daniel Spiewak
  */
-public enum MarkerType {
+public enum MarkerType implements Serializable {
 	ARROW("a"),
 	CROSS("c"),
 	DIAMOND("d"),
diff --git a/contrib/com/codecommit/wicket/ShapeMarker.java b/contrib/com/codecommit/wicket/ShapeMarker.java
index 88d1aa1..3b42cfb 100644
--- a/contrib/com/codecommit/wicket/ShapeMarker.java
+++ b/contrib/com/codecommit/wicket/ShapeMarker.java
@@ -4,11 +4,15 @@
 package com.codecommit.wicket;
 
 import java.awt.Color;
+import java.io.Serializable;
 
 /**
  * @author Daniel Spiewak
  */
-public class ShapeMarker implements IShapeMarker {
+public class ShapeMarker implements IShapeMarker, Serializable {
+	
+	private static final long serialVersionUID = 1L;
+	
 	private Color color;
 	private int index = -1;
 	private double point = -1;
diff --git a/gitblit.properties b/gitblit.properties
index a81e5f6..27a9fff 100644
--- a/gitblit.properties
+++ b/gitblit.properties
@@ -65,7 +65,7 @@
 
 # The number of commits to display on the summary page
 # Value must exceed 0 else default of 20 is used
-web.summaryCommitCount = 20
+web.summaryCommitCount = 16
 
 # The number of tags/heads to display on the summary page
 # Value must exceed 0 else default of 5 is used
@@ -130,11 +130,11 @@
 
 # Specify the interface for Jetty to bind the standard connector.
 # You may specify an ip or an empty value to bind to all interfaces. 
-server.httpBindInterface = 
+server.httpBindInterface = localhost
 
 # Specify the interface for Jetty to bind the secure connector.
 # You may specify an ip or an empty value to bind to all interfaces.
-server.httpsBindInterface = 
+server.httpsBindInterface = localhost
 
 # Password for SSL keystore (keystore password and certificate password must match)
 server.storePassword = dosomegit
diff --git a/src/com/gitblit/tests/JGitUtilsTest.java b/src/com/gitblit/tests/JGitUtilsTest.java
index 0ebcd71..f3c798f 100644
--- a/src/com/gitblit/tests/JGitUtilsTest.java
+++ b/src/com/gitblit/tests/JGitUtilsTest.java
@@ -50,6 +50,14 @@
 		r.close();
 		assertTrue("Could not get last repository change date!", date != null);
 	}
+	
+	public void testFirstCommit() throws Exception {
+		Repository r = getRepository();
+		RevCommit commit = JGitUtils.getFirstCommit(r, null);
+		r.close();
+		assertTrue("Could not get first commit!", commit != null);
+		System.out.println(commit.getName() + " " + commit.getShortMessage());
+	}
 
 	public void testRetrieveRevObject() throws Exception {
 		Repository r = getRepository();
diff --git a/src/com/gitblit/utils/JGitUtils.java b/src/com/gitblit/utils/JGitUtils.java
index b126768..daeb638 100644
--- a/src/com/gitblit/utils/JGitUtils.java
+++ b/src/com/gitblit/utils/JGitUtils.java
@@ -36,6 +36,7 @@
 import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevSort;
 import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.revwalk.filter.RevFilter;
@@ -99,6 +100,34 @@
 			}
 		}
 		return list;
+	}
+
+	public static RevCommit getFirstCommit(Repository r, String branch) {
+		if (StringUtils.isEmpty(branch)) {
+			branch = Constants.HEAD;
+		}
+		try {
+			RevWalk walk = new RevWalk(r);
+			walk.sort(RevSort.REVERSE);
+			RevCommit head = walk.parseCommit(r.resolve(branch));
+			walk.markStart(head);
+			RevCommit commit = walk.next();
+			walk.dispose();
+			return commit;
+		} catch (Throwable t) {
+			LOGGER.error("Failed to determine first commit", t);
+		}
+		return null;
+	}
+
+	public static Date getFirstChange(Repository r, String branch) {
+		try {
+			RevCommit commit = getFirstCommit(r, branch);
+			return getCommitDate(commit);
+		} catch (Throwable t) {
+			LOGGER.error("Failed to determine first change", t);
+		}
+		return null;
 	}
 
 	public static Date getLastChange(Repository r) {
@@ -525,7 +554,7 @@
 			}
 			return null;
 		}
-		
+
 		public String toString() {
 			return name().toLowerCase();
 		}
@@ -552,7 +581,7 @@
 					case AUTHOR:
 						return (commit.getAuthorIdent().getName().toLowerCase().indexOf(lcValue) > -1) || (commit.getAuthorIdent().getEmailAddress().toLowerCase().indexOf(lcValue) > -1);
 					case COMMITTER:
-						return (commit.getCommitterIdent().getName().toLowerCase().indexOf(lcValue) > -1)|| (commit.getCommitterIdent().getEmailAddress().toLowerCase().indexOf(lcValue) > -1);
+						return (commit.getCommitterIdent().getName().toLowerCase().indexOf(lcValue) > -1) || (commit.getCommitterIdent().getEmailAddress().toLowerCase().indexOf(lcValue) > -1);
 					case COMMIT:
 						return commit.getFullMessage().toLowerCase().indexOf(lcValue) > -1;
 					}
@@ -695,29 +724,57 @@
 
 	public static List<Metric> getDateMetrics(Repository r) {
 		final List<RefModel> tags = getTags(r, -1);
-		final Map<String, Metric> map = new HashMap<String, Metric>();
+		final Map<ObjectId, RefModel> tagMap = new HashMap<ObjectId, RefModel>();
+		for (RefModel tag : tags) {
+			tagMap.put(tag.getCommitId(), tag);
+		}
+		Metric total = new Metric("TOTAL");
+		final Map<String, Metric> metricMap = new HashMap<String, Metric>();
 		try {
-			DateFormat df = new SimpleDateFormat("yyyy-MM");
 			RevWalk walk = new RevWalk(r);
 			ObjectId object = r.resolve(Constants.HEAD);
-			walk.markStart(walk.parseCommit(object));
+
+			RevCommit firstCommit = getFirstCommit(r, Constants.HEAD);
+			RevCommit lastCommit = walk.parseCommit(object);
+			int diffDays = (lastCommit.getCommitTime() - firstCommit.getCommitTime()) / (60 * 60 * 24);
+			total.duration = diffDays;
+			DateFormat df;
+			if (diffDays <= 90) {
+				// Days
+				df = new SimpleDateFormat("yyyy-MM-dd");
+			} else if (diffDays > 90 && diffDays < 365) {
+				// Weeks
+				df = new SimpleDateFormat("yyyy-MM (w)");
+			} else {
+				// Months
+				df = new SimpleDateFormat("yyyy-MM");
+			}
+			walk.markStart(lastCommit);
+			
 			Iterable<RevCommit> revlog = walk;
 			for (RevCommit rev : revlog) {
 				Date d = getCommitDate(rev);
 				String p = df.format(d);
-				if (!map.containsKey(p))
-					map.put(p, new Metric(p));
-				map.get(p).count++;
-			}
+				if (!metricMap.containsKey(p))
+					metricMap.put(p, new Metric(p));
+				Metric m = metricMap.get(p); 
+				m.count++;
+				total.count++;
+				if (tagMap.containsKey(rev.getId())) {
+					m.tag++;
+					total.tag++;
+				}
+			}			
 		} catch (Throwable t) {
 			LOGGER.error("Failed to mine log history for metrics", t);
 		}
-		List<String> keys = new ArrayList<String>(map.keySet());
+		List<String> keys = new ArrayList<String>(metricMap.keySet());
 		Collections.sort(keys);
 		List<Metric> metrics = new ArrayList<Metric>();
 		for (String key : keys) {
-			metrics.add(map.get(key));
+			metrics.add(metricMap.get(key));
 		}
+		metrics.add(0, total);
 		return metrics;
 	}
 
diff --git a/src/com/gitblit/utils/TimeUtils.java b/src/com/gitblit/utils/TimeUtils.java
index 60b525b..c4e5b59 100644
--- a/src/com/gitblit/utils/TimeUtils.java
+++ b/src/com/gitblit/utils/TimeUtils.java
@@ -23,6 +23,40 @@
 		return now.getDate() == (date.getDate() + 1) && now.getMonth() == date.getMonth() && now.getYear() == date.getYear();
 	}
 
+	public static String duration(int days) {
+		if (days <= 60) {
+			return days + (days > 1 ? " days" : " day");
+		} else if (days <= 365) {
+			int rem = days % 30;
+			return (days / 30) + " months, " + rem + (rem > 1 ? " days" : " day");
+		} else {
+			int years = days / 365;
+			int rem = days % 365;
+			String yearsString = years + (years > 1 ? " years" : " year");
+			if (rem < 30) {
+				if (rem == 0) {
+					return yearsString;
+				} else {
+					return yearsString + ", " + rem + (rem > 1 ? " days" : " day");
+				}
+			} else {
+				int months = rem / 30;
+				int remDays = (rem % 30);
+				String monthsString;
+				if (months == 0) {
+					monthsString = yearsString;
+				} else {
+					monthsString = yearsString + ", " + months + (months > 1 ? " months" : " month");
+				}
+				if (remDays == 0) {
+					return  monthsString;
+				} else {
+					return monthsString + ", " + remDays + (remDays > 1 ? " days":" day");
+				}
+			}
+		}
+	}
+
 	public static int minutesAgo(Date date, long endTime, boolean roundup) {
 		long diff = endTime - date.getTime();
 		int mins = (int) (diff / min);
diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties
index b8d31ae..f063858 100644
--- a/src/com/gitblit/wicket/GitBlitWebApp.properties
+++ b/src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -54,4 +54,5 @@
 gb.addition = addition
 gb.modification = modification
 gb.deletion = deletion
-gb.rename = rename
\ No newline at end of file
+gb.rename = rename
+gb.stats = stats
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/models/Metric.java b/src/com/gitblit/wicket/models/Metric.java
index a5efb13..194e265 100644
--- a/src/com/gitblit/wicket/models/Metric.java
+++ b/src/com/gitblit/wicket/models/Metric.java
@@ -8,6 +8,8 @@
 
 	public String name;
 	public double count;
+	public double tag;
+	public int duration;
 
 	public Metric(String name) {
 		this.name = name;
diff --git a/src/com/gitblit/wicket/pages/SummaryPage.html b/src/com/gitblit/wicket/pages/SummaryPage.html
index 47c10b3..6abef54 100644
--- a/src/com/gitblit/wicket/pages/SummaryPage.html
+++ b/src/com/gitblit/wicket/pages/SummaryPage.html
@@ -12,17 +12,18 @@
 	
 	<div style="clear:both;">
 		<!-- Repository Activity Chart -->	
-		<div style="width:400px;float:right;">
+		<div style="float:right;">
 			<img class="activityGraph" wicket:id="commitsChart" />
 		</div>	
 	
 		<!-- Repository info -->
-		<div style="margin-right:410px;"> 
+		<div> 
 			<table class="plain">
-				<tr><th><wicket:message key="gb.description">description</wicket:message></th><td><span wicket:id="repositoryDescription">[repository description]</span></td></tr>
-				<tr><th><wicket:message key="gb.owner">owner</wicket:message></th><td><span wicket:id="repositoryOwner">[repository owner]</span></td></tr>
-				<tr><th><wicket:message key="gb.lastChange">last change</wicket:message></th><td><span wicket:id="repositoryLastChange">[repository last change]</span></td></tr>
-				<tr><th><wicket:message key="gb.url">URL</wicket:message></th><td><span wicket:id="repositoryCloneUrl">[repository clone url]</span></td></tr>
+				<tr><th><wicket:message key="gb.description">[description]</wicket:message></th><td><span wicket:id="repositoryDescription">[repository description]</span></td></tr>
+				<tr><th><wicket:message key="gb.owner">[owner]</wicket:message></th><td><span wicket:id="repositoryOwner">[repository owner]</span></td></tr>
+				<tr><th><wicket:message key="gb.lastChange">[last change]</wicket:message></th><td><span wicket:id="repositoryLastChange">[repository last change]</span></td></tr>
+				<tr><th><wicket:message key="gb.stats">[stats]</wicket:message></th><td><span wicket:id="repositoryStats">[repository stats]</span></td></tr>
+				<tr><th><wicket:message key="gb.url">[URL]</wicket:message></th><td><span wicket:id="repositoryCloneUrl">[repository clone url]</span></td></tr>
 			</table>
 		</div>
 	</div>
diff --git a/src/com/gitblit/wicket/pages/SummaryPage.java b/src/com/gitblit/wicket/pages/SummaryPage.java
index 6d28df6..53a684b 100644
--- a/src/com/gitblit/wicket/pages/SummaryPage.java
+++ b/src/com/gitblit/wicket/pages/SummaryPage.java
@@ -1,6 +1,8 @@
 package com.gitblit.wicket.pages;
 
+import java.awt.Color;
 import java.awt.Dimension;
+import java.text.MessageFormat;
 import java.util.List;
 
 import org.apache.wicket.PageParameters;
@@ -15,9 +17,13 @@
 import com.codecommit.wicket.ChartProvider;
 import com.codecommit.wicket.ChartType;
 import com.codecommit.wicket.IChartData;
+import com.codecommit.wicket.LineStyle;
+import com.codecommit.wicket.MarkerType;
+import com.codecommit.wicket.ShapeMarker;
 import com.gitblit.GitBlit;
 import com.gitblit.Keys;
 import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.TimeUtils;
 import com.gitblit.wicket.RepositoryPage;
 import com.gitblit.wicket.WicketUtils;
 import com.gitblit.wicket.models.Metric;
@@ -44,11 +50,11 @@
 		}
 
 		Repository r = getRepository();
-		List<Metric> metrics = JGitUtils.getDateMetrics(r);
-
-		long numberOfCommits = 0;
-		for (Metric m : metrics) {
-			numberOfCommits += m.count;
+		List<Metric> metrics = null;
+		Metric metricsTotal = null;
+		if (GitBlit.self().settings().getBoolean(Keys.web.generateActivityGraph, true)) {
+			metrics = JGitUtils.getDateMetrics(r);
+			metricsTotal = metrics.remove(0);
 		}
 
 		// repository description
@@ -56,6 +62,11 @@
 		add(new Label("repositoryOwner", JGitUtils.getRepositoryOwner(r)));
 
 		add(WicketUtils.createTimestampLabel("repositoryLastChange", JGitUtils.getLastChange(r), getTimeZone()));
+		if (metricsTotal == null) {
+			add(new Label("repositoryStats", ""));
+		} else {
+			add(new Label("repositoryStats", MessageFormat.format("{0} commits and {1} tags in {2}", metricsTotal.count, metricsTotal.tag, TimeUtils.duration(metricsTotal.duration))));
+		}
 		add(new Label("repositoryCloneUrl", GitBlit.self().getCloneUrl(repositoryName)));
 
 		add(new LogPanel("commitsPanel", repositoryName, null, r, numberCommits, 0));
@@ -75,7 +86,7 @@
 		if (GitBlit.self().settings().getBoolean(Keys.web.generateActivityGraph, true)) {
 			IChartData data = getChartData(metrics);
 
-			ChartProvider provider = new ChartProvider(new Dimension(400, 80), ChartType.LINE, data);
+			ChartProvider provider = new ChartProvider(new Dimension(400, 100), ChartType.LINE, data);
 			ChartAxis dateAxis = new ChartAxis(ChartAxisType.BOTTOM);
 			dateAxis.setLabels(new String[] { metrics.get(0).name, metrics.get(metrics.size() / 2).name, metrics.get(metrics.size() - 1).name });
 			provider.addAxis(dateAxis);
@@ -84,6 +95,9 @@
 			commitAxis.setLabels(new String[] { "", String.valueOf((int) maxValue(metrics)) });
 			provider.addAxis(commitAxis);
 
+			provider.setLineStyles(new LineStyle[] {new LineStyle(2, 4, 0), new LineStyle(0, 4, 1)});	
+			provider.addShapeMarker(new ShapeMarker(MarkerType.DIAMOND, Color.BLUE, 1, -1, 5));
+			
 			add(new Chart("commitsChart", provider));
 		} else {
 			add(new ContextImage("commitsChart", "blank.png"));
@@ -91,23 +105,25 @@
 	}
 
 	protected IChartData getChartData(List<Metric> metrics) {
-		final double[] counts = new double[metrics.size()];
+		final double[] commits = new double[metrics.size()];
+		final double[] tags = new double[metrics.size()];
 		int i = 0;
 		double max = 0;
 		for (Metric m : metrics) {
-			counts[i++] = m.count;
+			commits[i] = m.count;
+			if (m.tag > 0) {
+				tags[i] = m.count;
+			} else {
+				tags[i] = -1d;
+			}
 			max = Math.max(max, m.count);
+			i++;
 		}
-		final double dmax = max;
-		IChartData data = new AbstractChartData() {
+		IChartData data = new AbstractChartData(max) {
 			private static final long serialVersionUID = 1L;
 
 			public double[][] getData() {
-				return new double[][] { counts };
-			}
-
-			public double getMax() {
-				return dmax;
+				return new double[][] { commits, tags };
 			}
 		};
 		return data;

--
Gitblit v1.9.1