Merge upstream (removes a newline I added)

This commit is contained in:
Neale Pickett
2015-11-04 13:07:09 -07:00
2 changed files with 60 additions and 28 deletions
+7 -2
View File
@@ -40,9 +40,9 @@ This script opens a browser window where you can log in to your Google account a
## Usage ## Usage
Run `fitsync.py` to download data from Fitbit and upload to Google, Use the `patch` command to download data from Fitbit and upload to Google,
$ python fitsync.py $ python fitsync.py patch
Use the `delete` command to remove data from Google, Use the `delete` command to remove data from Google,
@@ -52,6 +52,11 @@ Use the `get` command to see the data stored in Google,
$ python fitsync.py get $ python fitsync.py get
There are a few options supported,
$ python fitsync.py --help
## See your data ## See your data
To view weight data on [Google Fit](https://fit.google.com), To view weight data on [Google Fit](https://fit.google.com),
+53 -26
View File
@@ -1,8 +1,14 @@
#!/usr/bin/env python #!/usr/bin/env python
import httplib2 import httplib2
import sys import sys
import time import time
import yaml import yaml
import argparse
import logging
import datetime
import dateutil.tz
import dateutil.parser
import fitbit import fitbit
from apiclient.discovery import build from apiclient.discovery import build
@@ -15,27 +21,38 @@ POUNDS_PER_KILOGRAM = 2.20462
TIME_FORMAT = "%a, %d %b %Y %H:%M:%S" TIME_FORMAT = "%a, %d %b %Y %H:%M:%S"
def GetFitbitClient(filename):
def GetFitbitClient(): logging.debug("Creating Fitbit client")
credentials = yaml.load(open('fitbit.yaml')) credentials = yaml.load(open(filename))
client = fitbit.Fitbit(**credentials) client = fitbit.Fitbit(**credentials)
logging.debug("Fitbit client created")
return client return client
def GetGoogleClient(): def GetGoogleClient(filename):
credentials = Storage('google.json').get() logging.debug("Creating Google client")
credentials = Storage(filename).get()
http = credentials.authorize(httplib2.Http()) http = credentials.authorize(httplib2.Http())
client = build('fitness', 'v1', http=http) client = build('fitness', 'v1', http=http)
logging.debug("Google client created")
return client return client
dawnOfTime = datetime.datetime(1970, 1, 1, tzinfo=dateutil.tz.tzutc())
def epochOfFitbitLog(logEntry, tzinfo):
logTimestamp = "{} {}".format(logEntry["date"], logEntry["time"])
logTime = dateutil.parser.parse(logTimestamp).replace(tzinfo=tzinfo)
return (logTime - dawnOfTime).total_seconds()
def nano(val): def nano(val):
"""Converts a number to nano (str).""" """Converts a number to nano (str)."""
return '%d' % (val * 1e9) return '%d' % (val * 1e9)
def FitbitWeightToGoogleWeight(fitbitWeightLog): def FitbitWeightToGoogleWeight(fitbitWeightLog, tzinfo):
logSecs = fitbitWeightLog['logId'] / 1000 logSecs = epochOfFitbitLog(fitbitWeightLog, tzinfo)
logWeightLbs = fitbitWeightLog['weight'] logWeightLbs = fitbitWeightLog['weight']
logWeightKg = logWeightLbs / POUNDS_PER_KILOGRAM logWeightKg = logWeightLbs / POUNDS_PER_KILOGRAM
return dict( return dict(
@@ -58,22 +75,36 @@ def GetDataSourceId(dataSource):
def main(): def main():
fitbitClient = GetFitbitClient() parser = argparse.ArgumentParser("Transfer Fitbit weight data to Google Fit")
parser.add_argument("command", choices=('patch', 'get', 'delete'), help="What to do")
parser.add_argument("-d", "--debug", action="count", default=0, help="Increase debugging level")
parser.add_argument("-g", "--google-creds", default="google.json", help="Google credentials file")
parser.add_argument("-f", "--fitbit-creds", default="fitbit.yaml", help="Fitbit credentials file")
args = parser.parse_args()
debugLevel = logging.WARNING - (args.debug * 10)
logging.basicConfig(level=max(debugLevel, 0))
logging.root.name = "fitsync"
fitbitClient = GetFitbitClient(args.fitbit_creds)
userProfile = fitbitClient.user_profile_get()
tzinfo = dateutil.tz.gettz(userProfile['user']['timezone'])
devices = fitbitClient.get_devices() devices = fitbitClient.get_devices()
(scale,) = (device for device in devices if device['type'] == 'SCALE') (scale,) = (device for device in devices if device['type'] == 'SCALE')
fitbitBodyweight = fitbitClient.get_bodyweight(period='30d') fitbitBodyweight = fitbitClient.get_bodyweight(period='1m')
fitbitWeightLogs = fitbitBodyweight['weight'] fitbitWeightLogs = fitbitBodyweight['weight']
fitbitWeightLogTimes = [log['logId'] / 1000 for log in fitbitWeightLogs] fitbitWeightLogTimes = [epochOfFitbitLog(log, tzinfo) for log in fitbitWeightLogs]
minLogNs = nano(min(fitbitWeightLogTimes)) minLogNs = nano(min(fitbitWeightLogTimes))
maxLogNs = nano(max(fitbitWeightLogTimes)) maxLogNs = nano(max(fitbitWeightLogTimes))
googleWeightLogs = [FitbitWeightToGoogleWeight(log) googleWeightLogs = [FitbitWeightToGoogleWeight(log, tzinfo)
for log in fitbitWeightLogs] for log in fitbitWeightLogs]
googleClient = GetGoogleClient() googleClient = GetGoogleClient(args.google_creds)
dataSource = dict( dataSource = dict(
type='raw', type='raw',
@@ -114,7 +145,8 @@ def main():
dataSourceId=dataSourceId, dataSourceId=dataSourceId,
datasetId=datasetId).execute() datasetId=datasetId).execute()
#insert empty 'point' when there is nothing #insert empty 'point' when there is nothing
if 'point' not in ret: ret['point']=[] if 'point' not in ret:
ret['point']=[]
return ret return ret
def PointsDifference(left, right): def PointsDifference(left, right):
@@ -122,12 +154,8 @@ def main():
set(point['startTimeNanos'] for point in left['point']) - set(point['startTimeNanos'] for point in left['point']) -
set(point['startTimeNanos'] for point in right['point'])) set(point['startTimeNanos'] for point in right['point']))
command = 'patch'
if len(sys.argv) > 1:
command = sys.argv[1]
# Get weight dataset. # Get weight dataset.
if command == 'get': if args.command == 'get':
data = GetData() data = GetData()
numpoints = 0 numpoints = 0
for point in data['point']: for point in data['point']:
@@ -137,22 +165,22 @@ def main():
readableTime = time.strftime(TIME_FORMAT, time.localtime(startTimeSecs)) readableTime = time.strftime(TIME_FORMAT, time.localtime(startTimeSecs))
weightKgs = float(fpVal) weightKgs = float(fpVal)
weightLbs = float(fpVal) * POUNDS_PER_KILOGRAM weightLbs = float(fpVal) * POUNDS_PER_KILOGRAM
print "%.1f lbs ( %.2f kgs ), %s" % (weightLbs, weightKgs, readableTime) print("%.1f lbs ( %.2f kgs ), %s" % (weightLbs, weightKgs, readableTime))
numpoints += 1 numpoints += 1
print "Total %d points (in Google Fit)" % numpoints print("Total %d points (in Google Fit)" % numpoints)
# Delete weight dataset. # Delete weight dataset.
elif command == 'delete': elif args.command == 'delete':
dataPrior = GetData() dataPrior = GetData()
googleClient.users().dataSources().datasets().delete( googleClient.users().dataSources().datasets().delete(
userId='me', userId='me',
dataSourceId=dataSourceId, dataSourceId=dataSourceId,
datasetId=datasetId).execute() datasetId=datasetId).execute()
dataPost = GetData() dataPost = GetData()
print "Deleted %d points (from Google Fit)" % PointsDifference(dataPrior, dataPost) print("Deleted %d points (from Google Fit)" % PointsDifference(dataPrior, dataPost))
# Upload weight dataset. # Upload weight dataset.
elif command == 'patch': elif args.command == 'patch':
dataPrior = GetData() dataPrior = GetData()
googleClient.users().dataSources().datasets().patch( googleClient.users().dataSources().datasets().patch(
userId='me', userId='me',
@@ -165,10 +193,8 @@ def main():
point=googleWeightLogs, point=googleWeightLogs,
)).execute() )).execute()
dataPost = GetData() dataPost = GetData()
print "Added %d points (to Google Fit)" % PointsDifference(dataPost, dataPrior) print("Added %d points (to Google Fit)" % PointsDifference(dataPost, dataPrior))
else:
print "bad command"
def PointInData(startTimeNanos, data): def PointInData(startTimeNanos, data):
@@ -180,3 +206,4 @@ def PointInData(startTimeNanos, data):
if __name__ == '__main__': if __name__ == '__main__':
main() main()