106 lines
3.5 KiB
Go
106 lines
3.5 KiB
Go
|
package models
|
||
|
|
||
|
import "time"
|
||
|
|
||
|
/*
|
||
|
UsageStatistic holds basic analytics points for things like daily/monthly active user counts.
|
||
|
|
||
|
Generally, there will be one UserStatistic row for each combination of a UserID and Type for
|
||
|
each calendar day of the year. Type names may be like "dau" to log daily logins (Daily Active User),
|
||
|
or "chat" to log daily chat room users.
|
||
|
|
||
|
If a user logs in multiple times in the same day, their existing UsageStatistic for that day
|
||
|
is reused and the Counter is incremented. So if a user joins chat 3 times on the same day, there
|
||
|
will be a single row for that date for that user, but with a Counter of 3 in that case.
|
||
|
|
||
|
This makes it easier to query for aggregate reports on daily/monthly active users since each
|
||
|
row/event type combo only appears once per user per day.
|
||
|
*/
|
||
|
type UsageStatistic struct {
|
||
|
ID uint64 `gorm:"primaryKey"`
|
||
|
UserID uint64 `gorm:"uniqueIndex:idx_usage_statistics"`
|
||
|
Type string `gorm:"uniqueIndex:idx_usage_statistics"`
|
||
|
Date string `gorm:"uniqueIndex:idx_usage_statistics"` // unique days, yyyy-mm-dd format.
|
||
|
Counter uint64
|
||
|
CreatedAt time.Time `gorm:"index"` // full timestamps
|
||
|
UpdatedAt time.Time `gorm:"index"`
|
||
|
}
|
||
|
|
||
|
// Options for UsageStatistic Type values.
|
||
|
const (
|
||
|
UsageStatisticDailyVisit = "dau" // daily active user counter
|
||
|
UsageStatisticChatEntry = "chat" // daily chat room users
|
||
|
UsageStatisticForumUser = "forum" // daily forum users (when they open a thread)
|
||
|
UsageStatisticGalleryUser = "gallery" // daily Site Gallery user (when viewing the site gallery)
|
||
|
)
|
||
|
|
||
|
// LogDailyActiveUser will ping a UserStatistic for the current user to mark them present for the day.
|
||
|
func LogDailyActiveUser(user *User) error {
|
||
|
var (
|
||
|
date = time.Now().Format(time.DateOnly)
|
||
|
_, err = IncrementUsageStatistic(user, UsageStatisticDailyVisit, date)
|
||
|
)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// LogDailyChatUser will ping a UserStatistic for the current user to mark them as having used the chat room today.
|
||
|
func LogDailyChatUser(user *User) error {
|
||
|
var (
|
||
|
date = time.Now().Format(time.DateOnly)
|
||
|
_, err = IncrementUsageStatistic(user, UsageStatisticChatEntry, date)
|
||
|
)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// LogDailyForumUser will ping a UserStatistic for the current user to mark them as having used the forums today.
|
||
|
func LogDailyForumUser(user *User) error {
|
||
|
var (
|
||
|
date = time.Now().Format(time.DateOnly)
|
||
|
_, err = IncrementUsageStatistic(user, UsageStatisticForumUser, date)
|
||
|
)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// LogDailyGalleryUser will ping a UserStatistic for the current user to mark them as having used the site gallery today.
|
||
|
func LogDailyGalleryUser(user *User) error {
|
||
|
var (
|
||
|
date = time.Now().Format(time.DateOnly)
|
||
|
_, err = IncrementUsageStatistic(user, UsageStatisticGalleryUser, date)
|
||
|
)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// GetUsageStatistic looks up a user statistic.
|
||
|
func GetUsageStatistic(user *User, statType, date string) (*UsageStatistic, error) {
|
||
|
var (
|
||
|
result = &UsageStatistic{}
|
||
|
res = DB.Model(&UsageStatistic{}).Where(
|
||
|
"user_id = ? AND type = ? AND date = ?",
|
||
|
user.ID, statType, date,
|
||
|
).First(&result)
|
||
|
)
|
||
|
return result, res.Error
|
||
|
}
|
||
|
|
||
|
// IncrementUsageStatistic finds or creates a UserStatistic type and increments the counter.
|
||
|
func IncrementUsageStatistic(user *User, statType, date string) (*UsageStatistic, error) {
|
||
|
user.muStatistic.Lock()
|
||
|
defer user.muStatistic.Unlock()
|
||
|
|
||
|
// Is there an existing row?
|
||
|
stat, err := GetUsageStatistic(user, statType, date)
|
||
|
if err != nil {
|
||
|
stat = &UsageStatistic{
|
||
|
UserID: user.ID,
|
||
|
Type: statType,
|
||
|
Counter: 0,
|
||
|
Date: date,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Update and save it.
|
||
|
stat.Counter++
|
||
|
err = DB.Save(stat).Error
|
||
|
return stat, err
|
||
|
}
|