Compare commits
No commits in common. "0f69874475990627ec97d307764c36a7d7ab3396" and "865f1ee18409987f3bb7cefd50e7eb5656093255" have entirely different histories.
0f69874475
...
865f1ee184
2
main.go
2
main.go
@ -29,7 +29,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const VERSION = "v28"
|
const VERSION = "v27"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
|
63
problem.go
63
problem.go
@ -7,26 +7,21 @@ import (
|
|||||||
// Standard
|
// Standard
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Problem struct { // {{{
|
type Problem struct {
|
||||||
ID int
|
ID int
|
||||||
Start time.Time
|
Start time.Time
|
||||||
End sql.NullTime
|
End sql.NullTime
|
||||||
Acknowledged bool
|
Acknowledged bool
|
||||||
Datapoints map[string]any
|
|
||||||
DatapointValues map[string]any `json:"datapoints"`
|
|
||||||
TriggerID int `json:"trigger_id"`
|
TriggerID int `json:"trigger_id"`
|
||||||
TriggerName string `json:"trigger_name"`
|
TriggerName string `json:"trigger_name"`
|
||||||
AreaName string `json:"area_name"`
|
AreaName string `json:"area_name"`
|
||||||
SectionName string `json:"section_name"`
|
SectionName string `json:"section_name"`
|
||||||
} // }}}
|
}
|
||||||
|
|
||||||
func ProblemsRetrieve() (problems []Problem, err error) { // {{{
|
func ProblemsRetrieve() (problems []Problem, err error) {
|
||||||
problems = []Problem{}
|
problems = []Problem{}
|
||||||
row := service.Db.Conn.QueryRow(`
|
row := service.Db.Conn.QueryRow(`
|
||||||
SELECT
|
SELECT
|
||||||
@ -37,7 +32,6 @@ func ProblemsRetrieve() (problems []Problem, err error) { // {{{
|
|||||||
p.start,
|
p.start,
|
||||||
p.end,
|
p.end,
|
||||||
p.acknowledged,
|
p.acknowledged,
|
||||||
p.datapoints,
|
|
||||||
t.id AS trigger_id,
|
t.id AS trigger_id,
|
||||||
t.name AS trigger_name,
|
t.name AS trigger_name,
|
||||||
a.name AS area_name,
|
a.name AS area_name,
|
||||||
@ -58,7 +52,6 @@ func ProblemsRetrieve() (problems []Problem, err error) { // {{{
|
|||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
false,
|
false,
|
||||||
'{}',
|
|
||||||
-1 AS trigger_id,
|
-1 AS trigger_id,
|
||||||
CONCAT(
|
CONCAT(
|
||||||
'NODATA: ',
|
'NODATA: ',
|
||||||
@ -90,8 +83,9 @@ func ProblemsRetrieve() (problems []Problem, err error) { // {{{
|
|||||||
err = we.Wrap(err)
|
err = we.Wrap(err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
} // }}}
|
}
|
||||||
func ProblemStart(trigger Trigger) (problemID int, err error) { // {{{
|
|
||||||
|
func ProblemStart(trigger Trigger) (problemID int, err error) {
|
||||||
row := service.Db.Conn.QueryRow(`
|
row := service.Db.Conn.QueryRow(`
|
||||||
SELECT COUNT(id)
|
SELECT COUNT(id)
|
||||||
FROM problem
|
FROM problem
|
||||||
@ -111,16 +105,16 @@ func ProblemStart(trigger Trigger) (problemID int, err error) { // {{{
|
|||||||
|
|
||||||
// Open up a new problem if no open exists.
|
// Open up a new problem if no open exists.
|
||||||
if openProblems == 0 {
|
if openProblems == 0 {
|
||||||
datapointValuesJson, _ := json.Marshal(trigger.DatapointValues)
|
row = service.Db.Conn.QueryRow(`INSERT INTO problem(trigger_id) VALUES($1) RETURNING id`, trigger.ID)
|
||||||
row = service.Db.Conn.QueryRow(`INSERT INTO problem(trigger_id, datapoints) VALUES($1, $2) RETURNING id`, trigger.ID, datapointValuesJson)
|
|
||||||
err = row.Scan(&problemID)
|
err = row.Scan(&problemID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = we.Wrap(err).WithData(trigger)
|
err = we.Wrap(err).WithData(trigger)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
} // }}}
|
}
|
||||||
func ProblemClose(trigger Trigger) (problemID int, err error) { // {{{
|
|
||||||
|
func ProblemClose(trigger Trigger) (problemID int, err error) {
|
||||||
row := service.Db.Conn.QueryRow(`UPDATE problem SET "end"=NOW() WHERE trigger_id=$1 AND "end" IS NULL RETURNING id`, trigger.ID)
|
row := service.Db.Conn.QueryRow(`UPDATE problem SET "end"=NOW() WHERE trigger_id=$1 AND "end" IS NULL RETURNING id`, trigger.ID)
|
||||||
err = row.Scan(&problemID)
|
err = row.Scan(&problemID)
|
||||||
|
|
||||||
@ -134,44 +128,13 @@ func ProblemClose(trigger Trigger) (problemID int, err error) { // {{{
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
} // }}}
|
}
|
||||||
func ProblemAcknowledge(id int, state bool) (err error) { // {{{
|
|
||||||
|
func ProblemAcknowledge(id int, state bool) (err error) {
|
||||||
_, err = service.Db.Conn.Exec(`UPDATE problem SET "acknowledged"=$2 WHERE id=$1`, id, state)
|
_, err = service.Db.Conn.Exec(`UPDATE problem SET "acknowledged"=$2 WHERE id=$1`, id, state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = we.Wrap(err).WithData(id)
|
err = we.Wrap(err).WithData(id)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
} // }}}
|
|
||||||
|
|
||||||
func (p Problem) FormattedValues() string {
|
|
||||||
out := []string{}
|
|
||||||
for key, val := range p.DatapointValues {
|
|
||||||
var keyval string
|
|
||||||
|
|
||||||
switch val.(type) {
|
|
||||||
case int:
|
|
||||||
keyval = fmt.Sprintf("%s: %d", key, val)
|
|
||||||
|
|
||||||
case string:
|
|
||||||
if str, ok := val.(string); ok {
|
|
||||||
timeVal, err := time.Parse(time.RFC3339, str)
|
|
||||||
if err == nil {
|
|
||||||
formattedTime := timeVal.Format("2006-01-02 15:04:05")
|
|
||||||
keyval = fmt.Sprintf("%s: %s", key, formattedTime)
|
|
||||||
} else {
|
|
||||||
keyval = fmt.Sprintf("%s: %s", key, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
keyval = fmt.Sprintf("%s: %v", key, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
out = append(out, keyval)
|
|
||||||
|
|
||||||
}
|
|
||||||
sort.Strings(out)
|
|
||||||
|
|
||||||
return strings.Join(out, "\n")
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
ALTER TABLE public.problem ALTER COLUMN trigger_id DROP NOT NULL;
|
|
||||||
ALTER TABLE public.problem ADD COLUMN datapoints JSONB NOT NULL DEFAULT '{}';
|
|
||||||
ALTER TABLE public.problem DROP CONSTRAINT problem_trigger_fk;
|
|
||||||
ALTER TABLE public.problem ADD CONSTRAINT problem_trigger_fk FOREIGN KEY (trigger_id) REFERENCES public."trigger"(id) ON DELETE SET NULL ON UPDATE CASCADE;
|
|
@ -30,10 +30,6 @@
|
|||||||
#acknowledged-list .acknowledge img {
|
#acknowledged-list .acknowledge img {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
#problems-list .info,
|
|
||||||
#acknowledged-list .info {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
#acknowledged-list.hidden {
|
#acknowledged-list.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -45,7 +41,7 @@
|
|||||||
}
|
}
|
||||||
#areas .area .section {
|
#areas .area .section {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, min-content);
|
grid-template-columns: repeat(2, min-content);
|
||||||
grid-gap: 8px 12px;
|
grid-gap: 8px 12px;
|
||||||
}
|
}
|
||||||
#areas .area .section .name {
|
#areas .area .section .name {
|
||||||
|
@ -30,10 +30,6 @@
|
|||||||
#acknowledged-list .acknowledge img {
|
#acknowledged-list .acknowledge img {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
#problems-list .info,
|
|
||||||
#acknowledged-list .info {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
#acknowledged-list.hidden {
|
#acknowledged-list.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -45,7 +41,7 @@
|
|||||||
}
|
}
|
||||||
#areas .area .section {
|
#areas .area .section {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, min-content);
|
grid-template-columns: repeat(2, min-content);
|
||||||
grid-gap: 8px 12px;
|
grid-gap: 8px 12px;
|
||||||
}
|
}
|
||||||
#areas .area .section .name {
|
#areas .area .section .name {
|
||||||
|
@ -32,10 +32,6 @@
|
|||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#acknowledged-list.hidden{
|
#acknowledged-list.hidden{
|
||||||
@ -51,7 +47,7 @@
|
|||||||
.area {
|
.area {
|
||||||
.section {
|
.section {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, min-content);
|
grid-template-columns: repeat(2, min-content);
|
||||||
grid-gap: 8px 12px;
|
grid-gap: 8px 12px;
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
|
@ -19,7 +19,6 @@ type Trigger struct {
|
|||||||
SectionID int `db:"section_id"`
|
SectionID int `db:"section_id"`
|
||||||
Expression string
|
Expression string
|
||||||
Datapoints []string
|
Datapoints []string
|
||||||
DatapointValues map[string]any
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TriggerCreate(sectionID int, name string) (t Trigger, err error) { // {{{
|
func TriggerCreate(sectionID int, name string) (t Trigger, err error) { // {{{
|
||||||
@ -232,9 +231,9 @@ func (t *Trigger) Run() (output any, err error) { // {{{
|
|||||||
datapoints[dpname] = dp
|
datapoints[dpname] = dp
|
||||||
}
|
}
|
||||||
|
|
||||||
t.DatapointValues = make(map[string]any)
|
env := make(map[string]any)
|
||||||
for dpName, dp := range datapoints {
|
for dpName, dp := range datapoints {
|
||||||
t.DatapointValues[dpName] = dp.LastDatapointValue.Value()
|
env[dpName] = dp.LastDatapointValue.Value()
|
||||||
}
|
}
|
||||||
|
|
||||||
program, err := expr.Compile(t.Expression)
|
program, err := expr.Compile(t.Expression)
|
||||||
@ -242,7 +241,7 @@ func (t *Trigger) Run() (output any, err error) { // {{{
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err = expr.Run(program, t.DatapointValues)
|
output, err = expr.Run(program, env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -34,26 +34,13 @@
|
|||||||
<div class="area">{{ .AreaName }}</div>
|
<div class="area">{{ .AreaName }}</div>
|
||||||
<div class="section">{{ .SectionName }}</div>
|
<div class="section">{{ .SectionName }}</div>
|
||||||
<div class="start"></div>
|
<div class="start"></div>
|
||||||
<div class="acknowledge">
|
<div class="acknowledge"><img src="/images/{{ $version }}/{{ $theme }}/acknowledge.svg"></div>
|
||||||
<img class="info" src="/images/{{ $version }}/{{ $theme }}/info-outline.svg">
|
|
||||||
<img src="/images/{{ $version }}/{{ $theme }}/acknowledge.svg">
|
|
||||||
</div>
|
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<div class="trigger"><a href="/trigger/edit/{{ .TriggerID }}">{{ .TriggerName }}</a></div>
|
<div class="trigger"><a href="/trigger/edit/{{ .TriggerID }}">{{ .TriggerName }}</a></div>
|
||||||
<div class="area">{{ .AreaName }}</div>
|
<div class="area">{{ .AreaName }}</div>
|
||||||
<div class="section">{{ .SectionName }}</div>
|
<div class="section">{{ .SectionName }}</div>
|
||||||
<div class="start">{{ format_time .Start }}</div>
|
<div class="start">{{ format_time .Start }}</div>
|
||||||
<div class="acknowledge">
|
<div class="acknowledge"><a href="/problem/acknowledge/{{ .ID }}"><img src="/images/{{ $version }}/{{ $theme }}/acknowledge-filled.svg"></a></div>
|
||||||
{{ if .FormattedValues }}
|
|
||||||
<img class="info" src="/images/{{ $version }}/{{ $theme }}/info-filled.svg" title="{{ .FormattedValues }}">
|
|
||||||
{{ else }}
|
|
||||||
<img class="info" src="/images/{{ $version }}/{{ $theme }}/info-outline.svg">
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
<a href="/problem/acknowledge/{{ .ID }}">
|
|
||||||
<img src="/images/{{ $version }}/{{ $theme }}/acknowledge-filled.svg">
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
@ -76,9 +63,7 @@
|
|||||||
<div class="area">{{ .AreaName }}</div>
|
<div class="area">{{ .AreaName }}</div>
|
||||||
<div class="section">{{ .SectionName }}</div>
|
<div class="section">{{ .SectionName }}</div>
|
||||||
<div class="start">{{ format_time .Start }}</div>
|
<div class="start">{{ format_time .Start }}</div>
|
||||||
<div class="acknowledge">
|
<div class="acknowledge"><a href="/problem/unacknowledge/{{ .ID }}"><img src="/images/{{ $version }}/{{ $theme }}/acknowledge-outline.svg"></a></div>
|
||||||
<a href="/problem/unacknowledge/{{ .ID }}"><img src="/images/{{ $version }}/{{ $theme }}/acknowledge-outline.svg"></a>
|
|
||||||
</div>
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -94,7 +79,6 @@
|
|||||||
<div class="name">{{ $sectionName }}</div>
|
<div class="name">{{ $sectionName }}</div>
|
||||||
|
|
||||||
{{ range $problems }}
|
{{ range $problems }}
|
||||||
|
|
||||||
<div class="trigger">{{ .TriggerName }}</div>
|
<div class="trigger">{{ .TriggerName }}</div>
|
||||||
|
|
||||||
{{ if eq (.Start | html) "0001-01-01 00:00:00 +0000 UTC" }}
|
{{ if eq (.Start | html) "0001-01-01 00:00:00 +0000 UTC" }}
|
||||||
@ -102,13 +86,6 @@
|
|||||||
{{ else }}
|
{{ else }}
|
||||||
<div class="since">{{ format_time .Start }}</div>
|
<div class="since">{{ format_time .Start }}</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ if .FormattedValues }}
|
|
||||||
<div><img src="/images/{{ $version }}/{{ $theme }}/info-filled.svg" title="{{ .FormattedValues }}"></div>
|
|
||||||
{{ else }}
|
|
||||||
<div><img src="/images/{{ $version }}/{{ $theme }}/info-outline.svg"></div>
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
Loading…
Reference in New Issue
Block a user