package models // StatsData provides functionality. type StatsData struct { MonitorID *int64 `json:"monitor_id,omitempty"` Up30d *float64 `json:"up_30d"` Up7d *float64 `json:"up_7d"` Up1d *float64 `json:"up_1d"` } // Process provides functionality. func (data *StatsData) Process() { if data.Up1d == nil { dv := 100.0 data.Up1d = &dv } if data.Up7d == nil { dv := 100.0 data.Up7d = &dv } if data.Up30d == nil { dv := 100.0 data.Up30d = &dv } } // 86400 seconds / 1d // 604800 seconds / 7d // 2592000 seconds / 30d // UptimeSQL provides functionality. const UptimeSQL = ` SELECT monitors.id AS monitor_id, round(up1.up::numeric, 3) AS up1d, round(up7.up::numeric, 3) AS up7d, round(up30.up::numeric, 3) AS up30d FROM monitors LEFT JOIN ( select monitor_id, CAST(10000 - (10000 * sum(duration) / min(lt.least)) as float) / 100 as up from events join ( select id, LEAST(extract(epoch from (NOW() - created_at))::int, 86400) from monitors ) lt ON lt.id = events.monitor_id where start_time > NOW() - interval '1' day group by monitor_id ) up1 ON monitors.id = up1.monitor_id LEFT JOIN ( select monitor_id, CAST(10000 - (10000 * sum(duration) / min(lt.least)) as float) / 100 as up from events join ( select id, LEAST(extract(epoch from (NOW() - created_at))::int, 604800) from monitors ) lt ON lt.id = events.monitor_id where start_time > NOW() - interval '7' day group by monitor_id ) up7 ON monitors.id = up7.monitor_id LEFT JOIN ( select monitor_id, CAST(10000 - (10000 * sum(duration) / min(lt.least)) as float) / 100 as up from events join ( select id, LEAST(extract(epoch from (NOW() - created_at))::int, 2592000) from monitors ) lt ON lt.id = events.monitor_id where start_time > NOW() - interval '30' day group by monitor_id ) up30 ON monitors.id = up30.monitor_id ` // UptimeAllSQL provides functionality. const UptimeAllSQL = ` SELECT round(up1.up::numeric, 3) AS up1d, round(up7.up::numeric, 3) AS up7d, round(up30.up::numeric, 3) AS up30d FROM ( select CAST(10000 - (10000 * sum(duration) / sum(lt.least)) as float) / 100 as up from events join ( select id, LEAST(extract(epoch from (NOW() - created_at))::int, 86400) from monitors ) lt ON lt.id = events.monitor_id where start_time > NOW() - interval '1' day AND events.monitor_id IN (?) ) up1, ( select CAST(10000 - (10000 * sum(duration) / sum(lt.least)) as float) / 100 as up from events join ( select id, LEAST(extract(epoch from (NOW() - created_at))::int, 604800) from monitors ) lt ON lt.id = events.monitor_id where start_time > NOW() - interval '7' day AND events.monitor_id IN (?) ) up7, ( select CAST(10000 - (10000 * sum(duration) / sum(lt.least)) as float) / 100 as up from events join ( select id, LEAST(extract(epoch from (NOW() - created_at))::int, 2592000) from monitors ) lt ON lt.id = events.monitor_id where start_time > NOW() - interval '30' day AND events.monitor_id IN (?) ) up30 ` // MonitorStats provides functionality. func MonitorStats(monitors *[]Monitor) error { ids := make([]int64, len(*monitors)) for i, m := range *monitors { //nolint:gocritic // range copy is acceptable here ids[i] = m.ID } rows, err := DB().Raw(UptimeSQL+"WHERE monitors.id IN (?)", ids).Rows() if err != nil { return err } stats := make(map[int64]StatsData, 0) defer rows.Close() //nolint:errcheck for rows.Next() { data := StatsData{} _ = DB().ScanRows(rows, &data) data.Process() stats[*data.MonitorID] = data } for i, m := range *monitors { //nolint:gocritic // range copy is acceptable here (*monitors)[i].StatsData = stats[m.ID] } return nil }