Skip to content

Commit 3115875

Browse files
authored
Merge pull request #311 from jenkinsci/trend-chart
A trend charts for the commit statistics
2 parents d9fe080 + 400c99f commit 3115875

12 files changed

+257
-33
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
<data-tables-api.version>1.10.25-3</data-tables-api.version>
3232
<jquery3-api.version>3.6.0-2</jquery3-api.version>
3333
<font-awesome-api.version>5.15.3-4</font-awesome-api.version>
34-
<echarts-api.version>5.1.2-5</echarts-api.version>
34+
<echarts-api.version>5.1.2-7</echarts-api.version>
3535
<bootstrap5-api.version>5.0.2-1</bootstrap5-api.version>
3636

3737
</properties>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package io.jenkins.plugins.forensics.miner;
2+
3+
import java.util.Map;
4+
5+
import edu.hm.hafner.echarts.SeriesBuilder;
6+
7+
import static io.jenkins.plugins.forensics.miner.AddedVersusDeletedLinesForensicsSeriesBuilder.*;
8+
9+
/**
10+
* Builds one x-axis point for the series of a line chart showing the added and deleted lines per build.
11+
*
12+
* @author Ullrich Hafner
13+
*/
14+
class AddedVersusDeletedLinesCommitStatisticsSeriesBuilder extends SeriesBuilder<CommitStatisticsBuildAction> {
15+
@Override
16+
protected Map<String, Integer> computeSeries(final CommitStatisticsBuildAction current) {
17+
return computeAddedVsDeletedSeries(current.getCommitStatistics());
18+
}
19+
}

src/main/java/io/jenkins/plugins/forensics/miner/AddedVersusDeletedLinesSeriesBuilder.java renamed to src/main/java/io/jenkins/plugins/forensics/miner/AddedVersusDeletedLinesForensicsSeriesBuilder.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,17 @@
1010
*
1111
* @author Ullrich Hafner
1212
*/
13-
class AddedVersusDeletedLinesSeriesBuilder extends SeriesBuilder<ForensicsBuildAction> {
13+
class AddedVersusDeletedLinesForensicsSeriesBuilder extends SeriesBuilder<ForensicsBuildAction> {
1414
static final String ADDED = "added";
1515
static final String DELETED = "deleted";
1616

1717
@Override
1818
protected Map<String, Integer> computeSeries(final ForensicsBuildAction current) {
19+
return computeAddedVsDeletedSeries(current.getCommitStatistics());
20+
}
21+
22+
static Map<String, Integer> computeAddedVsDeletedSeries(final CommitStatistics commitStatistics) {
1923
Map<String, Integer> series = new HashMap<>();
20-
CommitStatistics commitStatistics = current.getCommitStatistics();
2124
series.put(ADDED, commitStatistics.getAddedLines());
2225
series.put(DELETED, commitStatistics.getDeletedLines());
2326
return series;

src/main/java/io/jenkins/plugins/forensics/miner/AddedVersusDeletedLinesTrendChart.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import edu.hm.hafner.echarts.LinesChartModel;
1010
import edu.hm.hafner.echarts.LinesDataSet;
1111
import edu.hm.hafner.echarts.Palette;
12+
import edu.hm.hafner.echarts.SeriesBuilder;
1213

1314
/**
1415
* Builds the Java side model for a trend chart showing the number of deleted and added lines of code in a build.
@@ -21,16 +22,30 @@
2122
* @see JacksonFacade
2223
*/
2324
class AddedVersusDeletedLinesTrendChart {
24-
LinesChartModel create(final Iterable<? extends BuildResult<ForensicsBuildAction>> results,
25-
final ChartModelConfiguration configuration) {
26-
AddedVersusDeletedLinesSeriesBuilder builder = new AddedVersusDeletedLinesSeriesBuilder();
27-
LinesDataSet dataSet = builder.createDataSet(configuration, results);
25+
/**
26+
* Creates the chart for the specified results.
27+
*
28+
* @param results
29+
* the results to render - these results must be provided in descending order, i.e. the current
30+
* build is the head of the list, then the previous builds, and so on
31+
* @param configuration
32+
* the chart configuration to be used
33+
* @param seriesBuilder
34+
* the builder to plot the data points
35+
* @param <T>
36+
* the type of the action that stores the results
37+
*
38+
* @return the chart model, ready to be serialized to JSON
39+
*/
40+
<T> LinesChartModel create(final Iterable<? extends BuildResult<T>> results,
41+
final ChartModelConfiguration configuration, final SeriesBuilder<T> seriesBuilder) {
42+
LinesDataSet dataSet = seriesBuilder.createDataSet(configuration, results);
2843

2944
LinesChartModel model = new LinesChartModel(dataSet);
3045
LineSeries newSeries = getSeries(dataSet, "Added Lines", Palette.GREEN,
31-
AddedVersusDeletedLinesSeriesBuilder.ADDED);
46+
AddedVersusDeletedLinesForensicsSeriesBuilder.ADDED);
3247
LineSeries fixedSeries = getSeries(dataSet, "Deleted Lines", Palette.RED,
33-
AddedVersusDeletedLinesSeriesBuilder.DELETED);
48+
AddedVersusDeletedLinesForensicsSeriesBuilder.DELETED);
3449

3550
model.addSeries(newSeries, fixedSeries);
3651

src/main/java/io/jenkins/plugins/forensics/miner/CommitStatisticsBuildAction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,6 @@ public void onLoad(final Run<?, ?> run) {
116116

117117
@Override
118118
public Collection<? extends Action> getProjectActions() {
119-
return Collections.emptyList();
119+
return Collections.singleton(new CommitStatisticsJobAction(owner.getParent(), scmKey));
120120
}
121121
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package io.jenkins.plugins.forensics.miner;
2+
3+
import java.util.ArrayList;
4+
import java.util.Iterator;
5+
import java.util.List;
6+
import java.util.Optional;
7+
8+
import org.apache.commons.lang3.StringUtils;
9+
10+
import edu.hm.hafner.echarts.Build;
11+
import edu.hm.hafner.echarts.BuildResult;
12+
import edu.hm.hafner.echarts.ChartModelConfiguration;
13+
import edu.hm.hafner.echarts.JacksonFacade;
14+
import edu.hm.hafner.echarts.LinesChartModel;
15+
16+
import org.kohsuke.stapler.bind.JavaScriptMethod;
17+
import hudson.model.InvisibleAction;
18+
import hudson.model.Job;
19+
import hudson.model.Run;
20+
21+
import io.jenkins.plugins.echarts.AsyncConfigurableTrendChart;
22+
23+
/**
24+
* This job action is responsible to render the historical trend of the commit statistics via its associated
25+
* 'floatingBox.jelly' view.
26+
*
27+
* @author Ullrich Hafner
28+
*/
29+
public class CommitStatisticsJobAction extends InvisibleAction implements AsyncConfigurableTrendChart {
30+
enum ChartType {
31+
DELTA, COUNT
32+
}
33+
34+
private static final JacksonFacade JACKSON_FACADE = new JacksonFacade();
35+
36+
private final String scmKey;
37+
private final Job<?, ?> owner;
38+
39+
CommitStatisticsJobAction(final Job<?, ?> owner, final String scmKey) {
40+
super();
41+
42+
this.owner = owner;
43+
this.scmKey = scmKey;
44+
}
45+
46+
@JavaScriptMethod
47+
@Override
48+
public String getConfigurableBuildTrendModel(final String configuration) {
49+
return new JacksonFacade().toJson(createChartModel(configuration));
50+
}
51+
52+
private LinesChartModel createChartModel(final String configuration) {
53+
ChartModelConfiguration modelConfiguration = ChartModelConfiguration.fromJson(configuration);
54+
55+
ChartType chart = getChart(configuration);
56+
57+
Iterable<? extends BuildResult<CommitStatisticsBuildAction>> buildHistory
58+
= createBuildHistory(modelConfiguration.getBuildCount());
59+
if (chart == ChartType.DELTA) {
60+
return new AddedVersusDeletedLinesTrendChart().create(buildHistory, modelConfiguration,
61+
new AddedVersusDeletedLinesCommitStatisticsSeriesBuilder());
62+
}
63+
return new RelativeCountTrendChart().create(buildHistory, modelConfiguration,
64+
new RelativeCountCommitStatisticsSeriesBuilder());
65+
}
66+
67+
private ChartType getChart(final String configuration) {
68+
String type = JACKSON_FACADE.getString(configuration, "chartType", "delta");
69+
for (ChartType chartType : ChartType.values()) {
70+
if (StringUtils.equalsIgnoreCase(type, chartType.name())) {
71+
return chartType;
72+
}
73+
}
74+
75+
return ChartType.DELTA;
76+
}
77+
78+
private Iterable<? extends BuildResult<CommitStatisticsBuildAction>> createBuildHistory(final int buildCount) {
79+
List<BuildResult<CommitStatisticsBuildAction>> history = new ArrayList<>();
80+
for (Run<?, ?> run = owner.getLastCompletedBuild(); run != null; run = run.getPreviousBuild()) {
81+
Optional<CommitStatisticsBuildAction> latestAction = run.getActions(CommitStatisticsBuildAction.class)
82+
.stream()
83+
.filter(a -> scmKey.equals(a.getScmKey()))
84+
.findAny();
85+
if (latestAction.isPresent()) {
86+
int buildTimeInSeconds = (int) (run.getTimeInMillis() / 1000);
87+
Build build = new Build(run.getNumber(), run.getDisplayName(), buildTimeInSeconds);
88+
89+
history.add(new BuildResult<>(build, latestAction.get()));
90+
}
91+
92+
if (buildCount > 0 && history.size() >= buildCount) {
93+
break;
94+
}
95+
}
96+
97+
return history;
98+
}
99+
100+
@Override
101+
public boolean isTrendVisible() {
102+
Iterable<? extends BuildResult<CommitStatisticsBuildAction>> results = createBuildHistory(2);
103+
Iterator<? extends BuildResult<CommitStatisticsBuildAction>> iterator = results.iterator();
104+
105+
if (iterator.hasNext()) {
106+
iterator.next();
107+
}
108+
return iterator.hasNext();
109+
}
110+
}

src/main/java/io/jenkins/plugins/forensics/miner/ForensicsJobAction.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,18 +107,20 @@ LinesChartModel createChart(final Iterable<? extends BuildResult<ForensicsBuildA
107107
return new CodeMetricTrendChart().create(buildHistory, modelConfiguration);
108108
}
109109
if (chart == ChartType.DELTA) {
110-
return new AddedVersusDeletedLinesTrendChart().create(buildHistory, modelConfiguration);
110+
return new AddedVersusDeletedLinesTrendChart().create(buildHistory, modelConfiguration,
111+
new AddedVersusDeletedLinesForensicsSeriesBuilder());
111112
}
112113
if (chart == ChartType.COUNT) {
113-
return new RelativeCountTrendChart().create(buildHistory, modelConfiguration);
114+
return new RelativeCountTrendChart().create(buildHistory, modelConfiguration,
115+
new RelativeCountForesnsicsSeriesBuilder());
114116
}
115117
return new FilesCountTrendChart().create(buildHistory, modelConfiguration);
116118
}
117119

118120
private ChartType getChart(final String configuration) {
119121
String type = JACKSON_FACADE.getString(configuration, "chartType", "files");
120122
for (ChartType chartType : ChartType.values()) {
121-
if (StringUtils.equalsIgnoreCase(chartType.name(), type)) {
123+
if (StringUtils.equalsIgnoreCase(type, chartType.name())) {
122124
return chartType;
123125
}
124126
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package io.jenkins.plugins.forensics.miner;
2+
3+
import java.util.Map;
4+
5+
import edu.hm.hafner.echarts.SeriesBuilder;
6+
7+
import static io.jenkins.plugins.forensics.miner.RelativeCountForesnsicsSeriesBuilder.*;
8+
9+
/**
10+
* Builds one x-axis point for the series of a line chart showing the number of modified files, commits and authors in
11+
* the repository.
12+
*
13+
* @author Ullrich Hafner
14+
*/
15+
class RelativeCountCommitStatisticsSeriesBuilder extends SeriesBuilder<CommitStatisticsBuildAction> {
16+
@Override
17+
protected Map<String, Integer> computeSeries(final CommitStatisticsBuildAction current) {
18+
return computeRelativeCountStatistics(current.getCommitStatistics());
19+
}
20+
}

src/main/java/io/jenkins/plugins/forensics/miner/RelativeCountSeriesBuilder.java renamed to src/main/java/io/jenkins/plugins/forensics/miner/RelativeCountForesnsicsSeriesBuilder.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,25 @@
1111
*
1212
* @author Ullrich Hafner
1313
*/
14-
class RelativeCountSeriesBuilder extends SeriesBuilder<ForensicsBuildAction> {
14+
class RelativeCountForesnsicsSeriesBuilder extends SeriesBuilder<ForensicsBuildAction> {
1515
static final String AUTHORS_KEY = "authors";
1616
static final String FILES_KEY = "files";
1717
static final String COMMITS_KEY = "commits";
1818

1919
@Override
2020
protected Map<String, Integer> computeSeries(final ForensicsBuildAction current) {
21-
Map<String, Integer> series = new HashMap<>();
2221
CommitStatistics commitStatistics;
2322
if (current.getTotalLinesOfCode() == 0) {
2423
commitStatistics = current.getResult().getLatestStatistics();
2524
}
2625
else {
2726
commitStatistics = current.getCommitStatistics();
2827
}
28+
return computeRelativeCountStatistics(commitStatistics);
29+
}
30+
31+
static Map<String, Integer> computeRelativeCountStatistics(final CommitStatistics commitStatistics) {
32+
Map<String, Integer> series = new HashMap<>();
2933
series.put(AUTHORS_KEY, commitStatistics.getAuthorCount());
3034
series.put(FILES_KEY, commitStatistics.getFilesCount());
3135
series.put(COMMITS_KEY, commitStatistics.getCommitCount());

src/main/java/io/jenkins/plugins/forensics/miner/RelativeCountTrendChart.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import edu.hm.hafner.echarts.LinesChartModel;
1010
import edu.hm.hafner.echarts.LinesDataSet;
1111
import edu.hm.hafner.echarts.Palette;
12+
import edu.hm.hafner.echarts.SeriesBuilder;
1213

1314
/**
1415
* Builds the Java side model for a trend chart showing the number modified files, commits and authors in the
@@ -28,22 +29,24 @@ class RelativeCountTrendChart {
2829
* build is the head of the list, then the previous builds, and so on
2930
* @param configuration
3031
* the chart configuration to be used
32+
* @param seriesBuilder
33+
* the builder to plot the data points
34+
* @param <T>
35+
* the type of the action that stores the results
3136
*
3237
* @return the chart model, ready to be serialized to JSON
3338
*/
34-
LinesChartModel create(final Iterable<? extends BuildResult<ForensicsBuildAction>> results,
35-
final ChartModelConfiguration configuration) {
36-
RelativeCountSeriesBuilder builder = new RelativeCountSeriesBuilder();
37-
38-
LinesDataSet dataSet = builder.createDataSet(configuration, results);
39+
<T> LinesChartModel create(final Iterable<? extends BuildResult<T>> results,
40+
final ChartModelConfiguration configuration, final SeriesBuilder<T> seriesBuilder) {
41+
LinesDataSet dataSet = seriesBuilder.createDataSet(configuration, results);
3942
LinesChartModel model = new LinesChartModel(dataSet);
4043
if (dataSet.getDomainAxisSize() > 0) {
4144
LineSeries authors = getSeries(dataSet, "Authors", Palette.BLUE,
42-
RelativeCountSeriesBuilder.AUTHORS_KEY);
45+
RelativeCountForesnsicsSeriesBuilder.AUTHORS_KEY);
4346
LineSeries commits = getSeries(dataSet, "Commits", Palette.GREEN,
44-
RelativeCountSeriesBuilder.COMMITS_KEY);
47+
RelativeCountForesnsicsSeriesBuilder.COMMITS_KEY);
4548
LineSeries files = getSeries(dataSet, "Modified files", Palette.ORANGE,
46-
RelativeCountSeriesBuilder.FILES_KEY);
49+
RelativeCountForesnsicsSeriesBuilder.FILES_KEY);
4750

4851
model.addSeries(authors, commits, files);
4952
}

0 commit comments

Comments
 (0)