Details for Withings weight log CSV.ipynb

Published by madprime

Description

Can I get my weight log data from my Withings scale? Withings provides a CSV download, but I wanted to see how to get this from the API-based data on Open Humans. Turns out it's a bit ugly! The units and nested format of the data makes it difficult to use.

You can use this notebook to parse the data and save weight logs in CSV format. I hope it's a helpful starting point for any other analyses using this data.

0

Tags & Data Sources

weight csv Nokia Health (Withings) Connection

Comments

Please log in to comment.

Notebook
Last updated 1 month ago

Withings weight log CSV

This notebook extracts weight logs from the data exported from Withings and writes CSV format weight logs. If you have a Withings scale, you can connect the data it's recording to Open Humans using this tool: https://withings.openhumans.org

The file produced by this notebook is essentially redundant with the CSV export that Withings provides, but it's a nice starting point for anyone that wants to build analyses using the API-based data that is synced to an Open Humans account. ;)

Cell → Run all

Select "Run All" from the "Cell" menu above to run everything at once. Or hit the "Run" button above to run the code one step at a time.

In [1]:
import os
import requests
import tempfile

print("Checking for Withings data in Open Humans...\n")

response = requests.get(
    "https://www.openhumans.org/api/direct-sharing/project/exchange-member/"
    "?access_token={}".format(os.environ.get('OH_ACCESS_TOKEN')))

username = response.json()['username']
for entry in response.json()['data']:
    if entry['source'] == "direct-sharing-118":
        file_url = entry['download_url']
        
if 'file_url' not in locals():
    print("Sorry, you first need to add Withings data to Open Humans!\n"
          "You can do that here: https://withings.openhumans.org")
else:
    print("Great, you have Withings data in Open Humans! We'll retrieve this...\n")

file_withings = tempfile.NamedTemporaryFile()
file_withings.write(requests.get(file_url).content)
file_withings.flush()

print("Done!")
Checking for Withings data in Open Humans...

Great, you have Withings data in Open Humans! We'll retrieve this...

Done!

Step 2: Set your units in the code below

The default is kilograms.

Options:

  1. metric (kg)
  2. imperial (lb)
  3. imperial (stone)
In [2]:
weight_format = 1

Step 2: Retrieving weight log data

Withings documentation is incomplete, so understanding this was partly reverse-engineered.

In [3]:
import arrow
import json

file_withings.seek(0)
withings_data = json.load(file_withings)

type_key = {
    1: 'weight',          # in grams (!)
    8: 'fat-mass-weight', # in decagrams (?!)
    76: 'muscle-mass',    # in decagrams
    77: 'hydration',      # in grams
    88: 'bone-mass',      # in decagrams
    6: 'fat-ratio',       # per 100,000
    5: 'fat-free-mass'    # in grams
}

# Filter for "real measures".
# Accord to Withings docs: http://developer.withings.com/oauth2/#tag/measure
# 'category' == 1 is for real measures, while 2 == user objectives.
all_data = [meas for meas in withings_data['measure']['body']['measuregrps'] if meas['category'] == 1]

logs = {
    type_key[i]: {} for i in type_key.keys()
}

for event in all_data:

    datetime = arrow.get(event['date']).isoformat()

    for measure in event['measures']:

        if measure['type'] in type_key:
            measure_type = type_key[measure['type']]
            value = measure['value']

            # convert to kg from grams
            if measure_type in ['weight', 'fat-free-mass']:
                value = measure['value'] / 1000

            # convert to kg from decagrams
            if measure_type in ['fat-mass-weight', 'muscle-mass', 'bone-mass', 'hydration']:
                value = measure['value'] / 100

            # convert to percent (per 100) from per 100,000
            if measure_type in ['fat-ratio']:
                value = value / 1000
                
            if measure_type in ['weight', 'fat-mass-weight', 'muscle-mass', 'bone-mass', 'hydration', 'fat-free-mass']:
                if weight_format == 2:
                    value = value * 2.20462
                elif weight_format == 3:
                    value = value * 2.20462 / 14
            logs[measure_type][datetime] = value

print("Data extracted. Use the next step to write these to CSV files.")
Data extracted. Use the next step to write these to CSV files.
In [4]:
import csv

filename = "{}-withings-weight-logs.csv".format(arrow.now().format('YYYYMMDD'))

measure_types = [type_key[item] for item in type_key.keys()]

with open(filename, 'w') as f:
    csv_out = csv.writer(f)
    csv_out.writerow(['datetime'] + measure_types)
    for datetime in logs['weight'].keys():
        row_data = [datetime]
        for measure in measure_types:
            try:
                row_data.append(logs[measure][datetime])
            except KeyError:
                row_data.append(None)
        csv_out.writerow(row_data)

print("https://notebooks.openhumans.org/user/{}/tree/{}".format(username, filename))

print("\nDone writing to CSV file. Use the link above to download.")
https://notebooks.openhumans.org/user/madprime/tree/20190613-withings-weight-logs.csv

Done writing to CSV file. Use the link above to download.