From: Florian Forster Date: Sat, 13 Jan 2018 21:35:12 +0000 (+0100) Subject: Package gfit: Steps: Calculate diff to previously stored data point. X-Git-Url: https://git.octo.it/?p=kraftakt.git;a=commitdiff_plain;h=fc31210924dc60e5a885a669e2431603f7344bbf 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. --- 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), }, }, },