Implement daily stats

This commit is contained in:
Pijus Kamandulis
2025-06-23 21:35:16 +03:00
parent 4ddd9abd2e
commit b89a1a2a7e
8 changed files with 318 additions and 0 deletions

View File

@@ -3,7 +3,9 @@ package database
import (
"fmt"
"log"
"pool-stats/helpers"
"pool-stats/models"
"sort"
"time"
"github.com/ostafen/clover/v2"
@@ -16,6 +18,7 @@ const (
CollectionName = "shares"
TopSharesCollectionName = "TopShares"
TimeWindowHighShareCollectionName = "TimeWindowHighShareStat"
DailyStatsCollectionName = "DailyStats"
)
func InitDatabase(path string) (*clover.DB, error) {
@@ -79,6 +82,20 @@ func InitDatabase(path string) (*clover.DB, error) {
}
}
// Init DailyStats collection
hasDailyStatsCollection, err := db.HasCollection(DailyStatsCollectionName)
if err != nil {
return nil, fmt.Errorf("failed to check DailyStats collection: %v", err)
}
if !hasDailyStatsCollection {
if err := db.CreateCollection(DailyStatsCollectionName); err != nil {
return nil, fmt.Errorf("failed to create DailyStats collection: %v", err)
}
if err := db.CreateIndex(DailyStatsCollectionName, "Date"); err != nil {
return nil, fmt.Errorf("failed to create index for DailyStats: %v", err)
}
}
return db, nil
}
@@ -211,3 +228,97 @@ func SetTimeWindowHighShare(db *clover.DB, share models.TimeWindowHighShare) err
return nil
}
func ListSharesInTimeRange(db *clover.DB, since time.Time, till time.Time) []models.ShareLog {
lower := since.Unix()
upper := till.Unix()
results, err := db.FindAll(c.NewQuery(CollectionName).
Where(c.Field("CreateDate").GtEq(fmt.Sprint(lower)).
And(c.Field("CreateDate").LtEq(fmt.Sprint(upper)))).
Sort(c.SortOption{Field: "CreateDate", Direction: -1}))
if err != nil {
log.Printf("failed to list shares in time range: %v", err)
return nil
}
shareLogs := make([]models.ShareLog, len(results))
for idx, doc := range results {
var shareLog models.ShareLog
doc.Unmarshal(&shareLog)
shareLogs[idx] = shareLog
}
return shareLogs
}
// GetStatsForDay retrieves daily statistics for a given date
// Tries to find from DailyStats collection, calculates on the fly if not found and stores
func GetDailyStats(db *clover.DB, date time.Time) (*models.DailyStats, error) {
dateStr := date.Format(time.DateOnly)
// Check if stats already exist
isToday := dateStr == time.Now().UTC().Format(time.DateOnly)
existingDoc, err := db.FindFirst(c.NewQuery(DailyStatsCollectionName).
Where(c.Field("Date").Eq(dateStr)))
if !isToday && err == nil && existingDoc != nil {
var stats models.DailyStats
if err := existingDoc.Unmarshal(&stats); err != nil {
return nil, fmt.Errorf("failed to unmarshal daily stats: %v", err)
}
return &stats, nil
}
// Get shares in range
since := date.Truncate(24 * time.Hour)
till := since.Add(24 * time.Hour)
shares := ListSharesInTimeRange(db, since, till)
sort.Slice(shares, func(i, j int) bool {
return shares[i].SDiff > shares[j].SDiff
})
// Calculate daily stats
stats := &models.DailyStats{
Date: dateStr,
ShareCount: len(shares),
Workers: make(map[string]models.WorkerDailyStats),
}
if len(shares) > 0 {
stats.TopShare = shares[0]
stats.PoolHashrate = helpers.CalculateAverageHashrate(shares)
}
// Calculate worker stats
sharesByWorker := make(map[string][]models.ShareLog)
for _, share := range shares {
sharesByWorker[share.WorkerName] = append(sharesByWorker[share.WorkerName], share)
}
for workerName, workerShares := range sharesByWorker {
workerHashrate := helpers.CalculateAverageHashrate(workerShares)
workerTopShare := workerShares[0] // Already sorted by SDiff
stats.Workers[workerName] = models.WorkerDailyStats{
TopShare: workerTopShare,
Hashrate: workerHashrate,
Shares: len(workerShares),
}
}
// Insert or update the daily stats in the collection
doc := document.NewDocumentOf(stats)
if _, err := db.InsertOne(DailyStatsCollectionName, doc); err != nil {
return nil, fmt.Errorf("failed to insert daily stats: %v", err)
}
return stats, nil
}
func ClearDailyStats(db *clover.DB) error {
// Delete all documents in DailyStats collection
if err := db.Delete(c.NewQuery(DailyStatsCollectionName)); err != nil {
return fmt.Errorf("failed to clear DailyStats collection: %v", err)
}
return nil
}