mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2024-12-26 13:29:05 +01:00
Add Tag view including required changes.
This commit is contained in:
parent
27fd3c4e5a
commit
14693ad32b
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/ClusterCockpit/cc-backend/auth"
|
"github.com/ClusterCockpit/cc-backend/auth"
|
||||||
@ -123,6 +124,42 @@ func (r *JobRepository) CreateTag(tagType string, tagName string) (tagId int64,
|
|||||||
return res.LastInsertId()
|
return res.LastInsertId()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *JobRepository) GetTags() (tags []schema.Tag, counts map[string]int, err error) {
|
||||||
|
tags = make([]schema.Tag, 0, 100)
|
||||||
|
xrows, err := r.DB.Queryx("SELECT * FROM tag")
|
||||||
|
for xrows.Next() {
|
||||||
|
var t schema.Tag
|
||||||
|
err = xrows.StructScan(&t)
|
||||||
|
tags = append(tags, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
q := sq.Select("t.tag_name, count(jt.tag_id)").
|
||||||
|
From("tag t").
|
||||||
|
LeftJoin("jobtag jt ON t.id = jt.tag_id").
|
||||||
|
GroupBy("t.tag_name")
|
||||||
|
|
||||||
|
qs, _, err := q.ToSql()
|
||||||
|
rows, err := r.DB.Query(qs)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
counts = make(map[string]int)
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var tagName string
|
||||||
|
var count int
|
||||||
|
err = rows.Scan(&tagName, &count)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
counts[tagName] = count
|
||||||
|
}
|
||||||
|
err = rows.Err()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// AddTagOrCreate adds the tag with the specified type and name to the job with the database id `jobId`.
|
// AddTagOrCreate adds the tag with the specified type and name to the job with the database id `jobId`.
|
||||||
// If such a tag does not yet exist, it is created.
|
// If such a tag does not yet exist, it is created.
|
||||||
func (r *JobRepository) AddTagOrCreate(jobId int64, tagType string, tagName string) (tagId int64, err error) {
|
func (r *JobRepository) AddTagOrCreate(jobId int64, tagType string, tagName string) (tagId int64, err error) {
|
||||||
|
78
repository/job_test.go
Normal file
78
repository/job_test.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
)
|
||||||
|
|
||||||
|
var db *sqlx.DB
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if db != nil {
|
||||||
|
panic("prefer using sub-tests (`t.Run`) or implement `cleanup` before calling setup twice.")
|
||||||
|
}
|
||||||
|
db, err = sqlx.Open("sqlite3", "../var/test.db")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setup(t *testing.T) *JobRepository {
|
||||||
|
return &JobRepository{
|
||||||
|
DB: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFind(t *testing.T) {
|
||||||
|
r := setup(t)
|
||||||
|
|
||||||
|
job, err := r.Find(1001789, "emmy", 1540853248)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Printf("%+v", job)
|
||||||
|
|
||||||
|
if job.ID != 1245 {
|
||||||
|
t.Errorf("wrong summary for diagnostic 3\ngot: %d \nwant: 1245", job.JobID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindById(t *testing.T) {
|
||||||
|
r := setup(t)
|
||||||
|
|
||||||
|
job, err := r.FindById(1245)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Printf("%+v", job)
|
||||||
|
|
||||||
|
if job.JobID != 1001789 {
|
||||||
|
t.Errorf("wrong summary for diagnostic 3\ngot: %d \nwant: 1001789", job.JobID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetTags(t *testing.T) {
|
||||||
|
r := setup(t)
|
||||||
|
|
||||||
|
tags, _, err := r.GetTags()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("TAGS %+v \n", tags)
|
||||||
|
// fmt.Printf("COUNTS %+v \n", counts)
|
||||||
|
t.Errorf("wrong summary for diagnostic 3\ngot: %d \nwant: 23", 28)
|
||||||
|
|
||||||
|
// if counts["load-imbalance"] != 23 {
|
||||||
|
// t.Errorf("wrong summary for diagnostic 3\ngot: %d \nwant: 23", counts["load-imbalance"])
|
||||||
|
// }
|
||||||
|
}
|
23
server.go
23
server.go
@ -38,6 +38,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var db *sqlx.DB
|
var db *sqlx.DB
|
||||||
|
var jobRepo *repository.JobRepository
|
||||||
|
|
||||||
// Format of the configurartion (file). See below for the defaults.
|
// Format of the configurartion (file). See below for the defaults.
|
||||||
type ProgramConfig struct {
|
type ProgramConfig struct {
|
||||||
@ -162,11 +163,29 @@ func setupAnalysisRoute(i InfoType, r *http.Request) InfoType {
|
|||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupTaglistRoute(i InfoType, r *http.Request) InfoType {
|
||||||
|
tags, counts, _ := jobRepo.GetTags()
|
||||||
|
tagMap := make(map[string][]map[string]interface{})
|
||||||
|
|
||||||
|
for _, tag := range tags {
|
||||||
|
tagItem := map[string]interface{}{
|
||||||
|
"id": tag.ID,
|
||||||
|
"name": tag.Name,
|
||||||
|
"count": counts[tag.Name],
|
||||||
|
}
|
||||||
|
tagMap[tag.Type] = append(tagMap[tag.Type], tagItem)
|
||||||
|
}
|
||||||
|
log.Infof("TAGS %+v", tags)
|
||||||
|
i["tagmap"] = tagMap
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
var routes []Route = []Route{
|
var routes []Route = []Route{
|
||||||
{"/monitoring/jobs/", "monitoring/jobs.tmpl", "Jobs - ClusterCockpit", true, func(i InfoType, r *http.Request) InfoType { return i }},
|
{"/monitoring/jobs/", "monitoring/jobs.tmpl", "Jobs - ClusterCockpit", true, func(i InfoType, r *http.Request) InfoType { return i }},
|
||||||
{"/monitoring/job/{id:[0-9]+}", "monitoring/job.tmpl", "Job <ID> - ClusterCockpit", false, setupJobRoute},
|
{"/monitoring/job/{id:[0-9]+}", "monitoring/job.tmpl", "Job <ID> - ClusterCockpit", false, setupJobRoute},
|
||||||
{"/monitoring/users/", "monitoring/list.tmpl", "Users - ClusterCockpit", true, func(i InfoType, r *http.Request) InfoType { i["listType"] = "USER"; return i }},
|
{"/monitoring/users/", "monitoring/list.tmpl", "Users - ClusterCockpit", true, func(i InfoType, r *http.Request) InfoType { i["listType"] = "USER"; return i }},
|
||||||
{"/monitoring/projects/", "monitoring/list.tmpl", "Projects - ClusterCockpit", true, func(i InfoType, r *http.Request) InfoType { i["listType"] = "PROJECT"; return i }},
|
{"/monitoring/projects/", "monitoring/list.tmpl", "Projects - ClusterCockpit", true, func(i InfoType, r *http.Request) InfoType { i["listType"] = "PROJECT"; return i }},
|
||||||
|
{"/monitoring/tags/", "monitoring/taglist.tmpl", "Tags - ClusterCockpit", false, setupTaglistRoute},
|
||||||
{"/monitoring/user/{id}", "monitoring/user.tmpl", "User <ID> - ClusterCockpit", true, setupUserRoute},
|
{"/monitoring/user/{id}", "monitoring/user.tmpl", "User <ID> - ClusterCockpit", true, setupUserRoute},
|
||||||
{"/monitoring/systems/{cluster}", "monitoring/systems.tmpl", "Cluster <ID> - ClusterCockpit", false, setupClusterRoute},
|
{"/monitoring/systems/{cluster}", "monitoring/systems.tmpl", "Cluster <ID> - ClusterCockpit", false, setupClusterRoute},
|
||||||
{"/monitoring/node/{cluster}/{hostname}", "monitoring/node.tmpl", "Node <ID> - ClusterCockpit", false, setupNodeRoute},
|
{"/monitoring/node/{cluster}/{hostname}", "monitoring/node.tmpl", "Node <ID> - ClusterCockpit", false, setupNodeRoute},
|
||||||
@ -307,9 +326,11 @@ func main() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jobRepo = &repository.JobRepository{DB: db}
|
||||||
|
|
||||||
graphQLPlayground := playground.Handler("GraphQL playground", "/query")
|
graphQLPlayground := playground.Handler("GraphQL playground", "/query")
|
||||||
api := &api.RestApi{
|
api := &api.RestApi{
|
||||||
JobRepository: &repository.JobRepository{DB: db},
|
JobRepository: jobRepo,
|
||||||
AsyncArchiving: programConfig.AsyncArchiving,
|
AsyncArchiving: programConfig.AsyncArchiving,
|
||||||
Resolver: resolver,
|
Resolver: resolver,
|
||||||
MachineStateDir: programConfig.MachineStateDir,
|
MachineStateDir: programConfig.MachineStateDir,
|
||||||
|
@ -58,6 +58,14 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{block "navitem_tags" .}}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link fs-5" href="/monitoring/tags/">
|
||||||
|
<span class="cc-nav-text">Tags</span>
|
||||||
|
<i class="bi-tag-fill"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{block "navitem_stats" .}}
|
{{block "navitem_stats" .}}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
|
17
templates/monitoring/taglist.tmpl
Normal file
17
templates/monitoring/taglist.tmpl
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{{define "content"}}
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-10">
|
||||||
|
{{ range $tagType, $tagList := .Infos.tagmap }}
|
||||||
|
<div class="my-3 p-2 bg-secondary text-white text-capitalize">
|
||||||
|
{{ $tagType }}
|
||||||
|
</div>
|
||||||
|
{{ range $tagList }}
|
||||||
|
<a class="btn btn-lg btn-warning" href="/monitoring/jobs/?tag={{ .id }}" role="button">
|
||||||
|
{{ .name }} <span class="badge bg-light text-dark">{{ .count }}</span> </a>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
@ -22,13 +22,20 @@ type Page struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
templatesDir = "./templates/"
|
bp := "./"
|
||||||
|
ebp := os.Getenv("BASEPATH")
|
||||||
|
|
||||||
|
if ebp != "" {
|
||||||
|
bp = ebp
|
||||||
|
}
|
||||||
|
templatesDir = bp + "templates/"
|
||||||
base := template.Must(template.ParseFiles(templatesDir + "base.tmpl"))
|
base := template.Must(template.ParseFiles(templatesDir + "base.tmpl"))
|
||||||
files := []string{
|
files := []string{
|
||||||
"home.tmpl", "404.tmpl", "login.tmpl",
|
"home.tmpl", "404.tmpl", "login.tmpl",
|
||||||
"imprint.tmpl", "privacy.tmpl",
|
"imprint.tmpl", "privacy.tmpl",
|
||||||
"monitoring/jobs.tmpl",
|
"monitoring/jobs.tmpl",
|
||||||
"monitoring/job.tmpl",
|
"monitoring/job.tmpl",
|
||||||
|
"monitoring/taglist.tmpl",
|
||||||
"monitoring/list.tmpl",
|
"monitoring/list.tmpl",
|
||||||
"monitoring/user.tmpl",
|
"monitoring/user.tmpl",
|
||||||
"monitoring/systems.tmpl",
|
"monitoring/systems.tmpl",
|
||||||
|
Loading…
Reference in New Issue
Block a user