sort by multiple layers; common severity
This commit is contained in:
parent
cbc12b958b
commit
ab4fc1c85c
5
data.go
5
data.go
@ -61,7 +61,10 @@ func NewData(ctx context.Context, url, token string, num int) *Data {
|
|||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
num: num,
|
num: num,
|
||||||
}
|
}
|
||||||
data.tmpl = template.Must(template.New("index").ParseFS(content, "*.tmpl"))
|
funcMap := map[string]any{
|
||||||
|
"OrderedBy": OrderedBy,
|
||||||
|
}
|
||||||
|
data.tmpl = template.Must(template.New("index").Funcs(funcMap).ParseFS(content, "*.tmpl"))
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
81
issues.go
81
issues.go
@ -21,6 +21,7 @@ package main
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -111,7 +112,6 @@ func (b ByAssignment) Less(i, j int) bool {
|
|||||||
}
|
}
|
||||||
func (b ByAssignment) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
func (b ByAssignment) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||||
|
|
||||||
|
|
||||||
type ById []Issue
|
type ById []Issue
|
||||||
|
|
||||||
func (b ById) Len() int { return len(b) }
|
func (b ById) Len() int { return len(b) }
|
||||||
@ -213,3 +213,82 @@ func (i Issues) ByCategoryAndTarget(cat, tar string) (issues Issues) {
|
|||||||
sort.Sort(sort.Reverse(ByHandlerAndId(issues)))
|
sort.Sort(sort.Reverse(ByHandlerAndId(issues)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Follow the example for multiSort in https://pkg.go.dev/sort
|
||||||
|
|
||||||
|
type lessFunc func(i1, i2 *Issue) bool
|
||||||
|
|
||||||
|
type multiSorter struct {
|
||||||
|
issues Issues
|
||||||
|
less []lessFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *multiSorter) Len() int {
|
||||||
|
return len(ms.issues)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *multiSorter) Sort(issues Issues) Issues {
|
||||||
|
ms.issues = issues
|
||||||
|
sort.Sort(ms)
|
||||||
|
return ms.issues
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *multiSorter) Swap(i, j int) {
|
||||||
|
ms.issues[i], ms.issues[j] = ms.issues[j], ms.issues[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *multiSorter) Less(i, j int) bool {
|
||||||
|
p, q := &ms.issues[i], &ms.issues[j]
|
||||||
|
var k int
|
||||||
|
for k = 0; k < len(ms.less)-1; k++ {
|
||||||
|
less := ms.less[k]
|
||||||
|
switch {
|
||||||
|
case less(p, q):
|
||||||
|
return true
|
||||||
|
case less(q, p):
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ms.less[k](p, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
var severityOrder = map[string]int{
|
||||||
|
"block": 0,
|
||||||
|
"crash": 1,
|
||||||
|
"major": 2,
|
||||||
|
"minor": 3,
|
||||||
|
"text": 4,
|
||||||
|
"trivial": 5,
|
||||||
|
"tweak": 6}
|
||||||
|
|
||||||
|
var lessFuncs = map[string]lessFunc{
|
||||||
|
"Category": func(i1, i2 *Issue) bool { return strings.Compare(i1.Category.Name, i2.Category.Name) < 0 },
|
||||||
|
"Assignment": func(i1, i2 *Issue) bool { return strings.Compare(i1.Handler.Name, i2.Handler.Name) < 0 },
|
||||||
|
"Handler": func(i1, i2 *Issue) bool { return strings.Compare(i1.Handler.Name, i2.Handler.Name) < 0 },
|
||||||
|
"Target": func(i1, i2 *Issue) bool { return strings.Compare(i1.TargetVersion.Name, i2.TargetVersion.Name) < 0 },
|
||||||
|
"Id": func(i1, i2 *Issue) bool { return i1.Id < i2.Id },
|
||||||
|
"Severity": func(i1, i2 *Issue) bool {
|
||||||
|
s1, ok := severityOrder[i1.Severity.Name]
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
s2, ok := severityOrder[i2.Severity.Name]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return s1 < s2
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func OrderedBy(fields ...string) *multiSorter {
|
||||||
|
m := &multiSorter{}
|
||||||
|
for _, field := range fields {
|
||||||
|
fn, ok := lessFuncs[field]
|
||||||
|
if !ok {
|
||||||
|
log.Printf("unknown field to order by: %s\n", field)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
m.less = append(m.less, fn)
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
15
list.tmpl
15
list.tmpl
@ -21,13 +21,7 @@ pre {
|
|||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
text-overflow: wrap,ellipsis;
|
text-overflow: wrap,ellipsis;
|
||||||
}
|
}
|
||||||
.severity-block { color: red; font-style: bold; }
|
{{template "severity.tmpl"}}
|
||||||
.severity-crash { color: red; }
|
|
||||||
.severity-major { color: brown; }
|
|
||||||
.severity-minor { color: darkorange; }
|
|
||||||
.severity-text { color: black; }
|
|
||||||
.severity-trivial { color: blue; }
|
|
||||||
.severity-tweak { color: grey; }
|
|
||||||
</style></head>
|
</style></head>
|
||||||
<body>
|
<body>
|
||||||
<h1>GNU Taler Dashboard</h1>
|
<h1>GNU Taler Dashboard</h1>
|
||||||
@ -38,6 +32,7 @@ pre {
|
|||||||
{{- $features := .Features }}
|
{{- $features := .Features }}
|
||||||
{{- $issues := .Issues }}
|
{{- $issues := .Issues }}
|
||||||
{{- $versions := .VersionsByDate }}
|
{{- $versions := .VersionsByDate }}
|
||||||
|
{{- $sorter := OrderedBy "Severity" "Handler" "Id" -}}
|
||||||
<!-- p>
|
<!-- p>
|
||||||
{{- range $top.Tags }}
|
{{- range $top.Tags }}
|
||||||
<button>{{ . }}</button>
|
<button>{{ . }}</button>
|
||||||
@ -47,7 +42,7 @@ pre {
|
|||||||
{{- range $cat := $top.Categories }}
|
{{- range $cat := $top.Categories }}
|
||||||
<h3>{{ . }}</h3>
|
<h3>{{ . }}</h3>
|
||||||
<h4>Features</h4>
|
<h4>Features</h4>
|
||||||
{{- range $features.ByCategory $cat }}
|
{{- range $sorter.Sort ($features.ByCategory $cat) }}
|
||||||
<details>
|
<details>
|
||||||
<summary>
|
<summary>
|
||||||
{{ if .IsHandled }}<span title="assigned to {{.Handler.Name}}">🥷</span>{{else}}<span title="unassigned">❓</span>{{end}}
|
{{ if .IsHandled }}<span title="assigned to {{.Handler.Name}}">🥷</span>{{else}}<span title="unassigned">❓</span>{{end}}
|
||||||
@ -58,12 +53,12 @@ pre {
|
|||||||
</details>
|
</details>
|
||||||
{{ end -}}
|
{{ end -}}
|
||||||
<h4>Issues</h4>
|
<h4>Issues</h4>
|
||||||
{{- range $issues.ByCategory $cat }}
|
{{- range $sorter.Sort ($issues.ByCategory $cat) }}
|
||||||
<details>
|
<details>
|
||||||
<summary>
|
<summary>
|
||||||
{{ if .IsHandled }}<span title="assigned to {{.Handler.Name}}">🥷</span>{{else}}<span title="unassigned">❓</span>{{end}}
|
{{ if .IsHandled }}<span title="assigned to {{.Handler.Name}}">🥷</span>{{else}}<span title="unassigned">❓</span>{{end}}
|
||||||
<a href="https://bugs.gnunet.org/view.php?id={{.Id}}" target="_blank">{{.Id}}</a>
|
<a href="https://bugs.gnunet.org/view.php?id={{.Id}}" target="_blank">{{.Id}}</a>
|
||||||
<span class="severity-{{.Severity.Name}}">{{.Summary}}</span><br>
|
<span class="severity-{{.Severity.Name}}" title="severity {{.Severity.Name}}">{{.Summary}}</span><br>
|
||||||
</summary>
|
</summary>
|
||||||
<pre>{{ .Description }}</pre>
|
<pre>{{ .Description }}</pre>
|
||||||
</details>
|
</details>
|
||||||
|
7
severity.tmpl
Normal file
7
severity.tmpl
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.severity-block { color: red; font-weight: bold; }
|
||||||
|
.severity-crash { color: deeppink; font-weight: bold; }
|
||||||
|
.severity-major { color: crimson; }
|
||||||
|
.severity-minor { color: sienna; }
|
||||||
|
.severity-text { color: black; }
|
||||||
|
.severity-trivial { color: grey; }
|
||||||
|
.severity-tweak { color: steelblue; }
|
30
table.tmpl
30
table.tmpl
@ -26,21 +26,15 @@ tr:hover {
|
|||||||
background: #efefef;
|
background: #efefef;
|
||||||
}
|
}
|
||||||
.feature {
|
.feature {
|
||||||
background: #cdf;
|
background: powderblue;
|
||||||
}
|
}
|
||||||
.issue {
|
.issue {
|
||||||
background: #edc;
|
background: navajowhite;
|
||||||
}
|
}
|
||||||
.minh {
|
.minh {
|
||||||
min-height: 5em;
|
min-height: 5em;
|
||||||
}
|
}
|
||||||
.severity-block { color: red; font-style: bold; }
|
{{template "severity.tmpl"}}
|
||||||
.severity-crash { color: red; }
|
|
||||||
.severity-major { color: brown; }
|
|
||||||
.severity-minor { color: darkorange; }
|
|
||||||
.severity-text { color: black; }
|
|
||||||
.severity-trivial { color: blue; }
|
|
||||||
.severity-tweak { color: grey; }
|
|
||||||
summary {
|
summary {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -74,19 +68,19 @@ details.version {
|
|||||||
text-align: justify;
|
text-align: justify;
|
||||||
}
|
}
|
||||||
details[open].feature {
|
details[open].feature {
|
||||||
background: aliceblue;
|
background: ghostwhite;
|
||||||
border: 2px midnightblue solid;
|
border: 4px powderblue solid;
|
||||||
}
|
}
|
||||||
details[open].issue {
|
details[open].issue {
|
||||||
background: lightyellow;
|
background: ghostwhite;
|
||||||
border: 2px brown solid;
|
border: 4px navajowhite solid;
|
||||||
}
|
}
|
||||||
</style></head>
|
</style></head>
|
||||||
<body>
|
<body>
|
||||||
<h1>GNU Taler Dashboard</h1>
|
<h1>GNU Taler Dashboard</h1>
|
||||||
<h2>Table view | <a href="/list">List View</a></h2>
|
<h2>Table view | <a href="/list">List View</a></h2>
|
||||||
Data from {{ .Timestamp.Format "02 Jan 06 15:04 MST"}}, updated every {{ .Freq }} (no auto-refresh).
|
Data from {{ .Timestamp.Format "02 Jan 06 15:04 MST"}}, updated every {{ .Freq }} (no auto-refresh).
|
||||||
{{- with .Lasterror }}, Last error: {{ . }} {{end }}
|
{{- with .Lasterror }} Last error: {{ . }} {{end }}
|
||||||
{{- $top := . -}}
|
{{- $top := . -}}
|
||||||
{{- $features := .Features -}}
|
{{- $features := .Features -}}
|
||||||
{{- $issues := .Issues -}}
|
{{- $issues := .Issues -}}
|
||||||
@ -120,7 +114,8 @@ details[open].issue {
|
|||||||
<div>{{ $l := len .}}{{ if lt 1 $l }}{{ $l }} features{{ else }}1 feature{{ end }}</div>
|
<div>{{ $l := len .}}{{ if lt 1 $l }}{{ $l }} features{{ else }}1 feature{{ end }}</div>
|
||||||
<div class="assignment">{{- $unassigned := .Assigned false }}{{if gt $unassigned 0 }}({{ $unassigned}} unassigned){{ end -}}</div>
|
<div class="assignment">{{- $unassigned := .Assigned false }}{{if gt $unassigned 0 }}({{ $unassigned}} unassigned){{ end -}}</div>
|
||||||
</summary>
|
</summary>
|
||||||
{{- range . }}
|
{{- $sorter := OrderedBy "Handler" "Id" -}}
|
||||||
|
{{- range $sorter.Sort . }}
|
||||||
{{ if .IsHandled }}<span title="assigned to {{.Handler.Name}}">🥷</span>{{else}}<span title="unassigned">❓</span>{{end}}
|
{{ if .IsHandled }}<span title="assigned to {{.Handler.Name}}">🥷</span>{{else}}<span title="unassigned">❓</span>{{end}}
|
||||||
<a href="https://bugs.gnunet.org/view.php?id={{.Id}}" target="_blank">{{.Id}}</a>
|
<a href="https://bugs.gnunet.org/view.php?id={{.Id}}" target="_blank">{{.Id}}</a>
|
||||||
<span class="severity-{{.Severity.Name}}">{{.Summary}}</span><br>
|
<span class="severity-{{.Severity.Name}}">{{.Summary}}</span><br>
|
||||||
@ -133,10 +128,11 @@ details[open].issue {
|
|||||||
<div>{{ $l := len .}}{{ if lt 1 $l }}{{ $l }} issues{{ else }}1 issue{{ end }}</div>
|
<div>{{ $l := len .}}{{ if lt 1 $l }}{{ $l }} issues{{ else }}1 issue{{ end }}</div>
|
||||||
<div class="assignment">{{- $unassigned := .Assigned false }}{{if gt $unassigned 0 }} ({{ $unassigned}} unassigned){{ end -}}</div>
|
<div class="assignment">{{- $unassigned := .Assigned false }}{{if gt $unassigned 0 }} ({{ $unassigned}} unassigned){{ end -}}</div>
|
||||||
</summary>
|
</summary>
|
||||||
{{- range . }}
|
{{- $sorter := OrderedBy "Severity" "Handler" "Id" -}}
|
||||||
|
{{- range $sorter.Sort . }}
|
||||||
{{ if .IsHandled }}<span title="assigned to {{.Handler.Name}}">🥷</span>{{else}}<span title="unassigned">❓</span>{{end}}
|
{{ if .IsHandled }}<span title="assigned to {{.Handler.Name}}">🥷</span>{{else}}<span title="unassigned">❓</span>{{end}}
|
||||||
<a href="https://bugs.gnunet.org/view.php?id={{.Id}}" target="_blank">{{.Id}}</a>
|
<a href="https://bugs.gnunet.org/view.php?id={{.Id}}" target="_blank">{{.Id}}</a>
|
||||||
<span class="severity-{{.Severity.Name}}">{{.Summary}}</span><br>
|
<span class="severity-{{.Severity.Name}}" title="severity {{.Severity.Name}}">{{.Summary}}</span><br>
|
||||||
{{- end}}
|
{{- end}}
|
||||||
</details>
|
</details>
|
||||||
{{ end -}}
|
{{ end -}}
|
||||||
|
Loading…
Reference in New Issue
Block a user