New Demo Program - FedEx Package Tracking Desktop Widget
This commit is contained in:
parent
a845014317
commit
b670662ead
|
@ -0,0 +1,178 @@
|
|||
import requests
|
||||
import PySimpleGUI as sg
|
||||
import datetime
|
||||
|
||||
"""
|
||||
Demo - FedEx Package Tracking
|
||||
|
||||
A simple Desktop Widget that checks your FedEx tracking number and
|
||||
shows the current delivery estimate.
|
||||
|
||||
USING:
|
||||
Enter a tracking number in the input element
|
||||
Right click and choose Refresh
|
||||
If additional tracking numbers are desired, right click and choose Add Package
|
||||
|
||||
The status information is courtesy of @israel-dryer (https://github.com/israel-dryer)
|
||||
He used web scraping to gather the data. This removes the need for using an account and access package.
|
||||
|
||||
At the moment only FedEx is supported. The drop-down list is there for future support of other carriers.
|
||||
|
||||
Like other PySimpleGUI Desktop Widgets, a number of standard features are include such as:
|
||||
* Alpha Channel
|
||||
* Theme Selection
|
||||
* Screen location
|
||||
* Edit Me (launcher your editor for easy code modification)
|
||||
|
||||
Copyright 2021 PySimpleGUI, Israel Dryer
|
||||
"""
|
||||
|
||||
def choose_theme(location):
|
||||
layout = [[sg.Text(f'Current theme {sg.theme()}')],
|
||||
[sg.Listbox(values=sg.theme_list(), size=(20, 20), key='-LIST-', enable_events=True)],
|
||||
[sg.OK(), sg.Cancel()]]
|
||||
|
||||
window = sg.Window('Look and Feel Browser', layout, location=location, keep_on_top=True, no_titlebar=True)
|
||||
old_theme = sg.theme()
|
||||
while True: # Event Loop
|
||||
event, values = window.read()
|
||||
if event in (sg.WIN_CLOSED, 'Exit', 'OK', 'Cancel'):
|
||||
break
|
||||
sg.theme(values['-LIST-'][0])
|
||||
window.close()
|
||||
|
||||
if event == 'OK' and values['-LIST-']:
|
||||
sg.theme(values['-LIST-'][0])
|
||||
sg.user_settings_set_entry('-theme-', values['-LIST-'][0])
|
||||
return values['-LIST-'][0]
|
||||
else:
|
||||
sg.theme(old_theme)
|
||||
return None
|
||||
|
||||
|
||||
def shipping_status(tracking_num):
|
||||
"""Request shipment status via tracking number.
|
||||
Args:
|
||||
tracking_num (str): The FedEx tracking number assigned to the shipment.
|
||||
"""
|
||||
url = "https://www.fedex.com/trackingCal/track"
|
||||
headers = {
|
||||
'Host': 'www.fedex.com',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0',
|
||||
'Accept': '*/*',
|
||||
'Accept-Language': 'en-US,en;q=0.5',
|
||||
'Accept-Encoding': 'gzip, deflate, br',
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'Origin': 'https://www.fedex.com',
|
||||
'Connection': 'keep-alive',
|
||||
'Referer': 'https://www.fedex.com/en-us/home.html',
|
||||
}
|
||||
payload = '''data=%7B%22TrackPackagesRequest%22%3A%7B%22appType%22%3A%22WTRK%22%2C%22appDeviceType%22%3A%22%22%2C%22supportHTML%22%3Atrue%2C%22supportCurrentLocation%22%3Atrue%2C%22uniqueKey%22%3A%22%22%2C%22processingParameters%22%3A%7B%7D%2C%22trackingInfoList%22%3A%5B%7B%22trackNumberInfo%22%3A%7B%22trackingNumber%22%3A%22{}%22%2C%22trackingQualifier%22%3A%22%22%2C%22trackingCarrier%22%3A%22%22%7D%7D%5D%7D%7D&action=trackpackages&locale=en_US&version=1&format=json'''
|
||||
|
||||
response = requests.post(url, headers=headers, data=payload.format(tracking_num))
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
return (None, response.status_code)
|
||||
|
||||
|
||||
def package_row(item_num, tracking_num=''):
|
||||
carrier_list = ('FedEx', 'USPS')
|
||||
|
||||
row = [sg.pin(sg.Col([[sg.B(sg.SYMBOL_X, border_width=0, button_color=sg.theme_background_color(), k=('-DEL-', item_num)),
|
||||
sg.Input(default_text=tracking_num, s=(20,1), key=('-ID-', item_num)), sg.Combo(carrier_list, default_value=carrier_list[0], s=(10,10), k=('-CARRIER-', item_num)), sg.T(size=(15,1), k=('-STATUS-', item_num))]], k=('-ROW-', item_num)))]
|
||||
return row
|
||||
|
||||
|
||||
def refresh(window: sg.Window):
|
||||
row_count = window.row_counter+1
|
||||
package_list = []
|
||||
for row in range(row_count):
|
||||
if not window[('-ROW-', row)].visible: # skip deleted rows
|
||||
continue
|
||||
status = shipping_status(window[('-ID-', row)].get())
|
||||
package_list.append(window[('-ID-', row)].get())
|
||||
if isinstance(status, tuple): # an error occured
|
||||
delivery_datetime = 'Error'
|
||||
else:
|
||||
delivery_datetime = status['TrackPackagesResponse']['packageList'][0]['displayEstDeliveryDateTime']
|
||||
window[('-STATUS-', row)].update(delivery_datetime)
|
||||
window['-REFRESHED-'].update(f'Refreshed {datetime.datetime.now().strftime("%m/%d/%Y %I:%M:%S %p")}')
|
||||
sg.user_settings_set_entry('-packages-', package_list)
|
||||
|
||||
|
||||
def add_packages_to_window(window: sg.Window):
|
||||
packages = sg.user_settings_get_entry('-packages-', [])
|
||||
for i, package in enumerate(packages):
|
||||
in_elem = window.find_element(('-ID-', i), silent_on_error=True)
|
||||
if isinstance(in_elem, sg.ErrorElement):
|
||||
window.row_counter += 1
|
||||
window.extend_layout(window['-TRACKING SECTION-'], [package_row(window.row_counter)])
|
||||
in_elem = window.find_element(('-ID-', window.row_counter), silent_on_error=True)
|
||||
in_elem.update(package)
|
||||
else:
|
||||
in_elem.update(package)
|
||||
|
||||
|
||||
def make_window(location):
|
||||
location = sg.user_settings_get_entry('-location-',location)
|
||||
alpha = sg.user_settings_get_entry('-alpha-', 0.9)
|
||||
|
||||
layout = [ [sg.Text('FedEx Package Tracking', font='_ 15')],
|
||||
[sg.Col([package_row(0)], k='-TRACKING SECTION-')],
|
||||
[sg.pin(sg.Text(size=(35,1), font='_ 8', k='-REFRESHED-',))],
|
||||
[sg.T(sg.SYMBOL_X, enable_events=True, k='Exit')]]
|
||||
|
||||
right_click_menu = [[''], ['Add Package', 'Edit Me', 'Change Theme', 'Save Location', 'Refresh', 'Alpha', [str(x) for x in range(1, 11)], 'Exit', ]]
|
||||
|
||||
window = sg.Window('Window Title', layout, finalize=True, no_titlebar=True, grab_anywhere=True, keep_on_top=True,
|
||||
right_click_menu=right_click_menu, alpha_channel=alpha, location=location, use_default_focus=False)
|
||||
return window
|
||||
|
||||
|
||||
def main():
|
||||
theme = sg.user_settings_get_entry('-theme-', 'Dark Gray 14')
|
||||
sg.theme(theme)
|
||||
location = sg.user_settings_get_entry('-location-', (None, None))
|
||||
|
||||
window = make_window(location)
|
||||
window.row_counter = 0
|
||||
add_packages_to_window(window)
|
||||
refresh(window)
|
||||
while True:
|
||||
event, values = window.read(timeout=1000*60*60) # wake every hour
|
||||
if event == sg.WIN_CLOSED or event == 'Exit':
|
||||
break
|
||||
if event == 'Add Package':
|
||||
window.row_counter += 1
|
||||
window.extend_layout(window['-TRACKING SECTION-'], [package_row(window.row_counter)])
|
||||
elif event == 'Edit Me':
|
||||
sg.execute_editor(__file__)
|
||||
elif event == 'Save Location':
|
||||
sg.user_settings_set_entry('-location-', window.current_location())
|
||||
elif event == 'Change Theme':
|
||||
loc = window.current_location()
|
||||
if choose_theme(loc) is not None:
|
||||
# this is result of hacking code down to 99 lines in total. Not tried it before. Interesting test.
|
||||
_, window = window.close(), make_window(loc)
|
||||
elif event in ('Refresh', sg.TIMEOUT_KEY):
|
||||
refresh(window)
|
||||
elif event in [str(x) for x in range(1,11)]:
|
||||
window.set_alpha(int(event)/10)
|
||||
sg.user_settings_set_entry('-alpha-', int(event)/10)
|
||||
if isinstance(event, tuple):
|
||||
if event[0] == '-DEL-':
|
||||
window[('-ROW-', event[1])].update(visible=False)
|
||||
packages: list = sg.user_settings_get_entry('-packages-', [])
|
||||
try:
|
||||
packages.remove(window[('-ID-', event[1])].get())
|
||||
except:
|
||||
pass
|
||||
sg.user_settings_set_entry('-packages-', packages)
|
||||
|
||||
window.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Reference in New Issue