added version details

This commit is contained in:
Özgür Kesim 2023-12-27 14:51:01 +01:00
parent 3fb0453a86
commit 9afc0ce0ff
Signed by: oec
GPG Key ID: F136A7F922D018D7
4 changed files with 106 additions and 20 deletions

75
data.go
View File

@ -47,7 +47,7 @@ type Data struct {
Issues Issues Issues Issues
Features Issues Features Issues
Projects Projects Project Project
Timestamp time.Time Timestamp time.Time
Freq time.Duration Freq time.Duration
Lasterror error Lasterror error
@ -73,6 +73,63 @@ func NewData(ctx context.Context, url, token string, num int) *Data {
return data return data
} }
func (d *Data) getProject() {
url := fmt.Sprintf("%s/projects/%d", d.url, d.projectId)
req, e := http.NewRequestWithContext(d.ctx, "GET", url, nil)
if nil != e {
d.mux.Lock()
defer d.mux.Unlock()
d.Lasterror = e
return
}
req.Header.Add("Authorization", d.token)
r, e := http.DefaultClient.Do(req)
if nil != e {
d.mux.Lock()
defer d.mux.Unlock()
d.Lasterror = e
return
} else if 200 != r.StatusCode {
d.mux.Lock()
defer d.mux.Unlock()
d.Lasterror = fmt.Errorf("Got unexpected status %s\n", r.Status)
return
}
var project Project
p := struct{ Projects []Project }{}
e = json.NewDecoder(r.Body).Decode(&p)
if len(p.Projects) < 1 {
e = fmt.Errorf("no project found with id", d.projectId)
} else {
// filter out obsolete versions
project = p.Projects[0]
var versions Versions
log.Println("found", len(project.Versions), "versions")
for _, v := range project.Versions {
v := v
if !v.Obsolete && !v.Released {
versions = append(versions, v)
}
}
project.Versions = versions
}
d.mux.Lock()
defer d.mux.Unlock()
d.Lasterror = e
if nil != e {
return
}
d.Timestamp = time.Now()
d.Project = project
fmt.Println("Got project details for", d.projectId, "with", len(d.Project.Versions), "version entries")
}
const statusFilter = `status%5B%5D=10&status%5B%5D=20&status%5B%5D=30&status%5B%5D=40&status%5B%5D=50&severity%5B%5D=20` const statusFilter = `status%5B%5D=10&status%5B%5D=20&status%5B%5D=30&status%5B%5D=40&status%5B%5D=50&severity%5B%5D=20`
var fields = []string{"id", var fields = []string{"id",
@ -92,7 +149,7 @@ var fields = []string{"id",
"tags", "tags",
} }
func (d *Data) update() { func (d *Data) getIssues() {
url := fmt.Sprintf("%s/issues?project_id=%d&page_size=%d&%s&select=%s", url := fmt.Sprintf("%s/issues?project_id=%d&page_size=%d&%s&select=%s",
d.url, d.projectId, d.num, statusFilter, d.url, d.projectId, d.num, statusFilter,
strings.Join(fields, ",")) strings.Join(fields, ","))
@ -132,9 +189,11 @@ func (d *Data) update() {
// Filter issues with old target versions out // Filter issues with old target versions out
var issues = Issues{} var issues = Issues{}
var features = Issues{} var features = Issues{}
var open = 0
for _, issue := range iss.Issues { for _, issue := range iss.Issues {
if issue.Resolution.Name == "open" && if issue.Resolution.Name == "open" &&
strings.Compare(d.minimumVersion, issue.TargetVersion.Name) < 0 { strings.Compare(d.minimumVersion, issue.TargetVersion.Name) < 0 {
open++
if issue.Severity.Name == "feature" { if issue.Severity.Name == "feature" {
features = append(features, issue) features = append(features, issue)
} else { } else {
@ -144,11 +203,12 @@ func (d *Data) update() {
} }
d.Issues = issues d.Issues = issues
d.Features = features d.Features = features
fmt.Println("got", len(iss.Issues), "entries: ", len(features), "features and ", len(issues), "issues") fmt.Println("Got", len(iss.Issues), "entries, of which", open, "are open and relevant:", len(features), "features and", len(issues), "issues")
} }
func (d *Data) Loop() { func (d *Data) Loop() {
d.update() d.getProject()
d.getIssues()
go func() { go func() {
var ticker = time.NewTicker(d.Freq) var ticker = time.NewTicker(d.Freq)
for range ticker.C { for range ticker.C {
@ -157,7 +217,8 @@ func (d *Data) Loop() {
return return
default: default:
fmt.Println("updating data") fmt.Println("updating data")
d.update() d.getProject()
d.getIssues()
} }
} }
}() }()
@ -203,6 +264,10 @@ func (d *Data) TargetVersions() (tv []string) {
return return
} }
func (d *Data) VersionsByDate() VersionsByDate {
return VersionsByDate(d.Project.Versions)
}
func (d *Data) Categories() (cs []string) { func (d *Data) Categories() (cs []string) {
d.mux.RLock() d.mux.RLock()
defer d.mux.RUnlock() defer d.mux.RUnlock()

View File

@ -31,6 +31,7 @@ pre {
{{ $top := . }} {{ $top := . }}
{{ $features := .Features }} {{ $features := .Features }}
{{ $issues := .Issues }} {{ $issues := .Issues }}
{{ $versions := .VersionsByDate }}
<!-- p> <!-- p>
{{ range $top.Tags }} {{ range $top.Tags }}
<button>{{ . }}</button> <button>{{ . }}</button>

View File

@ -22,8 +22,6 @@ import "time"
@author Özgür Kesim <oec-taler@kesim.org> @author Özgür Kesim <oec-taler@kesim.org>
*/ */
type Projects []Project
type Project struct { type Project struct {
Id int Id int
Name string Name string
@ -33,19 +31,37 @@ type Project struct {
Enabled bool Enabled bool
InheritGlobal bool `json:"inherit_global"` InheritGlobal bool `json:"inherit_global"`
AccessLevel KeyVal `json:"AccessLevel"` AccessLevel KeyVal `json:"AccessLevel"`
Versions []Version Versions Versions
Categories []Category Categories Categories
// CustomFields []any `json:"custom_fields"` // CustomFields []any `json:"custom_fields"`
} }
type Versions []Version
type Version struct { type Version struct {
KeyVal KeyVal
Description string
Released bool Released bool
Obsolete bool Obsolete bool
Timestamp time.Time Timestamp time.Time
} }
type VersionsByDate []Version
func (v VersionsByDate) Len() int { return len(v) }
func (v VersionsByDate) Less(i, j int) bool { return v[i].Timestamp.Before(v[j].Timestamp) }
func (v VersionsByDate) Swap(i, j int) { v[i], v[j] = v[j], v[i] }
type Categories []Category
type Category struct { type Category struct {
KeyVal KeyVal
Project KeyVal Project KeyVal
} }
func (vs VersionsByDate) Get(version string) *Version {
for i, v := range vs {
if v.Name == version {
return &vs[i]
}
}
return nil
}

View File

@ -12,8 +12,7 @@ table {
} }
th, td { th, td {
padding: 8px; padding: 8px;
border-bottom: 1px solid #ddd; border: 1px solid #ddd;
border-right: 1px solid #ddd;
} }
td { td {
min-width: 10em; min-width: 10em;
@ -25,8 +24,11 @@ th.side {
tr:hover { tr:hover {
background: #efe; background: #efe;
} }
.normal { details.version {
margin: 5px;
font-weight: normal; font-weight: normal;
max-width: 40em;
text-align: justify;
} }
.feature { .feature {
background: #cde; background: #cde;
@ -38,7 +40,7 @@ tr:hover {
border-radius: 5px; border-radius: 5px;
padding: 8px; padding: 8px;
} }
details { details.entry, details.feature {
max-width: 30em; max-width: 30em;
overflow-x: auto; overflow-x: auto;
white-space: nowrap; white-space: nowrap;
@ -58,6 +60,7 @@ details {
{{ $top := . }} {{ $top := . }}
{{ $features := .Features }} {{ $features := .Features }}
{{ $issues := .Issues }} {{ $issues := .Issues }}
{{ $versions := .VersionsByDate }}
<!--p> <!--p>
{{ range $top.Tags }} {{ range $top.Tags }}
<button>{{ . }}</button> <button>{{ . }}</button>
@ -69,10 +72,11 @@ details {
<th class="side"></th> <th class="side"></th>
{{ range $top.TargetVersions }} {{ range $top.TargetVersions }}
<th> <th>
<details> <details class="version">
<summary>{{ . }}</summary> {{ $version := $versions.Get . }}
<div class="normal"> <summary><b>{{ . }}</b> ({{ $version.Timestamp.Format "02 Jan 06"}})</summary>
TODO: more <div>
{{ $version.Description }}
</div> </div>
</details> </details>
</th> </th>