motherapp / weight-csv-to-gfit

Load weights.csv and import to a Google Fit account
25 stars 20 forks source link

"Unable to fetch DataSource for Dataset:" #1

Closed Chreeq closed 6 years ago

Chreeq commented 6 years ago

Hello,

I've been trying to make this code work again, but my very limited knowledge only got me so far. By very limited I mean that this is the first time I am interacting with python.

However I managed to fix the oAuth mechanism and some minor issues I had with the code. But I eventually end up in an error, when trying to patch my data into my gfit account.

googleapiclient.errors.HttpError: <HttpError 400 when requesting https://www.googleapis.com/fitness/v1/users/me/dataSources/raw%3Acom.google.weight%3A35873072968%3Aunknown%3Aunknown%3Amaweightimport/datasets/1524024000000000000-1504843200000000000?alt=json&key=AIzaSyA5KxFyd7TlntBDbuags-F4miQo402OG5c returned "Unable to fetch DataSource for Dataset: raw:com.google.weight:35873072968:unknown:unknown:maweightimport">

This is where the error occurs:

    fitness_service.users().dataSources().datasets().patch(
      userId='me',
      dataSourceId=data_source_id,
      datasetId=dataset_id,
      body=dict(
        minStartTimeNs=min_log_ns,
        maxEndTimeNs=max_log_ns,
        dataSourceId=data_source_id,
        point=weights,
      )).execute()

My current version of the code looks like this (please excuse the excessive amount of prints and commented code. I am a horrible coder):

# -------------------------------------------------------------------------------
# Purpose: Load weights.csv and import to a Google Fit account
# Some codes refer to:
# 1. https://github.com/tantalor/fitsync
# 2. http://www.ewhitling.com/2015/04/28/scrapping-data-from-google-fitness/
import json
import httplib2
import sys
from apiclient.discovery import build

from oauth2client.file import Storage
from oauth2client.client import OAuth2WebServerFlow
from oauth2client.tools import run_flow, argparser
from oauth2client.client import AccessTokenRefreshError
from read_weight_csv import read_weights_csv_with_gfit_format
from googleapiclient.errors import HttpError

client_id = '35873072968-gtq30smu2bdnais7qsfs0ccsrb7a8oro.apps.googleusercontent.com'
client_secret = 'K4OuT-VlEtBP4Wd9Y68r9zdF'
scope = 'https://www.googleapis.com/auth/fitness.body.write'

# Setup for Google API:
# Steps: 
# 1. Go https://console.developers.google.com/apis/credentials
# 2. Create credentials => OAuth Client ID
# 3. Set Redirect URI to your URL or the playground https://developers.google.com/oauthplayground
#client_id = '35873072968-gtq30smu2bdnais7qsfs0ccsrb7a8oro.apps.googleusercontent.com'
#CLIENT_SECRET = 'K4OuT-VlEtBP4Wd9Y68r9zdF'

# Redirect URI to google Fit, See Steps 3 above
#REDIRECT_URI='https://developers.google.com/oauthplayground'

# See scope here: https://developers.google.com/fit/rest/v1/authorization
#SCOPE = 'https://www.googleapis.com/auth/fitness.body.write'

# API Key
# Steps: 
# 1. Go https://console.developers.google.com/apis/credentials
# 2. Create credentials => API Key => Server Key
API_KEY = 'AIzaSyA5KxFyd7TlntBDbuags-F4miQo402OG5c'

def import_weight_to_gfit():
    # first step of auth
    # only approved IP is my Digital Ocean Server
    flow = OAuth2WebServerFlow(client_id, client_secret, scope)
    storage = Storage('google.json')
    flags = argparser.parse_args([])
    creds = run_flow(flow, storage)
    print "access_token: %s" % creds.access_token
    #run_flow(flow, storage, flags)
    #flow = OAuth2WebServerFlow(client_id=client_id, client_secret=CLIENT_SECRET, scope=SCOPE, redirect_uri=REDIRECT_URI)
    #auth_uri = flow.step1_get_authorize_url()
    #print "Copy this url to web browser for authorization: "
    #print auth_uri

    # hmm, had to manually pull this as part of a Google Security measure. 
    # there must be a way to programatically get this, but this exercise doesn't need it ... yet...
    #token = raw_input("Copy the token from URL and input here: ")
    #cred = flow.step2_exchange(creds.access_token)
    http = httplib2.Http()
    http = creds.authorize(http)
    fitness_service = build('fitness','v1', http=http, developerKey=API_KEY)

    # init the fitness objects
    fitusr = fitness_service.users()
    print "fitnessuser: %s" % fitusr
    fitdatasrc = fitusr.dataSources()
    print "Building the fitness thingies"
    data_source = dict(
        type='raw',
        application=dict(name='maweightimport'),
        dataType=dict(
          name='com.google.weight',
          field=[dict(format='floatPoint', name='weight')]
        ),
        device=dict(
          type='scale',
          manufacturer='unknown',
          model='unknown',
          uid='maweightimport',
          version='1.0',
        )
      )
    print "Getting data sources id"
    def get_data_source_id(dataSource):
      project_number = client_id.split('-')[0]
      return ':'.join((
        dataSource['type'],
        dataSource['dataType']['name'],
        project_number,
        dataSource['device']['manufacturer'],
        dataSource['device']['model'],
        dataSource['device']['uid']))

    print "found data source id: %s" % get_data_source_id(data_source)
    data_source_id = get_data_source_id(data_source)
    # Ensure datasource exists for the device.

    print "reading in weights"
    weights = read_weights_csv_with_gfit_format()
    min_log_ns = weights[0]["startTimeNanos"]
    max_log_ns = weights[-1]["startTimeNanos"]
    dataset_id = '%s-%s' % (min_log_ns, max_log_ns)
    print "found data source id: %s" % dataset_id
    # patch data to google fit
    print "test1"
    fitness_service.users().dataSources().datasets().patch(
      userId='me',
      dataSourceId=data_source_id,
      datasetId=dataset_id,
      body=dict(
        minStartTimeNs=min_log_ns,
        maxEndTimeNs=max_log_ns,
        dataSourceId=data_source_id,
        point=weights,
      )).execute()
    print "test"
    # read data to verify
    print fitness_service.users().dataSources().datasets().get(
        userId='me',
        dataSourceId=data_source_id,
        datasetId=dataset_id).execute()

if __name__=="__main__":
    import_weight_to_gfit()
chwong1 commented 6 years ago

In original source code, there is a try except block that make sure the data source is created. line 84-94 in https://github.com/motherapp/weight-csv-to-gfit/blob/master/import_weight_to_gfit.py

I think that your account is missing the data source record in order to put the data in.

chwong1 commented 6 years ago

Thanks @Chreeq finding the root cause. The change is merged.

2

erikw commented 5 years ago

I still get the above error, even after the merged PR. I was able to get it to work by using the dataStreamId returned by the call fitness_service.users().dataSources().create().

This ID is different from the one produced by the local function get_data_source_id()! The difference seems to be that the one returned from the create call uses number representation of the project, whereas the locally produced one uses the clear-text name of the project.