James Moger
2011-04-20 45c0d6ed8c9c3afc4d09200358ee2d53f06023e2
Tag diamonds and sliding date scale on activity graph.
11 files modified
202 ■■■■ changed files
contrib/com/codecommit/wicket/LineStyle.java 7 ●●●● patch | view | raw | blame | history
contrib/com/codecommit/wicket/MarkerType.java 4 ●●● patch | view | raw | blame | history
contrib/com/codecommit/wicket/ShapeMarker.java 6 ●●●● patch | view | raw | blame | history
gitblit.properties 6 ●●●● patch | view | raw | blame | history
src/com/gitblit/tests/JGitUtilsTest.java 8 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/JGitUtils.java 73 ●●●● patch | view | raw | blame | history
src/com/gitblit/utils/TimeUtils.java 34 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp.properties 1 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/models/Metric.java 2 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/SummaryPage.html 13 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/SummaryPage.java 48 ●●●●● patch | view | raw | blame | history
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;
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"),
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;
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
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();
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) {
@@ -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;
    }
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);
src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -55,3 +55,4 @@
gb.modification = modification
gb.deletion = deletion
gb.rename = rename
gb.stats = stats
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;
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>
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;
            max = Math.max(max, m.count);
            commits[i] = m.count;
            if (m.tag > 0) {
                tags[i] = m.count;
            } else {
                tags[i] = -1d;
        }
        final double dmax = max;
        IChartData data = new AbstractChartData() {
            max = Math.max(max, m.count);
            i++;
        }
        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;