Commit 455b6994 authored by zauberstuhl's avatar zauberstuhl

Merge branch 'charts' into 'master'

Add new fancy charts

See merge request feneas/federation-testsuite-server!2
parents b46ba09b 60e4851b
Pipeline #738 passed with stage
in 1 minute and 20 seconds
image: golang:1.9
before_script:
- go get -u honnef.co/go/tools/...
- go get -d ./...
run go checks:
script:
- megacheck .
......@@ -21,35 +21,31 @@ import (
"net/http"
"github.com/wcharczuk/go-chart"
"github.com/wcharczuk/go-chart/drawing"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/wcharczuk/go-chart/util"
"time"
"fmt"
"sort"
)
type DataSet struct {
X []time.Time
Y []float64
}
func (d DataSet) Len() int {
return len(d.X)
type state struct {
Passed, Failed, Canceled float64
}
func (d DataSet) Swap(i, j int) {
d.X[i], d.X[j] = d.X[j], d.X[i]
d.Y[i], d.Y[j] = d.Y[j], d.Y[i]
type sortTime []time.Time
func (t sortTime) Len() int { return len(t) }
func (t sortTime) Swap(i, j int) {
t[i], t[j] = t[j], t[i]
}
func (d DataSet) Less(i, j int) bool {
return d.X[i].Before(d.X[j])
func (t sortTime) Less(i, j int) bool {
return t[i].Before(t[j])
}
func buildsPNG(w http.ResponseWriter, r *http.Request) {
db, err := gorm.Open(
confd.StringDefault("database.driver", "sqlite3"),
confd.StringDefault("database.dsn", "./server.db"))
db, err := OpenDatabase()
if err != nil {
logger.Println(err)
return
......@@ -64,91 +60,199 @@ func buildsPNG(w http.ResponseWriter, r *http.Request) {
return
}
var passed = make(map[time.Time]float64)
var failed = make(map[time.Time]float64)
statesPerDay := make(map[time.Time]*state)
for _, build := range builds {
year, month, day := build.CreatedAt.Date()
date := time.Date(year, month, day,0, 0, 0, 0, time.Local)
year, month, day := build.UpdatedAt.Date()
date := time.Date(year, month, day, 0, 0, 0, 0, time.Local)
if _, ok := statesPerDay[date]; !ok {
statesPerDay[date] = &state{}
}
if build.Status == STATUS_SUCCESS {
passed[date] += 1
} else if build.Status > STATUS_SUCCESS {
failed[date] += 1
statesPerDay[date].Passed += 1
} else if build.Status == STATUS_FAILED {
statesPerDay[date].Failed += 1
} else if build.Status == STATUS_CANCELED {
statesPerDay[date].Canceled += 1
}
}
var maxX float64
var p, f DataSet
for k, v := range passed {
p.X = append(p.X, k)
p.Y = append(p.Y, v)
if v > maxX {
maxX = v
}
// sort
var keys sortTime
for key, _ := range statesPerDay {
keys = append(keys, key)
}
for k, v := range failed {
f.X = append(f.X, k)
f.Y = append(f.Y, v)
if v > maxX {
maxX = v
}
sort.Sort(keys)
// max entries
if len(statesPerDay) > 5 {
keys = keys[:5]
}
sort.Sort(p)
sort.Sort(f)
graph := chart.Chart{
Background: chart.Style{
FontSize: 30,
},
XAxis: chart.XAxis{
Name: "Day",
NameStyle: chart.StyleShow(),
Style: chart.Style{
Show: true,
},
},
YAxis: chart.YAxis{
Name: "Count",
NameStyle: chart.StyleShow(),
Style: chart.Style{
Show: true,
},
Range: &chart.ContinuousRange{
Min: 0.0,
Max: maxX + 2,
},
},
Series: []chart.Series{
chart.TimeSeries{
Name: "failed",
Style: chart.Style{
Show: true,
StrokeWidth: 3,
StrokeColor: drawing.ColorFromHex("A93226"),
FillColor: drawing.ColorFromHex("CD6155").WithAlpha(64),
var bars []chart.StackedBar
for _, date := range keys {
value := statesPerDay[date]
bars = append(bars, chart.StackedBar{
Name: date.Format("02-01-2006"),
Values: []chart.Value{
{
Label: "passed",
Value: value.Passed,
Style: chart.Style{
Show: true,
StrokeWidth: 3,
StrokeColor: drawing.ColorFromHex("229954"),
FillColor: drawing.ColorFromHex("27AE60").WithAlpha(64),
},
},
YValues: f.Y,
XValues: f.X,
},
chart.TimeSeries{
Name: "passed",
Style: chart.Style{
Show: true,
StrokeWidth: 3,
StrokeColor: drawing.ColorFromHex("229954"),
FillColor: drawing.ColorFromHex("27AE60").WithAlpha(64),
{
Label: "failed",
Value: value.Failed,
Style: chart.Style{
Show: true,
StrokeWidth: 3,
StrokeColor: drawing.ColorFromHex("A93226"),
FillColor: drawing.ColorFromHex("CD6155").WithAlpha(64),
},
},
{
Label: "canceled",
Value: value.Canceled,
Style: chart.Style{
Show: true,
StrokeWidth: 3,
StrokeColor: drawing.ColorFromHex("666666"),
FillColor: drawing.ColorFromHex("999999").WithAlpha(64),
},
},
YValues: p.Y,
XValues: p.X,
},
},
})
}
w.Header().Set("Content-Type", "image/png")
graph.Elements = []chart.Renderable{
chart.Legend(&graph),
graph := chart.StackedBarChart{
Title: "Recent Builds",
TitleStyle: chart.StyleShow(),
Background: chart.Style{Padding: chart.Box{Top: 40}},
Height: 512,
Width: 1024,
XAxis: chart.StyleShow(),
YAxis: chart.StyleShow(),
Bars: bars,
}
graph.Elements = []chart.Renderable{Legend(&graph)}
w.Header().Set("Content-Type", "image/png")
graph.Render(chart.PNG, w)
}
func Legend(c *chart.StackedBarChart, userDefaults ...chart.Style) chart.Renderable {
return func(r chart.Renderer, cb chart.Box, chartDefaults chart.Style) {
legendDefaults := chart.Style{
FillColor: drawing.ColorWhite,
FontColor: chart.DefaultTextColor,
FontSize: 8.0,
StrokeColor: chart.DefaultAxisColor,
StrokeWidth: chart.DefaultAxisLineWidth,
}
var legendStyle chart.Style
if len(userDefaults) > 0 {
legendStyle = userDefaults[0].InheritFrom(chartDefaults.InheritFrom(legendDefaults))
} else {
legendStyle = chartDefaults.InheritFrom(legendDefaults)
}
// DEFAULTS
legendPadding := chart.Box{
Top: 10, Left: 10, Right: 10, Bottom: 10}
lineTextGap := 5
lineLengthMinimum := 25
var labels []string
var lines []chart.Style
for _, bar := range c.Bars {
for _, value := range bar.Values {
exists := false
for _, label := range labels {
if label == value.Label {
exists = true
}
}
if exists {
continue
}
labels = append(labels, value.Label)
lines = append(lines, value.Style)
}
}
legend := chart.Box{
Top: cb.Top,
// XXX 150 should be dynamic not hard-coded :\
Left: c.GetWidth() - 150,
}
legendContent := chart.Box{
Top: legend.Top + legendPadding.Top,
Left: legend.Left + legendPadding.Left,
Right: legend.Left + legendPadding.Left,
Bottom: legend.Top + legendPadding.Top,
}
legendStyle.GetTextOptions().WriteToRenderer(r)
// measure
labelCount := 0
for x := 0; x < len(labels); x++ {
if len(labels[x]) > 0 {
tb := r.MeasureText(labels[x])
if labelCount > 0 {
legendContent.Bottom += chart.DefaultMinimumTickVerticalSpacing
}
legendContent.Bottom += tb.Height()
right := legendContent.Left + tb.Width() + lineTextGap + lineLengthMinimum
legendContent.Right = util.Math.MaxInt(legendContent.Right, right)
labelCount++
}
}
legend = legend.Grow(legendContent)
legend.Right = legendContent.Right + legendPadding.Right
legend.Bottom = legendContent.Bottom + legendPadding.Bottom
chart.Draw.Box(r, legend, legendStyle)
legendStyle.GetTextOptions().WriteToRenderer(r)
ycursor := legendContent.Top
tx := legendContent.Left
legendCount := 0
var label string
for x := 0; x < len(labels); x++ {
label = labels[x]
if len(label) > 0 {
if legendCount > 0 {
ycursor += chart.DefaultMinimumTickVerticalSpacing
}
tb := r.MeasureText(label)
ty := ycursor + tb.Height()
r.Text(label, tx, ty)
th2 := tb.Height() >> 1
lx := tx + tb.Width() + lineTextGap
ly := ty - th2
lx2 := legendContent.Right - legendPadding.Right
r.SetStrokeColor(lines[x].GetStrokeColor())
r.SetStrokeWidth(lines[x].GetStrokeWidth())
r.SetStrokeDashArray(lines[x].GetStrokeDashArray())
r.MoveTo(lx, ly)
r.LineTo(lx2, ly)
r.Stroke()
ycursor += tb.Height()
legendCount++
}
}
}
}
......@@ -47,7 +47,7 @@ func LoadSchema() {
type MergeRequest struct {
gorm.Model
MergeRequestID uint
MRID uint
SourceBranch string
Sha, URL string
}
......
......@@ -30,8 +30,10 @@ func main() {
flag.Parse()
LoadConfig()
LoadSchema()
// start build agent
go BuildAgent()
if !devMode {
// start build agent
go BuildAgent()
}
http.HandleFunc("/", indexPage)
http.HandleFunc("/images/stats/builds.png", buildsPNG)
......
......@@ -29,7 +29,7 @@ import (
type GitlabHook struct {
Attrs struct {
ID uint `json:"id"`
IID uint `json:"iid"`
Title string `json:"title"`
State string `json:"state"`
Description string `json:"description"`
......@@ -140,7 +140,7 @@ func webhookGitlab(w http.ResponseWriter, r *http.Request) {
}
mergeRequest := MergeRequest{
MergeRequestID: gitlabHook.Attrs.ID,
MRID: gitlabHook.Attrs.IID,
SourceBranch: gitlabHook.Attrs.SrcBranch,
Sha: gitlabHook.Attrs.Commit.Sha,
URL: gitlabHook.Attrs.Source.HttpUrl,
......@@ -245,14 +245,8 @@ func webhookGithub(w http.ResponseWriter, r *http.Request) {
return
}
// convert int64 into uint
var id uint
if pr.GetID() > 0 {
id = uint(pr.GetID())
}
mergeRequest := MergeRequest{
MergeRequestID: id,
MRID: uint(pr.GetNumber()),
SourceBranch: pr.GetHead().GetRef(),
Sha: pr.GetHead().GetSHA(),
URL: pr.GetHead().GetRepo().GetCloneURL(),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment