statping/database/group.go

216 lines
4.2 KiB
Go
Raw Normal View History

2020-02-22 23:52:05 +00:00
package database
import (
"fmt"
"github.com/hunterlong/statping/types"
2020-02-24 05:53:15 +00:00
"github.com/hunterlong/statping/utils"
"net/http"
"net/url"
"strconv"
2020-02-22 23:52:05 +00:00
"time"
)
type GroupBy struct {
db Database
2020-02-24 05:53:15 +00:00
query *GroupQuery
2020-02-22 23:52:05 +00:00
}
type GroupByer interface {
2020-02-24 05:53:15 +00:00
ToTimeValue(interface{}) (*TimeVar, error)
2020-02-22 23:52:05 +00:00
}
2020-02-24 05:53:15 +00:00
type By string
func (b By) String() string {
return string(b)
}
type GroupQuery struct {
db Database
Start time.Time
End time.Time
Group string
Order string
Limit int
Offset int
FillEmpty bool
}
func (b GroupQuery) Database() Database {
return b.db
2020-02-22 23:52:05 +00:00
}
var (
2020-02-24 05:53:15 +00:00
ByCount = By("COUNT(id) as amount")
BySum = func(column string) By {
return By(fmt.Sprintf("SUM(%s) as amount", column))
2020-02-22 23:52:05 +00:00
}
2020-02-24 05:53:15 +00:00
ByAverage = func(column string) By {
return By(fmt.Sprintf("AVG(%s) as amount", column))
2020-02-22 23:52:05 +00:00
}
)
2020-02-24 05:53:15 +00:00
func (db *Db) GroupQuery(q *GroupQuery, by By) GroupByer {
dbQuery := db.MultipleSelects(
db.SelectByTime(q.Group),
by.String(),
).Group("timeframe")
2020-02-22 23:52:05 +00:00
2020-02-24 05:53:15 +00:00
return &GroupBy{dbQuery, q}
2020-02-22 23:52:05 +00:00
}
type TimeVar struct {
g *GroupBy
data []*TimeValue
}
2020-02-24 05:53:15 +00:00
func (t *TimeVar) ToValues() []*TimeValue {
return t.data
}
func (g *GroupBy) toFloatRows() []*TimeValue {
2020-02-22 23:52:05 +00:00
rows, err := g.db.Rows()
if err != nil {
2020-02-24 05:53:15 +00:00
return nil
2020-02-22 23:52:05 +00:00
}
var data []*TimeValue
for rows.Next() {
2020-02-24 05:53:15 +00:00
var timeframe time.Time
amount := float64(0)
rows.Scan(&timeframe, &amount)
newTs := types.FixedTime(timeframe, g.duration())
2020-02-22 23:52:05 +00:00
data = append(data, &TimeValue{
2020-02-24 05:53:15 +00:00
Timeframe: newTs,
2020-02-22 23:52:05 +00:00
Amount: amount,
})
}
2020-02-24 05:53:15 +00:00
return data
2020-02-22 23:52:05 +00:00
}
2020-02-24 05:53:15 +00:00
func (g *GroupBy) ToTimeValue(dbType interface{}) (*TimeVar, error) {
rows, err := g.db.Rows()
if err != nil {
return nil, err
}
var data []*TimeValue
for rows.Next() {
var timeframe time.Time
amount := float64(0)
rows.Scan(&timeframe, &amount)
newTs := types.FixedTime(timeframe, g.duration())
data = append(data, &TimeValue{
Timeframe: newTs,
Amount: amount,
2020-02-22 23:52:05 +00:00
})
}
2020-02-24 05:53:15 +00:00
return &TimeVar{g, data}, nil
2020-02-22 23:52:05 +00:00
}
2020-02-24 05:53:15 +00:00
func (t *TimeVar) FillMissing(current, end time.Time) []*TimeValue {
timeMap := make(map[string]float64)
2020-02-22 23:52:05 +00:00
var validSet []*TimeValue
2020-02-24 05:53:15 +00:00
dur := t.g.duration()
2020-02-22 23:52:05 +00:00
for _, v := range t.data {
2020-02-24 05:53:15 +00:00
timeMap[v.Timeframe] = v.Amount
2020-02-22 23:52:05 +00:00
}
2020-02-24 05:53:15 +00:00
currentStr := types.FixedTime(current, t.g.duration())
2020-02-22 23:52:05 +00:00
for {
2020-02-24 05:53:15 +00:00
var amount float64
if timeMap[currentStr] != 0 {
amount = timeMap[currentStr]
2020-02-22 23:52:05 +00:00
}
validSet = append(validSet, &TimeValue{
2020-02-24 05:53:15 +00:00
Timeframe: currentStr,
2020-02-22 23:52:05 +00:00
Amount: amount,
})
2020-02-24 05:53:15 +00:00
if current.After(end) {
2020-02-22 23:52:05 +00:00
break
}
2020-02-24 05:53:15 +00:00
current = current.Add(dur)
currentStr = types.FixedTime(current, t.g.duration())
2020-02-22 23:52:05 +00:00
}
return validSet
}
func (g *GroupBy) duration() time.Duration {
switch g.query.Group {
case "second":
2020-02-24 05:53:15 +00:00
return types.Second
2020-02-22 23:52:05 +00:00
case "minute":
2020-02-24 05:53:15 +00:00
return types.Minute
2020-02-22 23:52:05 +00:00
case "hour":
2020-02-24 05:53:15 +00:00
return types.Hour
2020-02-22 23:52:05 +00:00
case "day":
2020-02-24 05:53:15 +00:00
return types.Day
2020-02-22 23:52:05 +00:00
case "month":
2020-02-24 05:53:15 +00:00
return types.Month
2020-02-22 23:52:05 +00:00
case "year":
2020-02-24 05:53:15 +00:00
return types.Year
2020-02-22 23:52:05 +00:00
default:
2020-02-24 05:53:15 +00:00
return types.Hour
}
}
func ParseQueries(r *http.Request, db Database) *GroupQuery {
fields := parseGet(r)
grouping := fields.Get("group")
if grouping == "" {
grouping = "hour"
}
startField := utils.ToInt(fields.Get("start"))
endField := utils.ToInt(fields.Get("end"))
limit := utils.ToInt(fields.Get("limit"))
offset := utils.ToInt(fields.Get("offset"))
fill, _ := strconv.ParseBool(fields.Get("fill"))
orderBy := fields.Get("order")
if limit == 0 {
limit = 10000
}
query := &GroupQuery{
Start: time.Unix(startField, 0).UTC(),
End: time.Unix(endField, 0).UTC(),
Group: grouping,
Order: orderBy,
Limit: int(limit),
Offset: int(offset),
FillEmpty: fill,
2020-02-22 23:52:05 +00:00
}
2020-02-24 05:53:15 +00:00
if query.Limit != 0 {
db = db.Limit(query.Limit)
}
if query.Offset > 0 {
db = db.Offset(query.Offset)
}
if !query.Start.IsZero() && !query.End.IsZero() {
db = db.Where("created_at BETWEEN ? AND ?", db.FormatTime(query.Start), db.FormatTime(query.End))
} else {
if !query.Start.IsZero() {
db = db.Where("created_at > ?", db.FormatTime(query.Start))
}
if !query.End.IsZero() {
db = db.Where("created_at < ?", db.FormatTime(query.End))
}
}
if query.Order != "" {
db = db.Order(query.Order)
}
query.db = db.Debug()
return query
}
func parseForm(r *http.Request) url.Values {
r.ParseForm()
return r.PostForm
}
func parseGet(r *http.Request) url.Values {
r.ParseForm()
return r.Form
2020-02-22 23:52:05 +00:00
}