gosora/common/promotions.go
Azareal b6931fe16a Add registered time as a parameter for group promotions.
Run group promotions on group change.
Run group promotions on registration.
Load the CreatedAt field when users are loaded.
Set the default for last_ip properly.
Fix the default values in the group promotion form.
Add initial group promotion tests.

Add panel_group_promotion_registered_for phrase.
Add the registeredFor column to the users_groups_promotions table.

You will need to run the updater / patcher for this commit.
2020-02-09 20:00:08 +10:00

135 lines
4.5 KiB
Go

package common
import (
"database/sql"
//"log"
"time"
qgen "github.com/Azareal/Gosora/query_gen"
)
var GroupPromotions GroupPromotionStore
type GroupPromotion struct {
ID int
From int
To int
TwoWay bool
Level int
Posts int
MinTime int
RegisteredFor int
}
type GroupPromotionStore interface {
GetByGroup(gid int) (gps []*GroupPromotion, err error)
Get(id int) (*GroupPromotion, error)
PromoteIfEligible(u *User, level, posts int, registeredAt time.Time) error
Delete(id int) error
Create(from, to int, twoWay bool, level, posts, registeredFor int) (int, error)
}
type DefaultGroupPromotionStore struct {
getByGroup *sql.Stmt
get *sql.Stmt
delete *sql.Stmt
create *sql.Stmt
getByUser *sql.Stmt
getByUserMins *sql.Stmt
updateUser *sql.Stmt
updateGeneric *sql.Stmt
}
func NewDefaultGroupPromotionStore(acc *qgen.Accumulator) (*DefaultGroupPromotionStore, error) {
ugp := "users_groups_promotions"
prs := &DefaultGroupPromotionStore{
getByGroup: acc.Select(ugp).Columns("pid, from_gid, to_gid, two_way, level, posts, minTime, registeredFor").Where("from_gid=? OR to_gid=?").Prepare(),
get: acc.Select(ugp).Columns("from_gid, to_gid, two_way, level, posts, minTime, registeredFor").Where("pid=?").Prepare(),
delete: acc.Delete(ugp).Where("pid=?").Prepare(),
create: acc.Insert(ugp).Columns("from_gid, to_gid, two_way, level, posts, minTime, registeredFor").Fields("?,?,?,?,?,?,?").Prepare(),
//err := s.getByUser.QueryRow(u.Group, level, posts, mins).Scan(&g.ID, &g.To, &g.TwoWay, &g.Level, &g.Posts, &g.MinTime, &g.RegisteredFor)
//getByUserMins: acc.Select(ugp).Columns("pid, to_gid, two_way, level, posts, minTime, registeredFor").Where("from_gid=? AND level<=? AND posts<=?").DateOlderThanQ("registeredFor", "minute").Orderby("level DESC").Limit("1").Prepare(),
getByUserMins: acc.Select(ugp).Columns("pid, to_gid, two_way, level, posts, minTime, registeredFor").Where("from_gid=? AND level<=? AND posts<=? AND registeredFor<=?").Orderby("level DESC").Limit("1").Prepare(),
getByUser: acc.Select(ugp).Columns("pid, to_gid, two_way, level, posts, minTime, registeredFor").Where("from_gid=? AND level<=? AND posts<=?").Orderby("level DESC").Limit("1").Prepare(),
updateUser: acc.Update("users").Set("group=?").Where("group=? AND uid=?").Prepare(),
updateGeneric: acc.Update("users").Set("group=?").Where("group=? AND level>=? AND posts>=?").Prepare(),
}
AddScheduledFifteenMinuteTask(prs.Tick)
return prs, acc.FirstError()
}
func (s *DefaultGroupPromotionStore) Tick() error {
return nil
}
func (s *DefaultGroupPromotionStore) GetByGroup(gid int) (gps []*GroupPromotion, err error) {
rows, err := s.getByGroup.Query(gid, gid)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
g := &GroupPromotion{}
err := rows.Scan(&g.ID, &g.From, &g.To, &g.TwoWay, &g.Level, &g.Posts, &g.MinTime, &g.RegisteredFor)
if err != nil {
return nil, err
}
gps = append(gps, g)
}
return gps, rows.Err()
}
// TODO: Cache the group promotions to avoid hitting the database as much
func (s *DefaultGroupPromotionStore) Get(id int) (*GroupPromotion, error) {
/*g, err := s.cache.Get(id)
if err == nil {
return u, nil
}*/
g := &GroupPromotion{ID: id}
err := s.get.QueryRow(id).Scan(&g.From, &g.To, &g.TwoWay, &g.Level, &g.Posts, &g.MinTime, &g.RegisteredFor)
if err == nil {
//s.cache.Set(u)
}
return g, err
}
// TODO: Optimise this to avoid the query
func (s *DefaultGroupPromotionStore) PromoteIfEligible(u *User, level, posts int, registeredAt time.Time) error {
mins := time.Since(registeredAt).Minutes()
g := &GroupPromotion{From: u.Group}
//log.Printf("pre getByUserMins: %+v\n", u)
err := s.getByUserMins.QueryRow(u.Group, level, posts, mins).Scan(&g.ID, &g.To, &g.TwoWay, &g.Level, &g.Posts, &g.MinTime, &g.RegisteredFor)
if err == sql.ErrNoRows {
//log.Print("no matches found")
return nil
} else if err != nil {
return err
}
//log.Printf("g: %+v\n", g)
if g.RegisteredFor == 0 {
_, err = s.updateGeneric.Exec(g.To, g.From, g.Level, g.Posts)
} else {
_, err = s.updateUser.Exec(g.To, g.From, u.ID)
}
return err
}
func (s *DefaultGroupPromotionStore) Delete(id int) error {
_, err := s.delete.Exec(id)
return err
}
func (s *DefaultGroupPromotionStore) Create(from, to int, twoWay bool, level, posts, registeredFor int) (int, error) {
res, err := s.create.Exec(from, to, twoWay, level, posts, 0, registeredFor)
if err != nil {
return 0, err
}
lastID, err := res.LastInsertId()
return int(lastID), err
}