package main import ( // External werr "git.gibonuddevalla.se/go/wrappederror" // Standard "database/sql" "encoding/json" "fmt" "sort" "strings" "time" ) type Problem struct { // {{{ ID int Start time.Time End time.Time Acknowledged bool Datapoints map[string]any DatapointValues map[string]any `json:"datapoints"` TriggerID int `json:"trigger_id"` TriggerName string `json:"trigger_name"` AreaName string `json:"area_name"` SectionName string `json:"section_name"` } // }}} func ProblemsRetrieve(showCurrent bool, from, to time.Time) (problems []Problem, err error) { // {{{ problems = []Problem{} row := service.Db.Conn.QueryRow(` SELECT jsonb_agg(problems.*) FROM ( (SELECT p.id, p.start, TO_CHAR(p.end, 'YYYY-MM-DD"T"HH24:MI:SSTZH:TZM') AS end, p.acknowledged, p.datapoints, t.id AS trigger_id, p.trigger_name AS trigger_name, a.name AS area_name, s.name AS section_name FROM problem p LEFT JOIN "trigger" t ON p.trigger_id = t.id LEFT JOIN section s ON t.section_id = s.id LEFT JOIN area a ON s.area_id = a.id WHERE CASE WHEN NOT $1 THEN p.end IS NULL WHEN $1 THEN p.start >= $2 AND ( p.end IS NULL OR p.end <= $3 ) END ORDER BY p.start DESC) UNION ALL (SELECT -1 AS id, dp.last_value, null, false, '{}', -1 AS trigger_id, CONCAT( 'NODATA: ', dp.name ) AS trigger_name, '[NODATA]' AS area_name, '[NODATA]' AS section_name FROM datapoint dp WHERE dp.nodata_is_problem ORDER BY dp.name ASC) ) AS problems`, showCurrent, from, to, ) var jsonBody []byte err = row.Scan(&jsonBody) if err != nil { err = werr.Wrap(err) return } if jsonBody == nil { return } err = json.Unmarshal(jsonBody, &problems) if err != nil { err = werr.Wrap(err) } return } // }}} func ProblemStart(trigger Trigger) (problemID int, err error) { // {{{ row := service.Db.Conn.QueryRow(` SELECT COUNT(id) FROM problem WHERE trigger_id = $1 AND "end" IS NULL GROUP BY trigger_id `, trigger.ID, ) var openProblems int err = row.Scan(&openProblems) if err != nil && err != sql.ErrNoRows { err = werr.Wrap(err).WithData(trigger.ID) return } // Open up a new problem if no open exists. if openProblems == 0 { datapointValuesJson, _ := json.Marshal(trigger.DatapointValues) row = service.Db.Conn.QueryRow( `INSERT INTO problem(trigger_id, trigger_name, datapoints, trigger_expression) VALUES($1, $2, $3, $4) RETURNING id`, trigger.ID, trigger.Name, datapointValuesJson, trigger.Expression, ) err = row.Scan(&problemID) if err != nil { err = werr.Wrap(err).WithData(trigger) } } return } // }}} 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) err = row.Scan(&problemID) if err == sql.ErrNoRows { err = nil return } if err != nil { err = werr.Wrap(err).WithData(trigger) return } return } // }}} func ProblemAcknowledge(id int, state bool) (err error) { // {{{ _, err = service.Db.Conn.Exec(`UPDATE problem SET "acknowledged"=$2 WHERE id=$1`, id, state) if err != nil { err = werr.Wrap(err).WithData(id) 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") } // }}} func (p Problem) IsArchived() bool { // {{{ return !p.End.IsZero() } // }}}