Package gfit: Steps: Calculate diff to previously stored data point.
[kraftakt.git] / gfit / gfit.go
index ff4c26d..8fe0d54 100644 (file)
@@ -19,6 +19,8 @@ import (
 const (
        csrfToken = "@CSRFTOKEN@"
        userID    = "me"
+
+       dataTypeNameSteps = "com.google.step_count.delta"
 )
 
 var oauthConfig = &oauth2.Config{
@@ -141,12 +143,49 @@ func (c *Client) DataSetPatch(ctx context.Context, dataSourceID string, points [
        return nil
 }
 
-func (c *Client) SetSteps(ctx context.Context, steps int, date time.Time) error {
-       const dataTypeName = "com.google.step_count.delta"
+func (c *Client) Steps(ctx context.Context, startTime, endTime time.Time) (int, time.Time, error) {
+       dataSourceID := DataStreamID(&fitness.DataSource{
+               Type: "raw",
+               DataType: &fitness.DataType{
+                       Name: dataTypeNameSteps,
+               },
+       })
+       datasetID := fmt.Sprintf("%d-%d", startTime.UnixNano(), endTime.UnixNano())
+
+       res, err := c.Service.Users.DataSources.Datasets.Get(userID, dataSourceID, datasetID).Context(ctx).Do()
+       if err != nil {
+               log.Errorf(ctx, "c.Service.Users.DataSources.Datasets.Get(%q, %q) = %v",
+                       dataSourceID, datasetID, err)
+               return 0, time.Time{}, err
+       }
+
+       if len(res.Point) == 0 {
+               return 0, startTime, nil
+       }
+
+       steps := 0
+       maxEndTime := startTime
+       for _, p := range res.Point {
+               pointEndTime := time.Unix(0, p.EndTimeNanos).In(startTime.Location())
+               value := p.Value[0].IntVal
+
+               steps += int(value)
+               if maxEndTime.Before(pointEndTime) {
+                       maxEndTime = pointEndTime
+               }
+       }
+
+       log.Debugf(ctx, "Google Fit has data points until %v: %d steps", maxEndTime, steps)
+       return steps, maxEndTime, nil
+}
+
+func (c *Client) SetSteps(ctx context.Context, totalSteps int, startOfDay time.Time) error {
+       if totalSteps == 0 {
+               return nil
+       }
 
        dataSourceID, err := c.DataSourceCreate(ctx, &fitness.DataSource{
                Application:    Application(ctx),
-               DataStreamId:   "", // COMPUTED
                DataStreamName: "", // "daily summary"?
                DataType: &fitness.DataType{
                        Field: []*fitness.DataTypeField{
@@ -155,24 +194,41 @@ func (c *Client) SetSteps(ctx context.Context, steps int, date time.Time) error
                                        Name:   "steps",
                                },
                        },
-                       Name: dataTypeName,
+                       Name: dataTypeNameSteps,
                },
                Name: "Step Count",
                Type: "raw",
+               // Type: "derived",
        })
        if err != nil {
                return err
        }
 
+       endOfDay := startOfDay.Add(24 * time.Hour).Add(-1 * time.Nanosecond)
+       prevSteps, startTime, err := c.Steps(ctx, startOfDay, endOfDay)
+       if totalSteps == prevSteps {
+               return nil
+       }
+       diffSteps := totalSteps - prevSteps
+       if diffSteps < 0 {
+               log.Warningf(ctx, "c.Steps returned %d steps, but current count is %d", prevSteps, totalSteps)
+               diffSteps = totalSteps
+       }
+       endTime := endOfDay
+       if now := time.Now().In(startOfDay.Location()); now.Before(endOfDay) {
+               endTime = now
+       }
+       log.Debugf(ctx, "new data point: %v-%v %d steps", startTime, endTime, diffSteps)
+
        return c.DataSetPatch(ctx, dataSourceID, []*fitness.DataPoint{
                &fitness.DataPoint{
                        ComputationTimeMillis: time.Now().UnixNano() / 1000000,
-                       DataTypeName:          dataTypeName,
-                       StartTimeNanos:        date.UnixNano(),
-                       EndTimeNanos:          date.Add(24 * time.Hour).Add(-1 * time.Nanosecond).UnixNano(),
+                       DataTypeName:          dataTypeNameSteps,
+                       StartTimeNanos:        startTime.UnixNano(),
+                       EndTimeNanos:          endTime.UnixNano(),
                        Value: []*fitness.Value{
                                &fitness.Value{
-                                       IntVal: int64(steps),
+                                       IntVal: int64(diffSteps),
                                },
                        },
                },