Skip to content

Instantly share code, notes, and snippets.

@patmigliaccio
Created May 10, 2023 02:49
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save patmigliaccio/b60e4007fff756b00feb67c1f87ad76a to your computer and use it in GitHub Desktop.
Save patmigliaccio/b60e4007fff756b00feb67c1f87ad76a to your computer and use it in GitHub Desktop.
A simple script for exporting all FatSecret weight history to CSV.
"""
# Export FatSecret Weight History CSV
A simple script for exporting all FatSecret weight history to CSV.
## Installation
*Install Dependencies*
```sh
pip install fatsecret decouple rauth
```
*Create .env file with OAuth1.0 credentials*
```
CONSUMER_KEY=
CONSUMER_SECRET=
```
## Run
```sh
python export.py
```
### Config
Update the `years_of_data` variable to the maximum number of years
worth of data to retrieve.
"""
import csv
from datetime import date, datetime, timedelta
from decouple import config
from fatsecret import Fatsecret
import os
import pickle
import time
consumer_key = config("CONSUMER_KEY")
consumer_secret = config("CONSUMER_SECRET")
years_of_data = 10
token_file = "token.pickle"
output_file = "output.csv"
def auth() -> Fatsecret:
"""Logins in the user and returns a Fatsecret instance."""
session_token = None
if os.path.exists(token_file):
with open(token_file, "rb") as file:
session_token = pickle.load(file)
if session_token is None:
fs = Fatsecret(consumer_key, consumer_secret)
auth_url = fs.get_authorize_url()
print(
f"Browse to the following URL in your browser to authorize access:\n{1}",
auth_url,
)
pin = input("Enter the PIN provided by FatSecret: ")
session_token = fs.authenticate(pin)
with open(token_file, "wb") as file:
pickle.dump(session_token, file)
else:
fs = Fatsecret(consumer_key, consumer_secret, session_token=session_token)
return fs
def convert_date(date_int: int):
"""Transforms a date_int value to YYYY-MM-DD."""
reference_date = datetime(1970, 1, 1)
date = reference_date + timedelta(days=float(date_int))
return date.strftime("%Y-%m-%d")
def convert_weight(weight_kg: float):
"""Converts kgs to lbs (rounded to nearest decimal)."""
return round(weight_kg * 2.20462, 2)
def transform_weights(response: dict[str, str]):
"""Tranforms the values to output object."""
date_str = convert_date(int(response["date_int"]))
weight_lbs = convert_weight(float(response["weight_kg"]))
return {
"date": date_str,
"weight": weight_lbs,
"comment": response.get("weight_comment"),
}
def get_weights_by_month(fs: Fatsecret, date: datetime):
"""Retrieves all of the weights recorded during a particular month."""
all_weights = []
weights = fs.weights_get_month(date)
if weights is not None:
if not isinstance(weights, list):
weights = [weights]
for weight in weights:
if weight:
result = transform_weights(weight)
all_weights.append(result)
return all_weights
def dump_csv(values):
"""Creates a .csv file with the object data."""
fieldnames = ["date", "weight", "comment"]
with open(output_file, "w", newline="") as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for value in values:
writer.writerow(value)
def main():
"""
Authenticates and iterates over each month for a set number
of years. Uses a retry loop to handle rate limiting.
"""
fs = auth()
start_date = date.today() - timedelta(days=365 * years_of_data)
end_date = date.today()
all_weights = []
retries = 10
try:
current_date = start_date
while current_date <= end_date:
try:
first_day_of_month = datetime(current_date.year, current_date.month, 1)
weights = get_weights_by_month(fs, first_day_of_month)
time.sleep(1)
if len(weights):
all_weights.extend(weights)
# Reset number of revivals
retries = 10
# Move to the next month
current_date = current_date.replace(day=1)
current_date += timedelta(days=32)
current_date = current_date.replace(day=1)
except Exception as e:
print(e)
if retries == 0:
raise Exception("Failed too many times...")
time.sleep(5)
retries -= 1
print("Retrying...")
except:
print("Failed. Saving dump...")
dump_csv(all_weights)
print("Dumped.")
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment