This commit is contained in:
2025-09-18 05:40:45 -07:00
parent 030ad360c2
commit 7da16e55a9
20 changed files with 843 additions and 219 deletions

View File

@@ -1,6 +1,7 @@
package stats
import (
"encoding/json"
"fmt"
"strings"
"time"
@@ -20,26 +21,34 @@ type BaseStats struct {
func (b *BaseStats) List(end time.Time, period int, client *client.Client) ([]interface{}, error) {
endDate := utils.FormatEndDate(end)
var allData []interface{}
var errs []error
if period > b.PageSize {
// Handle pagination - get first page
page, err := b.fetchPage(endDate, b.PageSize, client)
if err != nil || len(page) == 0 {
return page, err
for period > 0 {
pageSize := b.PageSize
if period < pageSize {
pageSize = period
}
// Get remaining pages recursively
remainingStart := endDate.AddDate(0, 0, -b.PageSize)
remainingPeriod := period - b.PageSize
remainingData, err := b.List(remainingStart, remainingPeriod, client)
page, err := b.fetchPage(endDate, pageSize, client)
if err != nil {
return page, err
errs = append(errs, err)
// Continue to next page even if current fails
} else {
allData = append(page, allData...)
}
return append(remainingData, page...), nil
// Move to previous page
endDate = endDate.AddDate(0, 0, -pageSize)
period -= pageSize
}
return b.fetchPage(endDate, period, client)
// Return partial data with aggregated errors
var finalErr error
if len(errs) > 0 {
finalErr = fmt.Errorf("partial failure: %v", errs)
}
return allData, finalErr
}
func (b *BaseStats) fetchPage(end time.Time, period int, client *client.Client) ([]interface{}, error) {
@@ -55,24 +64,26 @@ func (b *BaseStats) fetchPage(end time.Time, period int, client *client.Client)
path = strings.Replace(path, "{period}", fmt.Sprintf("%d", period), 1)
}
response, err := client.ConnectAPI(path, "GET", nil)
data, err := client.ConnectAPI(path, "GET", nil, nil)
if err != nil {
return nil, err
}
if response == nil {
if len(data) == 0 {
return []interface{}{}, nil
}
responseSlice, ok := response.([]interface{})
if !ok || len(responseSlice) == 0 {
var responseSlice []map[string]interface{}
if err := json.Unmarshal(data, &responseSlice); err != nil {
return nil, err
}
if len(responseSlice) == 0 {
return []interface{}{}, nil
}
var results []interface{}
for _, item := range responseSlice {
itemMap := item.(map[string]interface{})
for _, itemMap := range responseSlice {
// Handle nested "values" structure
if values, exists := itemMap["values"]; exists {
valuesMap := values.(map[string]interface{})

40
garth/stats/hrv_weekly.go Normal file
View File

@@ -0,0 +1,40 @@
package stats
import (
"errors"
"time"
)
const WEEKLY_HRV_PATH = "/wellness-service/wellness/weeklyHrv"
type WeeklyHRV struct {
CalendarDate time.Time `json:"calendar_date"`
AverageHRV float64 `json:"average_hrv"`
MaxHRV float64 `json:"max_hrv"`
MinHRV float64 `json:"min_hrv"`
HRVQualifier string `json:"hrv_qualifier"`
WellnessDataDaysCount int `json:"wellness_data_days_count"`
BaseStats
}
func NewWeeklyHRV() *WeeklyHRV {
return &WeeklyHRV{
BaseStats: BaseStats{
Path: WEEKLY_HRV_PATH + "/{end}/{period}",
PageSize: 52,
},
}
}
func (w *WeeklyHRV) Validate() error {
if w.CalendarDate.IsZero() {
return errors.New("calendar_date is required")
}
if w.AverageHRV < 0 || w.MaxHRV < 0 || w.MinHRV < 0 {
return errors.New("HRV values must be non-negative")
}
if w.MaxHRV < w.MinHRV {
return errors.New("max_hrv must be greater than min_hrv")
}
return nil
}

View File

@@ -0,0 +1,36 @@
package stats
import (
"errors"
"time"
)
const WEEKLY_STRESS_PATH = "/wellness-service/wellness/weeklyStress"
type WeeklyStress struct {
CalendarDate time.Time `json:"calendar_date"`
TotalStressDuration int `json:"total_stress_duration"`
AverageStressLevel float64 `json:"average_stress_level"`
MaxStressLevel int `json:"max_stress_level"`
StressQualifier string `json:"stress_qualifier"`
BaseStats
}
func NewWeeklyStress() *WeeklyStress {
return &WeeklyStress{
BaseStats: BaseStats{
Path: WEEKLY_STRESS_PATH + "/{end}/{period}",
PageSize: 52,
},
}
}
func (w *WeeklyStress) Validate() error {
if w.CalendarDate.IsZero() {
return errors.New("calendar_date is required")
}
if w.TotalStressDuration < 0 {
return errors.New("total_stress_duration must be non-negative")
}
return nil
}