logbook/measurement.go

156 lines
3.7 KiB
Go

package logbook
import (
"encoding/json"
"errors"
"fmt"
"time"
bolt "go.etcd.io/bbolt"
"git.wow.st/gmp/logbook/parser"
)
// --- Measurement Data Structure ---
type Measurement struct {
ID string // Unique identifier
Date time.Time // When the measurement was taken
Variable string // e.g. "Bodyweight", "Waist", "Biceps"
Value float64 // The actual measurement value
Note string // Optional notes
}
// --- BoltDB Bucket Name ---
var bucketMeasurements = []byte("measurements")
// --- MeasurementRepository ---
type MeasurementRepository struct {
db *bolt.DB
}
func NewMeasurementRepository(dbpath string) (*MeasurementRepository, error) {
db, err := bolt.Open(dbpath, 0600, nil)
if err != nil {
return nil, err
}
err = db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists(bucketMeasurements)
return err
})
if err != nil {
return nil, err
}
return &MeasurementRepository{db: db}, nil
}
func (mr *MeasurementRepository) Close() {
mr.db.Close()
}
func (r *MeasurementRepository) Save(m *Measurement) error {
if m.ID == "" {
m.ID = generateDateID(m.Date, "measurement-" + "m.Variable")
}
return r.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket(bucketMeasurements)
data, err := json.Marshal(m)
if err != nil {
return err
}
return b.Put([]byte(m.ID), data)
})
}
func (r *MeasurementRepository) GetByID(id string) (*Measurement, error) {
var m Measurement
err := r.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(bucketMeasurements)
v := b.Get([]byte(id))
if v == nil {
return errors.New("measurement not found")
}
return json.Unmarshal(v, &m)
})
if err != nil {
return nil, err
}
return &m, nil
}
func (r *MeasurementRepository) FindAll() ([]*Measurement, error) {
var measurements []*Measurement
err := r.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(bucketMeasurements)
return b.ForEach(func(k, v []byte) error {
var m Measurement
if err := json.Unmarshal(v, &m); err != nil {
return err
}
measurements = append(measurements, &m)
return nil
})
})
return measurements, err
}
func (r *MeasurementRepository) Delete(id string) error {
return r.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket(bucketMeasurements)
return b.Delete([]byte(id))
})
}
// --- AddMeasurementsFromIR (with bucket creation) ---
func (r *MeasurementRepository) AddMeasurementsFromIR(irMeasurements []parser.Measurement) error {
db := r.db
if db == nil {
return fmt.Errorf("Measurement Repository is not initialized.")
}
for _, m := range(irMeasurements) {
measurementID := generateDateID(m.Date, m.Variable)
mm := &Measurement{
ID: measurementID,
Date: m.Date,
Variable: m.Variable,
Value: m.Value,
Note: m.Note,
}
err := r.Save(mm)
if err != nil {
return err
}
}
return nil
}
func (r *MeasurementRepository) GetOrPredict(variable string, date time.Time) (float64, error) {
var ret float64
id := date.UTC().Format("2006-01-02")
err := r.db.View(func(tx *bolt.Tx) error {
c := tx.Bucket(bucketMeasurements).Cursor()
k,v := c.Seek([]byte(id))
if k != nil {
var m Measurement
if err := json.Unmarshal(v, &m); err != nil {
return err
}
ret = m.Value
} else {
x, err := r.Predict(variable, date, 0.75)
if err != nil {
return err
}
ret = x
}
return nil
})
if err != nil {
return 0, err
}
return ret, nil
}