From fc31210924dc60e5a885a669e2431603f7344bbf Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Sat, 13 Jan 2018 22:35:12 +0100 Subject: [PATCH] Package gfit: Steps: Calculate diff to previously stored data point. Google Fit will happily store multiple data points with the exact same dataSourceID and start and end times. The web frontend then seems to discard duplicate entries. This commit reads existing data points and then calculates the difference to the existing entries. --- gfit/gfit.go | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 8 deletions(-) diff --git a/gfit/gfit.go b/gfit/gfit.go index ff4c26d..8fe0d54 100644 --- a/gfit/gfit.go +++ b/gfit/gfit.go @@ -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), }, }, }, -- 2.11.0