coddingtonbear / python-myfitnesspal

Access your meal tracking data stored in MyFitnessPal programatically
MIT License
795 stars 138 forks source link

KeyError: 'dehydrated state' #183

Open natesanshreyas opened 3 weeks ago

natesanshreyas commented 3 weeks ago

Has anyone come across this before?

weight_data = client.get_measurements('Weight', single_date, single_date)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

File "/Users/shreyasnatesan/Desktop/NucampFolder/Python/1-Fundamentals/myenv/lib/python3.12/site-packages/myfitnesspal/client.py", line 615, in get_measurements measurement_ids = self._get_measurement_ids(document) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/shreyasnatesan/Desktop/NucampFolder/Python/1-Fundamentals/myenv/lib/python3.12/site-packages/myfitnesspal/client.py", line 741, in _get_measurement_ids for q in next_data_json["props"]["pageProps"]["dehydratedState"]["queries"]:


KeyError: 'dehydratedState'

Seems like an issue with client.py, any way to debug or fix this?
hannahburkhardt commented 3 weeks ago

I can't reproduce the issue. Can you provide a more detailed example? What are the weights that you are expecting to be returned?

natesanshreyas commented 3 weeks ago

import os.path import pandas as pd from google.auth.transport.requests import Request from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from googleapiclient.discovery import build from googleapiclient.errors import HttpError import myfitnesspal import browser_cookie3 import datetime

Initialize MyFitnessPal client with Chrome cookies

client = myfitnesspal.Client(browser_cookie3.chrome())

Define the start date as today and go back 8 days

end_date = datetime.date.today() start_date = end_date - datetime.timedelta(days=7) # This will give you a 8-day range

data = []

Loop over the range of dates and collect totals for each day

for single_date in (start_date + datetime.timedelta(n) for n in range(8)): try:

Attempt to fetch the data

    day = client.get_date(single_date.year, single_date.month, single_date.day)
    weight_data = client.get_measurements('Weight', single_date, single_date)
    weight = weight_data.get(single_date, None)  # Leave blank if no data

    totals = day.totals

    # Append the data to the list
    data.append({
        'Date': single_date.strftime('%Y-%m-%d'),
        'Weight': weight if weight else 0,
        'Fat (g)': totals.get('fat', 0),
        'Carbohydrates (g)': totals.get('carbohydrates', 0),
        'Protein (g)': totals.get('protein', 0),
        'Calories': totals.get('calories', 0)
    })
except KeyError as e:
    print(f"KeyError occurred for date {single_date}: {e}. Skipping this date.")
except Exception as e:
    print(f"An unexpected error occurred for date {single_date}: {e}. Skipping this date.")

Convert the data list to a DataFrame

df = pd.DataFrame(data)

Google Sheets API integration

SCOPES = ["https://www.googleapis.com/auth/spreadsheets"]

The ID of the target spreadsheet.

SPREADSHEET_ID = ""

def get_start_position(service, spreadsheet_id, target_date):

Retrieve the sheet data

sheet = service.spreadsheets()
result = sheet.values().get(spreadsheetId=spreadsheet_id, range="Current Overall Protocol 2024!A1:Z").execute()
values = result.get('values', [])

# Find the date in the sheet
for row_idx, row in enumerate(values):
    for col_idx, cell_value in enumerate(row):
        if cell_value == target_date:  # Adjust the date format to match your sheet
            start_column = chr(ord('A') + col_idx)
            base_row = row_idx + 1
            return start_column, base_row

# Default fallback if date is not found
return None, None

def main(): creds = None if os.path.exists("token.json"): creds = Credentials.from_authorized_user_file("token.json", SCOPES)

if not creds or not creds.valid:
    if creds and creds.expired and creds.refresh_token:
        creds.refresh(Request())
    else:
        flow = InstalledAppFlow.from_client_secrets_file(
            "credentials.json", SCOPES
        )
        creds = flow.run_local_server(port=0)
    with open("token.json", "w") as token:
        token.write(creds.to_json())

try:
    # Build the service after creds are defined
    service = build("sheets", "v4", credentials=creds)

    for idx, row in df.iterrows():
        date_str = row['Date']
        start_column, base_row = get_start_position(service, SPREADSHEET_ID, date_str)

        if start_column and base_row:
            end_column = chr(ord(start_column) + len(row) - 1)
            RANGE_NAME = f"Current Overall Protocol 2024!{start_column}{base_row}:{end_column}{base_row}"

            # Convert DataFrame row to list for Google Sheets API
            values = [[row['Date'], row['Weight'], row['Fat (g)'], row['Carbohydrates (g)'], row['Protein (g)'], row['Calories']]]

            # Prepare the body for the API request
            body = {
                "values": values
            }

            # Call the Sheets API to update the target range
            result = service.spreadsheets().values().update(
                spreadsheetId=SPREADSHEET_ID, range=RANGE_NAME,
                valueInputOption="RAW", body=body
            ).execute()

            print(f"Updated {result.get('updatedCells')} cells for date {date_str}.")

        else:
            print(f"Date {date_str} not found in the sheet. Skipping update.")

except HttpError as error:
    print(f"An error occurred: {error}")

if name == 'main': main()

This is the script I'm using, I'm essentially trying to get the date, weight, fat, carbs, protein, and calories for the past seven days off of MFP. I'm not sure what the "dehydrated state" issue even is. If I get rid of the try except block I get this error:

Traceback (most recent call last): File "/Users/shreyasnatesan/Desktop/NucampFolder/Python/1-Fundamentals/quickstart.py", line 25, in weight_data = client.get_measurements('Weight', single_date, single_date) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/shreyasnatesan/Documents/python/venv/lib/python3.12/site-packages/myfitnesspal/client.py", line 615, in get_measurements measurement_ids = self._get_measurement_ids(document) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/shreyasnatesan/Documents/python/venv/lib/python3.12/site-packages/myfitnesspal/client.py", line 741, in _get_measurement_ids for q in next_data_json["props"]["pageProps"]["dehydratedState"]["queries"]:


KeyError: 'dehydratedState'

Seems like there's something in the client.py around "dehydratedState"? No idea what that means or how to fix that.