start alerts
This commit is contained in:
@@ -195,3 +195,50 @@ type VehicleAttachment struct {
|
|||||||
VehicleID string `gorm:"primaryKey" json:"vehicleId"`
|
VehicleID string `gorm:"primaryKey" json:"vehicleId"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VehicleAlert struct {
|
||||||
|
Base
|
||||||
|
VehicleID string `json:"vehicleId"`
|
||||||
|
Vehicle Vehicle `json:"-"`
|
||||||
|
UserID string `json:"userId"`
|
||||||
|
User User `json:"user"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Comments string `json:"comments"`
|
||||||
|
StartDate time.Time `json:"date"`
|
||||||
|
StartOdoReading int `json:"startOdoReading"`
|
||||||
|
DistanceUnit DistanceUnit `json:"distanceUnit"`
|
||||||
|
AlertFrequency AlertFrequency `json:"alertFrequency"`
|
||||||
|
OdoFrequency int `json:"odoFrequency"`
|
||||||
|
DayFrequency int `json:"dayFrequency"`
|
||||||
|
AlertAllUsers bool `json:"alertAllUsers"`
|
||||||
|
IsActive bool `json:"isActive"`
|
||||||
|
EndDate *time.Time `json:"endDate"`
|
||||||
|
AlertType AlertType `json:"alertType"`
|
||||||
|
}
|
||||||
|
type AlertOccurance struct {
|
||||||
|
Base
|
||||||
|
VehicleID string `json:"vehicleId"`
|
||||||
|
Vehicle Vehicle `json:"-"`
|
||||||
|
VehicleAlertID string `json:"vehicleAlertId"`
|
||||||
|
VehicleAlert VehicleAlert `json:"-"`
|
||||||
|
UserID string `json:"userId"`
|
||||||
|
User User `json:"-"`
|
||||||
|
OdoReading int `json:"odoReading"`
|
||||||
|
Date *time.Time `json:"date"`
|
||||||
|
ProcessDate *time.Time `json:"processDate"`
|
||||||
|
AlertProcessType AlertType `json:"alertProcessType"`
|
||||||
|
CompleteDate *time.Time `json:"completeDate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Notification struct {
|
||||||
|
Base
|
||||||
|
Title string `json:"title"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
UserID string `json:"userId"`
|
||||||
|
VehicleID string `json:"vehicleId"`
|
||||||
|
User User `json:"-"`
|
||||||
|
Date time.Time `json:"date"`
|
||||||
|
ReadDate *time.Time `json:"readDate"`
|
||||||
|
ParentID string `json:"parentId"`
|
||||||
|
ParentType string `json:"parentType"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -160,6 +160,11 @@ func GetFillupsByVehicleId(id string) (*[]Fillup, error) {
|
|||||||
result := DB.Preload(clause.Associations).Order("date desc").Find(&obj, &Fillup{VehicleID: id})
|
result := DB.Preload(clause.Associations).Order("date desc").Find(&obj, &Fillup{VehicleID: id})
|
||||||
return &obj, result.Error
|
return &obj, result.Error
|
||||||
}
|
}
|
||||||
|
func GetLatestFillupsByVehicleId(id string) (*Fillup, error) {
|
||||||
|
var obj Fillup
|
||||||
|
result := DB.Preload(clause.Associations).Order("date desc").First(&obj, &Fillup{VehicleID: id})
|
||||||
|
return &obj, result.Error
|
||||||
|
}
|
||||||
func GetFillupsByVehicleIdSince(id string, since time.Time) (*[]Fillup, error) {
|
func GetFillupsByVehicleIdSince(id string, since time.Time) (*[]Fillup, error) {
|
||||||
var obj []Fillup
|
var obj []Fillup
|
||||||
result := DB.Where("date >= ? AND vehicle_id = ?", since, id).Preload(clause.Associations).Order("date desc").Find(&obj)
|
result := DB.Where("date >= ? AND vehicle_id = ?", since, id).Preload(clause.Associations).Order("date desc").Find(&obj)
|
||||||
@@ -190,6 +195,11 @@ func GetExpensesByVehicleId(id string) (*[]Expense, error) {
|
|||||||
result := DB.Preload(clause.Associations).Order("date desc").Find(&obj, &Expense{VehicleID: id})
|
result := DB.Preload(clause.Associations).Order("date desc").Find(&obj, &Expense{VehicleID: id})
|
||||||
return &obj, result.Error
|
return &obj, result.Error
|
||||||
}
|
}
|
||||||
|
func GetLatestExpenseByVehicleId(id string) (*Expense, error) {
|
||||||
|
var obj Expense
|
||||||
|
result := DB.Preload(clause.Associations).Order("date desc").First(&obj, &Expense{VehicleID: id})
|
||||||
|
return &obj, result.Error
|
||||||
|
}
|
||||||
func GetExpenseById(id string) (*Expense, error) {
|
func GetExpenseById(id string) (*Expense, error) {
|
||||||
var obj Expense
|
var obj Expense
|
||||||
result := DB.Preload(clause.Associations).First(&obj, "id=?", id)
|
result := DB.Preload(clause.Associations).First(&obj, "id=?", id)
|
||||||
@@ -271,6 +281,29 @@ func GetVehicleAttachments(vehicleId string) (*[]Attachment, error) {
|
|||||||
}
|
}
|
||||||
return &attachments, nil
|
return &attachments, nil
|
||||||
}
|
}
|
||||||
|
func GeAlertById(id string) (*VehicleAlert, error) {
|
||||||
|
var alert VehicleAlert
|
||||||
|
result := DB.Preload(clause.Associations).First(&alert, "id=?", id)
|
||||||
|
return &alert, result.Error
|
||||||
|
}
|
||||||
|
func GetAlertOccurenceByAlertId(id string) (*[]AlertOccurance, error) {
|
||||||
|
var alertOccurance []AlertOccurance
|
||||||
|
result := DB.Preload(clause.Associations).Order("created_at desc").Find(&alertOccurance, "vehicle_alert_id=?", id)
|
||||||
|
return &alertOccurance, result.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUnprocessedAlertOccurances() (*[]AlertOccurance, error) {
|
||||||
|
var alertOccurance []AlertOccurance
|
||||||
|
result := DB.Preload(clause.Associations).Order("created_at desc").Find(&alertOccurance, "process_date is NULL")
|
||||||
|
return &alertOccurance, result.Error
|
||||||
|
}
|
||||||
|
func MarkAlertOccuranceAsProcessed(id string, alertProcessType AlertType, date time.Time) error {
|
||||||
|
tx := DB.Debug().Model(&AlertOccurance{}).Where("id= ?", id).
|
||||||
|
Update("alert_process_type", alertProcessType).
|
||||||
|
Update("process_date", date)
|
||||||
|
return tx.Error
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func UpdateSettings(setting *Setting) error {
|
func UpdateSettings(setting *Setting) error {
|
||||||
tx := DB.Save(&setting)
|
tx := DB.Save(&setting)
|
||||||
|
|||||||
@@ -36,6 +36,21 @@ const (
|
|||||||
USER
|
USER
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type AlertFrequency int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ONETIME AlertFrequency = iota
|
||||||
|
RECURRING
|
||||||
|
)
|
||||||
|
|
||||||
|
type AlertType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
DISTANCE AlertType = iota
|
||||||
|
TIME
|
||||||
|
BOTH
|
||||||
|
)
|
||||||
|
|
||||||
type EnumDetail struct {
|
type EnumDetail struct {
|
||||||
Short string `json:"short"`
|
Short string `json:"short"`
|
||||||
Long string `json:"long"`
|
Long string `json:"long"`
|
||||||
|
|||||||
21
server/models/alert.go
Normal file
21
server/models/alert.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/akhilrex/hammond/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreateAlertModel struct {
|
||||||
|
Comments string `json:"comments"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
StartDate time.Time `json:"date"`
|
||||||
|
StartOdoReading int `json:"startOdoReading"`
|
||||||
|
DistanceUnit *db.DistanceUnit `json:"distanceUnit"`
|
||||||
|
AlertFrequency *db.AlertFrequency `json:"alertFrequency"`
|
||||||
|
OdoFrequency int `json:"odoFrequency"`
|
||||||
|
DayFrequency int `json:"dayFrequency"`
|
||||||
|
AlertAllUsers bool `json:"alertAllUsers"`
|
||||||
|
IsActive bool `json:"isActive"`
|
||||||
|
AlertType *db.AlertType `json:"alertType"`
|
||||||
|
}
|
||||||
164
server/service/alertSevice.go
Normal file
164
server/service/alertSevice.go
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/akhilrex/hammond/db"
|
||||||
|
"github.com/akhilrex/hammond/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateAlert(model models.CreateAlertModel, vehicleId, userId string) (*db.VehicleAlert, error) {
|
||||||
|
alert := db.VehicleAlert{
|
||||||
|
VehicleID: vehicleId,
|
||||||
|
UserID: userId,
|
||||||
|
Title: model.Title,
|
||||||
|
Comments: model.Comments,
|
||||||
|
StartDate: model.StartDate,
|
||||||
|
StartOdoReading: model.StartOdoReading,
|
||||||
|
DistanceUnit: *model.DistanceUnit,
|
||||||
|
AlertFrequency: *model.AlertFrequency,
|
||||||
|
OdoFrequency: model.OdoFrequency,
|
||||||
|
DayFrequency: model.DayFrequency,
|
||||||
|
AlertAllUsers: model.AlertAllUsers,
|
||||||
|
IsActive: model.IsActive,
|
||||||
|
AlertType: *model.AlertType,
|
||||||
|
}
|
||||||
|
tx := db.DB.Create(&alert)
|
||||||
|
if tx.Error != nil {
|
||||||
|
return nil, tx.Error
|
||||||
|
}
|
||||||
|
go CreateAlertInstance(alert.ID)
|
||||||
|
return &alert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateAlertInstance(alertId string) error {
|
||||||
|
alert, err := db.GeAlertById(alertId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
existingOccurence, err := db.GetAlertOccurenceByAlertId(alertId)
|
||||||
|
var lastOccurance db.AlertOccurance
|
||||||
|
|
||||||
|
if len(*existingOccurence) > 0 {
|
||||||
|
lastOccurance = (*existingOccurence)[0]
|
||||||
|
|
||||||
|
if alert.AlertFrequency == db.ONETIME {
|
||||||
|
return errors.New("Only single occurance is possible for this kind of alert")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
users := []string{alert.UserID}
|
||||||
|
if alert.AlertAllUsers {
|
||||||
|
allUsers, err := db.GetVehicleUsers(alert.VehicleID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
users = make([]string, len(*allUsers))
|
||||||
|
for i, user := range *allUsers {
|
||||||
|
users[i] = user.UserID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, userId := range users {
|
||||||
|
model := db.AlertOccurance{
|
||||||
|
VehicleID: alert.VehicleID,
|
||||||
|
UserID: userId,
|
||||||
|
VehicleAlertID: alertId,
|
||||||
|
}
|
||||||
|
|
||||||
|
if alert.AlertType == db.DISTANCE || alert.AlertType == db.BOTH {
|
||||||
|
model.OdoReading = alert.StartOdoReading + alert.OdoFrequency
|
||||||
|
if &lastOccurance != nil {
|
||||||
|
model.OdoReading = lastOccurance.OdoReading + alert.OdoFrequency
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if alert.AlertType == db.TIME || alert.AlertType == db.BOTH {
|
||||||
|
date := alert.StartDate.Add(time.Duration(alert.DayFrequency) * 24 * time.Hour)
|
||||||
|
if &lastOccurance != nil {
|
||||||
|
date = lastOccurance.Date.Add(time.Duration(alert.DayFrequency) * 24 * time.Hour)
|
||||||
|
}
|
||||||
|
model.Date = &date
|
||||||
|
}
|
||||||
|
tx := db.DB.Create(&model)
|
||||||
|
if tx.Error != nil {
|
||||||
|
return tx.Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProcessAlertOccurance(occurance db.AlertOccurance, today time.Time) error {
|
||||||
|
if occurance.ProcessDate != nil {
|
||||||
|
return errors.New("Alert occurence already processed")
|
||||||
|
}
|
||||||
|
alert := occurance.VehicleAlert
|
||||||
|
if !alert.IsActive {
|
||||||
|
return errors.New("Alert is not active")
|
||||||
|
}
|
||||||
|
notification := db.Notification{
|
||||||
|
Title: alert.Title,
|
||||||
|
Content: alert.Comments,
|
||||||
|
UserID: occurance.UserID,
|
||||||
|
VehicleID: occurance.VehicleID,
|
||||||
|
Date: today,
|
||||||
|
ParentID: occurance.ID,
|
||||||
|
ParentType: "AlertOccurance",
|
||||||
|
}
|
||||||
|
var alertProcessType db.AlertType
|
||||||
|
if alert.AlertType == db.DISTANCE || alert.AlertType == db.BOTH {
|
||||||
|
odoReading, err := GetLatestOdoReadingForVehicle(occurance.VehicleID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if odoReading >= occurance.OdoReading {
|
||||||
|
alertProcessType = db.DISTANCE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if alert.AlertType == db.TIME || alert.AlertType == db.BOTH {
|
||||||
|
if occurance.Date.Before(today) {
|
||||||
|
alertProcessType = db.TIME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db.DB.Create(¬ification)
|
||||||
|
return db.MarkAlertOccuranceAsProcessed(occurance.ID, alertProcessType, today)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindAlertOccurancesToProcess(today time.Time) ([]db.AlertOccurance, error) {
|
||||||
|
occurances, err := db.GetUnprocessedAlertOccurances()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(*occurances) == 0 {
|
||||||
|
return make([]db.AlertOccurance, 0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var toReturn []db.AlertOccurance
|
||||||
|
|
||||||
|
for _, occurance := range *occurances {
|
||||||
|
alert := occurance.VehicleAlert
|
||||||
|
if !alert.IsActive {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if alert.AlertType == db.DISTANCE || alert.AlertType == db.BOTH {
|
||||||
|
odoReading, err := GetLatestOdoReadingForVehicle(occurance.VehicleID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if odoReading >= occurance.OdoReading {
|
||||||
|
toReturn = append(toReturn, occurance)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if alert.AlertType == db.TIME || alert.AlertType == db.BOTH {
|
||||||
|
if occurance.Date.Before(today) {
|
||||||
|
toReturn = append(toReturn, occurance)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return toReturn, nil
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/akhilrex/hammond/db"
|
"github.com/akhilrex/hammond/db"
|
||||||
"github.com/akhilrex/hammond/models"
|
"github.com/akhilrex/hammond/models"
|
||||||
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -243,6 +244,24 @@ func GetDistinctFuelSubtypesForVehicle(vehicleId string) ([]string, error) {
|
|||||||
return names, tx.Error
|
return names, tx.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetLatestOdoReadingForVehicle(vehicleId string) (int, error) {
|
||||||
|
odoReading := 0
|
||||||
|
latestFillup, err := db.GetLatestExpenseByVehicleId(vehicleId)
|
||||||
|
if err != nil && err != gorm.ErrRecordNotFound {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
odoReading = latestFillup.OdoReading
|
||||||
|
|
||||||
|
latestExpense, err := db.GetLatestExpenseByVehicleId(vehicleId)
|
||||||
|
if err != nil && err != gorm.ErrRecordNotFound {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if latestExpense.OdoReading > odoReading {
|
||||||
|
odoReading = latestExpense.OdoReading
|
||||||
|
}
|
||||||
|
return odoReading, nil
|
||||||
|
}
|
||||||
|
|
||||||
func GetUserStats(userId string, model models.UserStatsQueryModel) ([]models.VehicleStatsModel, error) {
|
func GetUserStats(userId string, model models.UserStatsQueryModel) ([]models.VehicleStatsModel, error) {
|
||||||
|
|
||||||
vehicles, err := GetUserVehicles(userId)
|
vehicles, err := GetUserVehicles(userId)
|
||||||
|
|||||||
Reference in New Issue
Block a user