Created
May 10, 2023 02:49
-
-
Save patmigliaccio/b60e4007fff756b00feb67c1f87ad76a to your computer and use it in GitHub Desktop.
A simple script for exporting all FatSecret weight history to CSV.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
# 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