289 lines
6.3 KiB
Go
289 lines
6.3 KiB
Go
package main
|
|
|
|
import (
|
|
// External
|
|
werr "git.gibonuddevalla.se/go/wrappederror"
|
|
|
|
// Standard
|
|
"encoding/json"
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func init() {
|
|
fmt.Printf("")
|
|
}
|
|
|
|
type Schedule struct {
|
|
ID int
|
|
UserID int `json:"user_id" db:"user_id"`
|
|
Node Node
|
|
ScheduleUUID string `db:"schedule_uuid"`
|
|
Time time.Time
|
|
RemindMinutes int `db:"remind_minutes"`
|
|
Description string
|
|
Acknowledged bool
|
|
}
|
|
|
|
func scheduleHandler() { // {{{
|
|
// Wait for the approximate minute.
|
|
wait := 60000 - time.Now().Sub(time.Now().Truncate(time.Minute)).Milliseconds()
|
|
logger.Info("schedule", "wait", wait/1000)
|
|
time.Sleep(time.Millisecond * time.Duration(wait))
|
|
tick := time.NewTicker(time.Minute)
|
|
for {
|
|
schedules := ExpiredSchedules()
|
|
for _, event := range schedules {
|
|
notificationManager.Send(
|
|
event.UserID,
|
|
event.ScheduleUUID,
|
|
[]byte(
|
|
fmt.Sprintf(
|
|
"%s\n%s",
|
|
event.Time.Format("2006-01-02 15:04"),
|
|
event.Description,
|
|
),
|
|
),
|
|
)
|
|
}
|
|
<-tick.C
|
|
}
|
|
} // }}}
|
|
|
|
func ScanForSchedules(timezone string, content string) (schedules []Schedule) { // {{{
|
|
schedules = []Schedule{}
|
|
|
|
rxp := regexp.MustCompile(`\{\s*([0-9]{4}-[0-9]{2}-[0-9]{2}\s+[0-9]{2}:[0-9]{2}(?::[0-9]{2})?)\s*\,\s*(?:(\d+)\s*(h|min)\s*,)?\s*([^\]]+?)\s*\}`)
|
|
foundSchedules := rxp.FindAllStringSubmatch(content, -1)
|
|
|
|
for _, data := range foundSchedules {
|
|
// Missing seconds
|
|
if strings.Count(data[1], ":") == 1 {
|
|
data[1] = data[1] + ":00"
|
|
}
|
|
|
|
logger.Info("time", "parse", data[1])
|
|
location, err := time.LoadLocation(timezone)
|
|
if err != nil {
|
|
err = werr.Wrap(err).WithCode("002-00010")
|
|
return
|
|
}
|
|
|
|
timestamp, err := time.ParseInLocation("2006-01-02 15:04:05", data[1], location)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
// Reminder
|
|
var remindMinutes int
|
|
if data[2] != "" && data[3] != "" {
|
|
value, _ := strconv.Atoi(data[2])
|
|
unit := strings.ToLower(data[3])
|
|
switch unit {
|
|
case "min":
|
|
remindMinutes = value
|
|
case "h":
|
|
remindMinutes = value * 60
|
|
}
|
|
}
|
|
|
|
schedule := Schedule{
|
|
Time: timestamp,
|
|
RemindMinutes: remindMinutes,
|
|
Description: data[4],
|
|
}
|
|
schedules = append(schedules, schedule)
|
|
}
|
|
|
|
return
|
|
} // }}}
|
|
func RetrieveSchedules(userID int, nodeID int) (schedules []Schedule, err error) { // {{{
|
|
schedules = []Schedule{}
|
|
|
|
res := service.Db.Conn.QueryRow(`
|
|
WITH schedule_events AS (
|
|
SELECT
|
|
s.id,
|
|
s.user_id,
|
|
json_build_object('id', s.node_id) AS node,
|
|
s.schedule_uuid,
|
|
(time - MAKE_INTERVAL(mins => s.remind_minutes)) AT TIME ZONE u.timezone AS time,
|
|
s.description,
|
|
s.acknowledged
|
|
FROM schedule s
|
|
INNER JOIN _webservice.user u ON s.user_id = u.id
|
|
WHERE
|
|
user_id=$1 AND
|
|
CASE
|
|
WHEN $2 > 0 THEN node_id = $2
|
|
ELSE true
|
|
END
|
|
)
|
|
SELECT
|
|
COALESCE(jsonb_agg(s.*), '[]'::jsonb)
|
|
FROM schedule_events s
|
|
`,
|
|
userID,
|
|
nodeID,
|
|
)
|
|
|
|
var data []byte
|
|
err = res.Scan(&data)
|
|
if err != nil {
|
|
err = werr.Wrap(err).WithCode("002-000E")
|
|
return
|
|
}
|
|
|
|
err = json.Unmarshal(data, &schedules)
|
|
return
|
|
} // }}}
|
|
|
|
func (a Schedule) IsEqual(b Schedule) bool { // {{{
|
|
return a.UserID == b.UserID &&
|
|
a.Node.ID == b.Node.ID &&
|
|
a.Time.Equal(b.Time) &&
|
|
a.RemindMinutes == b.RemindMinutes &&
|
|
a.Description == b.Description
|
|
} // }}}
|
|
func (s *Schedule) Insert(queryable Queryable) error { // {{{
|
|
res := queryable.QueryRow(`
|
|
INSERT INTO schedule(user_id, node_id, time, remind_minutes, description)
|
|
VALUES($1, $2, $3, $4, $5)
|
|
RETURNING id
|
|
`,
|
|
s.UserID,
|
|
s.Node.ID,
|
|
s.Time.Format("2006-01-02 15:04:05"),
|
|
s.RemindMinutes,
|
|
s.Description,
|
|
)
|
|
|
|
err := res.Scan(&s.ID)
|
|
if err != nil {
|
|
err = werr.Wrap(err).WithCode("002-000D")
|
|
}
|
|
return err
|
|
} // }}}
|
|
func (s *Schedule) Delete(queryable Queryable) error { // {{{
|
|
_, err := queryable.Exec(`
|
|
DELETE FROM schedule
|
|
WHERE
|
|
user_id = $1 AND
|
|
id = $2
|
|
`,
|
|
s.UserID,
|
|
s.ID,
|
|
)
|
|
return err
|
|
} // }}}
|
|
|
|
func ExpiredSchedules() (schedules []Schedule) { // {{{
|
|
schedules = []Schedule{}
|
|
|
|
res, err := service.Db.Conn.Queryx(`
|
|
SELECT
|
|
s.id,
|
|
s.user_id,
|
|
s.node_id,
|
|
s.schedule_uuid,
|
|
(s.time - MAKE_INTERVAL(mins => s.remind_minutes)) AT TIME ZONE u.timezone AS time,
|
|
s.description
|
|
FROM schedule s
|
|
INNER JOIN _webservice.user u ON s.user_id = u.id
|
|
WHERE
|
|
(time - MAKE_INTERVAL(mins => remind_minutes)) AT TIME ZONE u.timezone < NOW() AND
|
|
NOT acknowledged
|
|
ORDER BY
|
|
time ASC
|
|
`)
|
|
if err != nil {
|
|
err = werr.Wrap(err).WithCode("002-0009")
|
|
return
|
|
}
|
|
defer res.Close()
|
|
|
|
for res.Next() {
|
|
s := Schedule{}
|
|
if err = res.Scan(&s.ID, &s.UserID, &s.Node.ID, &s.ScheduleUUID, &s.Time, &s.Description); err != nil {
|
|
werr.Wrap(err).WithCode("002-000a")
|
|
continue
|
|
}
|
|
schedules = append(schedules, s)
|
|
}
|
|
return
|
|
} // }}}
|
|
func FutureSchedules(userID int, nodeID int, start time.Time, end time.Time) (schedules []Schedule, err error) { // {{{
|
|
schedules = []Schedule{}
|
|
|
|
var foo string
|
|
row := service.Db.Conn.QueryRow(`SELECT TO_CHAR($1::date AT TIME ZONE 'UTC', 'yyyy-mm-dd HH24:MI')`, start)
|
|
err = row.Scan(&foo)
|
|
if err != nil {
|
|
return
|
|
}
|
|
logger.Info("FOO", "date", foo)
|
|
|
|
res := service.Db.Conn.QueryRow(`
|
|
WITH schedule_events AS (
|
|
SELECT
|
|
s.id,
|
|
s.user_id,
|
|
jsonb_build_object(
|
|
'id', n.id,
|
|
'name', n.name,
|
|
'updated', n.updated
|
|
) AS node,
|
|
s.schedule_uuid,
|
|
time AT TIME ZONE u.timezone AS time,
|
|
s.description,
|
|
s.acknowledged,
|
|
s.remind_minutes AS RemindMinutes
|
|
FROM schedule s
|
|
INNER JOIN _webservice.user u ON s.user_id = u.id
|
|
INNER JOIN node n ON s.node_id = n.id
|
|
WHERE
|
|
s.user_id = $1 AND
|
|
(
|
|
CASE
|
|
WHEN $2 > 0 THEN n.id = $2
|
|
ELSE true
|
|
END
|
|
) AND (
|
|
CASE WHEN TO_CHAR($3::date, 'yyyy-mm-dd HH24:MI') = '0001-01-01 00:00' THEN TRUE
|
|
ELSE (s.time AT TIME ZONE u.timezone) >= $3
|
|
END
|
|
) AND (
|
|
CASE WHEN TO_CHAR($4::date, 'yyyy-mm-dd HH24:MI') = '0001-01-01 00:00' THEN TRUE
|
|
ELSE (s.time AT TIME ZONE u.timezone) <= $4
|
|
END
|
|
) AND
|
|
time >= NOW() AND
|
|
NOT acknowledged
|
|
)
|
|
SELECT
|
|
COALESCE(jsonb_agg(s.*), '[]'::jsonb)
|
|
FROM schedule_events s
|
|
`,
|
|
userID,
|
|
nodeID,
|
|
start,
|
|
end,
|
|
)
|
|
var j []byte
|
|
err = res.Scan(&j)
|
|
if err != nil {
|
|
err = werr.Wrap(err).WithCode("002-000B").WithData(userID)
|
|
return
|
|
}
|
|
|
|
err = json.Unmarshal(j, &schedules)
|
|
if err != nil {
|
|
err = werr.Wrap(err).WithCode("002-0010").WithData(string(j)).Log()
|
|
return
|
|
}
|
|
|
|
return
|
|
} // }}}
|