Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions scripts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.venv

config.py

__pycache__/
15 changes: 15 additions & 0 deletions scripts/config.py.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
TOKEN="" # token for access to the REST sources auth backend
REST_SOURCES_AUTH_BACKEND_URL="https://stage.radar-base.net/rest-sources/backend" # base url of the REST sources auth backend. no trailing slash

# Specify either PULL_PERIOD_IN_MONTHS or START_DATE + END_DATE
PULL_PERIOD_IN_WEEKS=4 # period of time in weeks to pull data retrospectively from today
START_DATE="" # in format "%Y-%m-%dT%H%M%SZ" ISO 8601 format
END_DATE="" # in format "%Y-%m-%dT%H%M%SZ" ISO 8601 format

SOURCE_TYPE="Garmin" # source type to update the data for (e.g. Garmin, FitBit, or Oura)
PROJECT_ID="STAGING_PROJECT" # project id to update the data for

INCLUDE_SUBJECTS=[] # list of subject ids to include in the retrospective pull
EXCLUDE_SUBJECTS=[] # list of subject ids to exclude from the retrospective pull

ENABLE_DEBUG=False # enable debug mode
7 changes: 7 additions & 0 deletions scripts/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
certifi==2024.8.30
charset-normalizer==3.4.0
idna==3.10
python-dateutil==2.9.0.post0
requests==2.32.3
six==1.17.0
urllib3==2.2.3
95 changes: 95 additions & 0 deletions scripts/retrospective_pull.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import requests
import logging
from config import REST_SOURCES_AUTH_BACKEND_URL, PROJECT_ID, SOURCE_TYPE, TOKEN, START_DATE, END_DATE, PULL_PERIOD_IN_WEEKS, INCLUDE_SUBJECTS, EXCLUDE_SUBJECTS, ENABLE_DEBUG
from datetime import datetime
import datetime as dt
from dateutil import parser

logger = logging.getLogger(__name__)
logging.basicConfig(format='%(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S', level=logging.INFO)

if ENABLE_DEBUG:
logger.setLevel(logging.DEBUG)

if __name__ == "__main__":

# Ask user input for dry run option
dry_run = input("Dry run? (y/n): ")
if dry_run.lower() == "y":
logger.info("Dry run enabled")
elif dry_run.lower() == "n":
logger.info("Running retrospective pull reset script")
else:
logger.error("Invalid input. Please enter 'y' or 'n'")
exit()

now = datetime.now(dt.timezone.utc)

# make request to get all the subjects from the REST_SOURCES_AUTH_BACKEND_URL
response = requests.get(REST_SOURCES_AUTH_BACKEND_URL + f"/users?project-id={PROJECT_ID}&source-type={SOURCE_TYPE}&authorized=true", headers={"Authorization": "Bearer " + TOKEN})

if response.status_code != 200:
logger.error(f"{response.json().get('error', 'Error')}: {response.json().get('error_description', 'Error fetching subjects')}")
exit()

subjects = response.json()['users']

logger.debug(f"Type: {type(subjects)} ,Subjects: {subjects}")

if len(subjects) == 0:
logger.error("No subjects found")
exit()

confirm = input(f"Are you sure you want to reset {len(subjects)} subjects? (y/n): ")
if confirm.lower() != "y":
logger.info("Exiting")
exit()

# for each subject, make a request to the REST_SOURCES_AUTH_BACKEND_URL to update the user's start and end date and reset the user
for subject in subjects:

# check if the subject is in the include list
if len(INCLUDE_SUBJECTS) > 0 and subject["id"] not in INCLUDE_SUBJECTS:
continue

# check if the subject is in the exclude list
if len(EXCLUDE_SUBJECTS) > 0 and subject["id"] in EXCLUDE_SUBJECTS:
continue

if START_DATE and END_DATE:
start_date = datetime.strptime(START_DATE, "%Y-%m-%dT%H%M%SZ")
end_date = datetime.strptime(END_DATE, "%Y-%m-%dT%H%M%SZ")
elif PULL_PERIOD_IN_WEEKS:
start_date = now - dt.timedelta(weeks=PULL_PERIOD_IN_WEEKS)
end_date = now

else:
raise Exception("Either PULL_PERIOD_IN_WEEKS or START_DATE + END_DATE must be specified")

subject["startDate"] = start_date.timestamp()
subject["endDate"] = end_date.timestamp()

if dry_run.lower() == "n":
response = requests.post(REST_SOURCES_AUTH_BACKEND_URL + f"/users/{subject['id']}/reset", headers={"Authorization": "Bearer " + TOKEN}, json=subject)

logger.debug(f"Response: {response.json()}")

# Write a test case to check and confirm the user has been updated as expected
if response.status_code == 200:
start_date_res = parser.parse(response.json()['startDate'])
end_date_res = parser.parse(response.json()['endDate'])
logger.debug(f"start date: {start_date.timestamp()}, end date: {end_date.timestamp()}")
logger.debug(f"start date res: {start_date_res.timestamp()}, end date res: {end_date_res.timestamp()}")

assert start_date_res.timestamp() == start_date.timestamp()
assert end_date_res.timestamp() == end_date.timestamp()

logger.info(f"User {subject['userId']} reset successfully")
else:
if response.json().get('error') == "user_not_found":
logger.error(f"Error: {response.json().get('error_description', f"User {subject['userId']} not found in MP. Please check the user id")}")
elif dry_run.lower() == "y":
logger.info(f"DRY RUN: {subject}")

logger.info("All reset updated successfully")
exit()
Loading