Skip to content

Commit 20fe650

Browse files
Refactor: removes the complicated gmail attachments downloading process
1 parent 58c5159 commit 20fe650

File tree

4 files changed

+110
-192
lines changed

4 files changed

+110
-192
lines changed

src/fitfiletools_automation.py

+108-119
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ def fix_Fit_Activity_Files():
2424
owd = os.getcwd()
2525
os.chdir(ZWIFT_ACTIVITY_DIR)
2626
fitfile_list = glob.glob("*.fit")
27-
if ('inProgressActivity.fit' in fitfile_list):
28-
fitfile_list.remove('inProgressActivity.fit')
2927
if not fitfile_list:
3028
sys.exit("\nNo .fit file(s) to be fixed and uploaded to Strava.\nAborting...")
3129
else:
32-
print('\nWorkout data found: ', end="")
33-
for f in fitfile_list:
34-
print('\n\t' + f, end="")
35-
print('\n')
30+
for fitfile in fitfile_list:
31+
if (fitfile == 'inProgressActivity.fit') or \
32+
(os.path.getsize(fitfile) < 10000): # check if the size of the fit file is smaller than 10KB
33+
fitfile_list.remove(fitfile)
34+
os.remove(os.path.join(ZWIFT_ACTIVITY_DIR, fitfile))
35+
3636
with alive_bar(1, title='Opening the webpage of FIT File Tools', bar="blocks", spinner="classic") as bar:
3737
dir = os.path.dirname(__file__)
3838
WEB_DRIVER_PATH = os.path.abspath( os.path.join(dir, '..', 'webdriver', 'geckodriver.exe') )
@@ -49,133 +49,122 @@ def fix_Fit_Activity_Files():
4949

5050
with alive_bar(len(fitfile_list), title='Fixing FIT activity files', bar="blocks") as bar:
5151
for fitfile in fitfile_list:
52-
53-
path_to_fitfile = os.path.join(ZWIFT_ACTIVITY_DIR, fitfile)
54-
55-
# check if the size of the fit file is smaller than 10KB
56-
if (os.path.getsize(path_to_fitfile) < 10000):
57-
move_To_Original_Activities_Folder(fitfile)
52+
try:
53+
bar.text("Fixing " + fitfile + "...")
54+
55+
# Step 2: click the "Launch" button of "Time Adjuster"
56+
element = WebDriverWait(driver, 5).until(
57+
EC.presence_of_element_located((By.XPATH, "//a[@href='#/adjuster']"))
58+
)
59+
element.click()
60+
61+
# Step 3: click the "... or select files" button
62+
element = WebDriverWait(driver, 5).until(
63+
EC.presence_of_element_located((By.XPATH, "//button[@ng-file-select='']"))
64+
)
65+
element.click()
66+
time.sleep(1)
5867

59-
# update progress bar
68+
# Step 4: select the corresponding fit file to be fixed
69+
pyperclip.copy(os.path.join(ZWIFT_ACTIVITY_DIR, fitfile)) # copy to clipboard
70+
pyautogui.hotkey('ctrl', "v") # ctrl-v to paste from clipboard
71+
finish_file_selection(element)
6072
time.sleep(1)
61-
bar()
62-
else:
63-
try:
64-
bar.text("Fixing " + fitfile + "...")
6573

66-
# Step 2: click the "Launch" button of "Time Adjuster"
67-
element = WebDriverWait(driver, 5).until(
68-
EC.presence_of_element_located((By.XPATH, "//a[@href='#/adjuster']"))
69-
)
70-
element.click()
71-
72-
# Step 3: click the "... or select files" button
73-
element = WebDriverWait(driver, 5).until(
74-
EC.presence_of_element_located((By.XPATH, "//button[@ng-file-select='']"))
75-
)
76-
element.click()
77-
time.sleep(1)
78-
79-
# Step 4: select the corresponding fit file to be fixed
80-
pyperclip.copy(path_to_fitfile) # copy to clipboard
81-
pyautogui.hotkey('ctrl', "v") # ctrl-v to paste from clipboard
82-
finish_file_selection(element)
83-
time.sleep(1)
84-
85-
# Step 5: select the start date of the activity
86-
fitfilebasename = Path(fitfile).stem
87-
fitfilebasename_arr = fitfilebasename.split('-') # YYYY-MM-DD-hh-mm-ss
88-
# parsing
89-
year = int(fitfilebasename_arr[0])
90-
month = int(fitfilebasename_arr[1])
91-
day = int(fitfilebasename_arr[2])
92-
hour = int(fitfilebasename_arr[3])
93-
minute = int(fitfilebasename_arr[4])
94-
second = int(fitfilebasename_arr[5])
95-
96-
# convert time from 24-hour clock format to 12-hour clock format
97-
if (hour > 12):
98-
afternoon = True
99-
hour -= 12
100-
else:
101-
afternoon = False
102-
if (hour == 0): # special case
103-
hour = 12
104-
105-
element = WebDriverWait(driver, 5).until(
106-
EC.presence_of_element_located((By.XPATH, "//input[@ng-model='dt']"))
107-
)
108-
element.clear()
109-
# convert a month number to a month name
110-
month_name = calendar.month_name[month]
111-
DD_MM_YYYY = str(day) + '-' + month_name + '-' + str(year)
112-
element.send_keys(DD_MM_YYYY)
113-
time.sleep(1)
74+
# Step 5: select the start date of the activity
75+
fitfilebasename = Path(fitfile).stem
76+
fitfilebasename_arr = fitfilebasename.split('-') # YYYY-MM-DD-hh-mm-ss
77+
# parsing
78+
year = int(fitfilebasename_arr[0])
79+
month = int(fitfilebasename_arr[1])
80+
day = int(fitfilebasename_arr[2])
81+
hour = int(fitfilebasename_arr[3])
82+
minute = int(fitfilebasename_arr[4])
83+
second = int(fitfilebasename_arr[5])
84+
85+
# convert time from 24-hour clock format to 12-hour clock format
86+
if (hour > 12):
87+
afternoon = True
88+
hour -= 12
89+
else:
90+
afternoon = False
91+
if (hour == 0): # special case
92+
hour = 12
93+
94+
element = WebDriverWait(driver, 5).until(
95+
EC.presence_of_element_located((By.XPATH, "//input[@ng-model='dt']"))
96+
)
97+
element.clear()
98+
# convert a month number to a month name
99+
month_name = calendar.month_name[month]
100+
DD_MM_YYYY = str(day) + '-' + month_name + '-' + str(year)
101+
element.send_keys(DD_MM_YYYY)
102+
time.sleep(1)
114103

115-
# Step 6: select the start time (hours & minutes) of the activity
116-
# fill in minutes
117-
element = WebDriverWait(driver, 5).until(
118-
EC.presence_of_element_located((By.XPATH, "//input[@ng-model='minutes']"))
119-
)
120-
element.clear()
121-
element.send_keys(minute)
122-
time.sleep(1)
104+
# Step 6: select the start time (hours & minutes) of the activity
105+
# fill in minutes
106+
element = WebDriverWait(driver, 5).until(
107+
EC.presence_of_element_located((By.XPATH, "//input[@ng-model='minutes']"))
108+
)
109+
element.clear()
110+
element.send_keys(minute)
111+
time.sleep(1)
123112

124-
# choose A.M. or P.M.
125-
if (not afternoon):
126-
element = WebDriverWait(driver, 5).until(
127-
EC.presence_of_element_located((By.XPATH, "//button[@ng-click='toggleMeridian()']"))
128-
)
129-
element.click()
130-
time.sleep(1)
131-
132-
# fill in hours
113+
# choose A.M. or P.M.
114+
if (not afternoon):
133115
element = WebDriverWait(driver, 5).until(
134-
EC.presence_of_element_located((By.XPATH, "//input[@ng-model='hours']"))
116+
EC.presence_of_element_located((By.XPATH, "//button[@ng-click='toggleMeridian()']"))
135117
)
136-
element.clear()
137-
element.send_keys(str(hour))
118+
element.click()
138119
time.sleep(1)
120+
121+
# fill in hours
122+
element = WebDriverWait(driver, 5).until(
123+
EC.presence_of_element_located((By.XPATH, "//input[@ng-model='hours']"))
124+
)
125+
element.clear()
126+
element.send_keys(str(hour))
127+
time.sleep(1)
139128

140-
# Step 7: start fixing the activity file
141-
element = WebDriverWait(driver, 5).until(
142-
EC.presence_of_element_located((By.XPATH, "//a[@ng-click='adjust()']"))
143-
)
144-
element.click()
129+
# Step 7: start fixing the activity file
130+
element = WebDriverWait(driver, 5).until(
131+
EC.presence_of_element_located((By.XPATH, "//a[@ng-click='adjust()']"))
132+
)
133+
element.click()
134+
135+
# Step 8: download your file
136+
element = WebDriverWait(driver, 5).until(
137+
EC.presence_of_element_located((By.LINK_TEXT, 'Click here to download your file!'))
138+
)
139+
element.click()
140+
time.sleep(3)
141+
# pyautogui.press('return')
142+
# time.sleep(3)
143+
144+
# update progress bar
145+
bar()
145146

146-
# Step 8: download your file
147-
element = WebDriverWait(driver, 5).until(
148-
EC.presence_of_element_located((By.LINK_TEXT, 'Click here to download your file!'))
149-
)
150-
element.click()
151-
time.sleep(3)
152-
# pyautogui.press('return')
153-
# time.sleep(3)
154-
155-
# update progress bar
156-
bar()
157-
158-
# Step 9: click the "Close" button of "Time Adjuster"
159-
element = WebDriverWait(driver, 5).until(
160-
EC.presence_of_element_located((By.LINK_TEXT, '« Close'))
161-
)
162-
element.click()
147+
# Step 9: click the "Close" button of "Time Adjuster"
148+
element = WebDriverWait(driver, 5).until(
149+
EC.presence_of_element_located((By.LINK_TEXT, '« Close'))
150+
)
151+
element.click()
163152

164-
# Step 10: rename file in Downloads folder and move to FixedActivities folder
165-
newfitfilename = fitfilebasename + "-fixed" + ".fit"
166-
rename_FitFile(newfitfilename)
167-
move_To_Fixed_Activities_Folder(newfitfilename)
153+
# Step 10: rename file in Downloads folder and move to FixedActivities folder
154+
newfitfilename = fitfilebasename + "-fixed" + ".fit"
155+
rename_FitFile(newfitfilename)
156+
move_To_Fixed_Activities_Folder(newfitfilename)
168157

169-
# Step 11: move the original fit file in ZWIFT_ACTIVITY_DIR to OriginalActivities folder
170-
move_To_Original_Activities_Folder(fitfile)
158+
# Step 11: move the original fit file in ZWIFT_ACTIVITY_DIR to OriginalActivities folder
159+
move_To_Original_Activities_Folder(fitfile)
171160

172-
except TimeoutException:
173-
print("ERROR - Timeout!")
174-
driver.quit()
161+
except TimeoutException:
162+
print("ERROR - Timeout!")
163+
driver.quit()
175164

176-
except NoSuchElementException:
177-
print("ERROR - Cannot find the element!")
178-
driver.quit()
165+
except NoSuchElementException:
166+
print("ERROR - Cannot find the element!")
167+
driver.quit()
179168

180169
# Step 12: finally, close the web browser window and change back to the original working directory
181170
driver.quit()

src/read_config_file.py

-10
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,6 @@
1515
except KeyError:
1616
print("[ERROR]: Please add STRAVA_CLIENT_SECRET to the list of environment variables")
1717

18-
try:
19-
GMAIL_USER_ID = os.environ["GMAIL_USER_ID"]
20-
except KeyError:
21-
print("[ERROR]: Please add GMAIL_USER_ID to the list of environment variables")
22-
23-
try:
24-
GMAIL_PASSWORD = os.environ["GMAIL_PASSWORD"]
25-
except KeyError:
26-
print("[ERROR]: Please add GMAIL_PASSWORD to the list of environment variables")
27-
2818
try:
2919
LINE_CHANNEL_ACCESS_TOKEN = os.environ["LINE_CHANNEL_ACCESS_TOKEN"]
3020
except KeyError:

src/strava_upload.py

-61
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
import os
2-
import sys
32
import requests
43
import subprocess
54
import glob
65
import time
7-
import datetime
8-
import imaplib
9-
import email
106
import re
117
from typing import Any, Tuple
128
from alive_progress import alive_bar
@@ -38,33 +34,6 @@ def preprocessing():
3834
exit_code = os.system("taskkill /f /im " + zwift)
3935
if (exit_code == 0):
4036
print("Successfully killed", zwift)
41-
else:
42-
print(zwift, "is not running")
43-
sys.exit("Aborting...")
44-
else:
45-
# Enable less secure apps on your Google account:
46-
# https://myaccount.google.com/lesssecureapps
47-
print(zwift + " is not running." + "\n" + "Try to find .fit file(s) from GMAIL...")
48-
gmail = imaplib.IMAP4_SSL("imap.gmail.com")
49-
typ, accountDetails = gmail.login(GMAIL_USER_ID, GMAIL_PASSWORD)
50-
if (typ != 'OK'):
51-
print('Not able to sign in!')
52-
raise
53-
typ, data = gmail.select('Inbox')
54-
if (typ != 'OK'):
55-
print('Error searching Inbox!')
56-
raise
57-
today = datetime.date.today().strftime("%d-%b-%Y")
58-
typ, email_list = gmail.search(None, f'(ON {today} TO {GMAIL_USER_ID})')
59-
# print(email_list)
60-
# Useful links:
61-
# 1. https://docs.python.org/3/library/imaplib.html#imaplib.IMAP4.search
62-
# 2. https://gist.github.com/martinrusev/6121028
63-
email_list = email_list[0].split()
64-
for email_id in email_list:
65-
downloaAttachmentsInEmail(gmail, email_id, ZWIFT_ACTIVITY_DIR)
66-
gmail.close()
67-
gmail.logout()
6837

6938

7039
# ------------------------------------
@@ -172,36 +141,6 @@ def wait(poll_interval: float):
172141
def findWholeWord(w):
173142
return re.compile(r'\b({0})\b'.format(w), flags=re.IGNORECASE).search
174143

175-
def downloaAttachmentsInEmail(connection, email_id, download_folder):
176-
"""
177-
Function to download all attachment files for a given email
178-
"""
179-
try:
180-
typ, data = connection.fetch(email_id, "(BODY.PEEK[])") # use PEEK so we don't change the UNSEEN status of the email messages
181-
if (typ != 'OK'):
182-
print('Error fetching email!')
183-
raise
184-
email_body = data[0][1]
185-
raw_emails = email.message_from_bytes(email_body)
186-
# print(raw_emails)
187-
for mail in raw_emails.walk():
188-
if (mail.get_content_maintype() == 'multipart'):
189-
# print(mail.as_string())
190-
continue
191-
if (mail.get('Content-Disposition') is None):
192-
# print(mail.as_string())
193-
continue
194-
fileName = mail.get_filename()
195-
if fileName.endswith('.fit'):
196-
attachment_path = os.path.join(download_folder, fileName)
197-
if not os.path.isfile(attachment_path):
198-
print('Downloading email attachment: ' + fileName + '...')
199-
f = open(attachment_path, 'wb')
200-
f.write(mail.get_payload(decode=True))
201-
f.close()
202-
except:
203-
print('Error downloading all attachments!')
204-
205144
def link(uri, label=None):
206145
"""
207146
Source: https://stackoverflow.com/a/71309268/10351382

tokens.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
STRAVA_ACCESS_TOKEN = c0bbcd8f24c12dea971aa8bf34a33f4ec0f30291
1+
STRAVA_ACCESS_TOKEN = cccc640ddb0f3bb015e7f259daed3a6a0282780c
22
STRAVA_REFRESH_TOKEN = 94f06d238645a184bdc3a5283d9badeb8d2e9b0b
3-
STRAVA_TOKEN_EXPIRY_TIME = 1676225636
3+
STRAVA_TOKEN_EXPIRY_TIME = 1677439745

0 commit comments

Comments
 (0)