website/pkg/models/usage_statistic.go

106 lines
3.5 KiB
Go
Raw Permalink Normal View History

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
}