def _go_to(self, url, locator=None, element=None): log_message("GET", url) self.browser.get(url) if locator and element: if not self._wait_for(locator, element): raise ValueError(url + " not fully loaded")
def __init__(self, user_name, password, download_gpx, chromedriver_path, url=DEFAULT_BASE_URL): """ :param user_name: str Username (email) to login to Garmin Connect :param password: str Password to login to Garmin Connect :param download_gpx: bool Download .gpx files of activities :param chromedriver_path: str Path to Chrome driver to use as browser :param url: str Url to base downloads on """ object.__init__(self) self.browser = webdriver.Chrome( chromedriver_path) # bot browser to use self.user_name = user_name # user settings self.user_password = password self.user_logged_in = False # True iff user is correctly logged in self.user_id = None # id of user logged in self.download_gpx = download_gpx self.user_url = url + self.USER_PATH self.base_url = url garmin_region = self.user_url.split("/")[2].split("connect.")[-1] log_message("Region:", garmin_region) self.login_url = \ self.BASE_LOGIN_URL.replace("garmin.com", garmin_region)
def get_steps_details(self, date_time): try: self.go_to_steps_details(date_time) soup = self.get_html_parser() steps_details_html = soup.find('pre').text log_message("found steps details data") except: steps_details_html = '[]' log_message("NOT found steps details data") return steps_details_html
def save_gpx(self, data): """ :param data: [] of GCDayTimeline Timeline with activities :return: void Downloads .gpx file for each activity to folder """ if self.download_gpx: timelines = [timeline.activities for timeline in data] for timeline in timelines: for activity in timeline.activities: self._go_to(activity["gpx"]) log_message("Saved .gpx for", activity["name"], activity["time_day"])
def _wait_for(self, locator, element, attempts=3): for i in range(attempts): log_message("attempt", str(i)) try: WebDriverWait(self.browser, self.BROWSER_WAIT_TIMEOUT_SECONDS).until( EC.presence_of_element_located( (locator, element))) # wait until fully loaded self.browser.find_element(locator, element) return True except: pass # maybe next time log_message(self.BROWSER_TIMEOUT_ERROR.format(element)) return False
def get_day(self, date_time): """ :param date_time: datetime Datetime object with date :return: GCDayTimline Data about day """ log_message("Getting day", str(date_time)) self.go_to_day(date_time) soup = self.get_html_parser() tabs_html = soup.find("div", {"class": "tab-content"}) summary_html = soup.find( "div", {"class": "content page steps sleep calories timeline"}) steps_html = soup.find("div", {"class": "row-fluid bottom-m"}) try: sleep_html = tabs_html.find("div", {"id": "pane5"}) log_message("found sleep data") except: sleep_html = None log_message("NOT found sleep data") try: activities_html = tabs_html.find("div", {"id": "pane4"}) log_message("found activities data") except: activities_html = None log_message("NOT found activities data") try: breakdown_html = tabs_html.find("div", {"id": "pane2"}) log_message("found breakdown data") except: breakdown_html = None log_message("NOT found breakdown data") yesterday = date_time + timedelta(days=-1) today = date_time tomorrow = date_time + timedelta(days=1) steps_details_html_yesterday = json.loads( self.get_steps_details(yesterday)) steps_details_html_today = json.loads(self.get_steps_details(today)) steps_details_html_tomorrow = json.loads( self.get_steps_details(tomorrow)) steps_details_html = json.dumps( steps_details_html_yesterday + steps_details_html_today + steps_details_html_tomorrow) # merge days return GCDayTimeline(date_time, summary_html, steps_html, steps_details_html, sleep_html, activities_html, breakdown_html)