From 0abbee69c80f64d25215f09f446cb1394e47264c Mon Sep 17 00:00:00 2001 From: JackRabbit <38199281+jackrabbitbit@users.noreply.github.com> Date: Thu, 29 Apr 2021 06:18:59 -0700 Subject: [PATCH 1/6] Update watcher.py started to add in Kroger Scheduling --- vaccinewatcher/watcher.py | 57 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/vaccinewatcher/watcher.py b/vaccinewatcher/watcher.py index 05f2e5c..c804e68 100644 --- a/vaccinewatcher/watcher.py +++ b/vaccinewatcher/watcher.py @@ -100,20 +100,22 @@ class Config: ] _avail_links = { 'cvs': 'https://www.cvs.com//vaccine/intake/store/cvd-schedule.html?icid=coronavirus-lp-vaccine-sd-statetool', - 'wg': 'https://www.walgreens.com/findcare/vaccination/covid-19?ban=covid_scheduler_brandstory_main_March2021' + 'wg': 'https://www.walgreens.com/findcare/vaccination/covid-19?ban=covid_scheduler_brandstory_main_March2021', + 'kroger': 'https://www.kroger.com/rx/covid-eligibility' } class VaccineWatcher: - def __init__(self, config, freq_secs=600, hook=None, check_walgreens=True, check_cvs=True, send_data=True, always_send=False, verbose=False): + def __init__(self, config, freq_secs=600, hook=None, check_walgreens=True, check_cvs=True, check_kroger=True, send_data=True, always_send=False, verbose=False): self.config = Config(**config) self.freq = freq_secs self.send_data = send_data self.always_send = always_send self.hook = hook self.verbose = verbose - self._last_status = {'walgreens': {'available': False, 'data': None, 'timestamp': None}, 'cvs': {'available': False, 'data': None, 'timestamp': None}} + self._last_status = {'walgreens': {'available': False, 'data': None, 'timestamp': None}, 'cvs': {'available': False, 'data': None, 'timestamp': None}, 'kroger': {'available': False, 'data': None, 'timestamp': None}} self._check_wg = check_walgreens self._check_cvs = check_cvs + self._check_kroger = check_kroger self.api = Browser() self.browser = self.api.browser self.alive = True @@ -180,6 +182,49 @@ def check_cvs(self): return self._cvs_parser(r.response) return None + def _kroger_parser(self): + data = json.loads(resp.body.decode('utf-8'))['responsePayloadData']['data'][self.config.state_abbr] + for item in data: + if item['city'] == self.config.city.upper(): + self._last_status['cvs']['data'] = item + if item['status'] == 'Available': + msg = f'Kroger has Available Appointments in {item["city"]}, {item["state"]}' + msg += f'\nPlease Visit: {_avail_links["kroger"]} to schedule.' + self._call_hook(msg) + logger.log(msg) + return True + if self.verbose: + msg = f'Results for Kroger: {item}' + logger.log(msg) + return False + + def check_kroger(self): + self.browser.visit('https://www.kroger.com/rx/covid-eligibility') + self.browser.get_element(text='I Agree').click() + time.sleep(2) + self.browser.get_element(text='No').click() + time.sleep(2) + self.browser.get_element(partial_text='Select...', wait=2).get_element(value=self.config.state_abbr).select() + time.sleep(2) + self.browser.get_element(text='MM/DD/YYYY').fill("01/01/1980") + time.sleep(1) + self.browser.get_button(class_name="kds-Button kds-Button--primary KrogerForm-form-button KrogerForm-form-button").click() + time.sleep(2) + self.browser.get_button(text="No").click() + time.sleep(2) + self.browser.get_button(text='No').click() + time.sleep(2) + self.browser.get_button(text='Schedule Your COVID-19 Vaccine').click() # clicking the submit button takes you to a new url https://www.kroger.com/rx/covid-vaccine + time.sleep(2) + self.browser.get_element(id='ko2vcuwa-input').fill(self.config.zipcode) + time.sleep(2) + self.browser.get_element(text='Find Appointment').click() + if self.browser.get_element(text="None of the locations in your search currently have appointments available for COVID-19 vaccines. Please try another zip code or city within the state in which you're eligible, or check back soon for available appointments.") == True: + return None + return self._kroger_parser() + + + def run(self): if not self.dactive: t = threading.Thread(target=self._daemon, daemon=True) @@ -211,6 +256,9 @@ def _daemon(self): if self._check_wg: self._last_status['walgreens']['available'] = self.check_wg() self._last_status['walgreens']['timestamp'] = create_timestamp() + if self._check_kroger: + self._last_status['kroger']['available'] = self.check_kroger() + self._last_status['kroger']['timestamp'] = create_timestamp() self._call_hook() self.api.should_reset() time.sleep(self.freq) @@ -286,13 +334,14 @@ def cli(): parser.add_argument('--no-cvs', dest='cvs', default=True, action='store_false', help='Disable CVS Search.') parser.add_argument('--no-wg', dest='wg', default=True, action='store_false', help='Disable Walgreens Search.') + parser.add_argument('--no-kroger', dest='kroger', default=True, action='store_false', help='Disable Kroger Search.') parser.add_argument('--verbose', dest='verbose', default=False, action='store_true', help='Enable verbosity. Will log results regardless of status') args = parser.parse_args() params = {'city': args.city.capitalize(), 'state': args.state.capitalize(), 'state_abbr': args.state_abbr.upper(), 'zipcode': args.zipcode} hook = None if args.zapierhook: hook = ZapierWebhook(args.zapierhook) - watcher = get_vaccine_watcher(config=params, freq_secs=args.freq, hook=hook, check_walgreens=args.wg, check_cvs=args.cvs, verbose=args.verbose) + watcher = get_vaccine_watcher(config=params, freq_secs=args.freq, hook=hook, check_walgreens=args.wg, check_cvs=args.cvs, check_kroger=args.kroger, verbose=args.verbose) watcher.run() while True: try: From 0005c3fa2e6a69ebd1dd1b701cc82a89fd2d59aa Mon Sep 17 00:00:00 2001 From: Christopher Thompson Date: Thu, 29 Apr 2021 10:00:29 -0700 Subject: [PATCH 2/6] () --- vaccinewatcher/watcher.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/vaccinewatcher/watcher.py b/vaccinewatcher/watcher.py index c804e68..a190578 100644 --- a/vaccinewatcher/watcher.py +++ b/vaccinewatcher/watcher.py @@ -204,12 +204,14 @@ def check_kroger(self): time.sleep(2) self.browser.get_element(text='No').click() time.sleep(2) - self.browser.get_element(partial_text='Select...', wait=2).get_element(value=self.config.state_abbr).select() + self.browser.get_element(class_name="kds-Select").get_element(value=self.config.state_abbr).select() + #self.browser.get_element(partial_text='Select...').get_element(value=self.config.state_abbr).select() time.sleep(2) - self.browser.get_element(text='MM/DD/YYYY').fill("01/01/1980") + self.browser.get_element(class_name="kds-Input").fill("01/01/1980") time.sleep(1) - self.browser.get_button(class_name="kds-Button kds-Button--primary KrogerForm-form-button KrogerForm-form-button").click() - time.sleep(2) + self.browser.get_button(partial_text="Submit").click() + # self.browser.get_button(class_name="kds-Button kds-Button--primary KrogerForm-form-button KrogerForm-form-button").click() + # time.sleep(2) self.browser.get_button(text="No").click() time.sleep(2) self.browser.get_button(text='No').click() From 96bfe47454fc7f8e3bae44e6fe95f5126d68baae Mon Sep 17 00:00:00 2001 From: Christopher Thompson Date: Thu, 29 Apr 2021 11:04:02 -0700 Subject: [PATCH 3/6] works until find appointment --- vaccinewatcher/watcher.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/vaccinewatcher/watcher.py b/vaccinewatcher/watcher.py index a190578..978f7b0 100644 --- a/vaccinewatcher/watcher.py +++ b/vaccinewatcher/watcher.py @@ -209,18 +209,17 @@ def check_kroger(self): time.sleep(2) self.browser.get_element(class_name="kds-Input").fill("01/01/1980") time.sleep(1) - self.browser.get_button(partial_text="Submit").click() - # self.browser.get_button(class_name="kds-Button kds-Button--primary KrogerForm-form-button KrogerForm-form-button").click() - # time.sleep(2) - self.browser.get_button(text="No").click() - time.sleep(2) - self.browser.get_button(text='No').click() + self.browser.get_element(class_name="SearchOverlay").click() + time.sleep(1) + self.browser.get_element(class_name="Attachment-ButtonGroup").click() + time.sleep(1) + self.browser.get_element(class_name="Attachment-ButtonGroup").click() time.sleep(2) - self.browser.get_button(text='Schedule Your COVID-19 Vaccine').click() # clicking the submit button takes you to a new url https://www.kroger.com/rx/covid-vaccine + self.browser.get_element(class_name="Attachment-ButtonGroup").click()#get_button(partial_text="Schedule Your COVID-19 Vaccine").click() # clicking the submit button takes you to a new url https://www.kroger.com/rx/covid-vaccine time.sleep(2) - self.browser.get_element(id='ko2vcuwa-input').fill(self.config.zipcode) + self.browser.get_element(class_name="kds-Input").fill(self.config.zipcode) time.sleep(2) - self.browser.get_element(text='Find Appointment').click() + self.browser.get_element(class_name="kds-Form-field").click() if self.browser.get_element(text="None of the locations in your search currently have appointments available for COVID-19 vaccines. Please try another zip code or city within the state in which you're eligible, or check back soon for available appointments.") == True: return None return self._kroger_parser() From 98c556dea02112b152bd2d39acb245b1f0891bbb Mon Sep 17 00:00:00 2001 From: Christopher Thompson Date: Thu, 29 Apr 2021 12:03:01 -0700 Subject: [PATCH 4/6] code cleanup --- vaccinewatcher/watcher.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vaccinewatcher/watcher.py b/vaccinewatcher/watcher.py index 978f7b0..b3de8d9 100644 --- a/vaccinewatcher/watcher.py +++ b/vaccinewatcher/watcher.py @@ -205,7 +205,6 @@ def check_kroger(self): self.browser.get_element(text='No').click() time.sleep(2) self.browser.get_element(class_name="kds-Select").get_element(value=self.config.state_abbr).select() - #self.browser.get_element(partial_text='Select...').get_element(value=self.config.state_abbr).select() time.sleep(2) self.browser.get_element(class_name="kds-Input").fill("01/01/1980") time.sleep(1) @@ -215,7 +214,7 @@ def check_kroger(self): time.sleep(1) self.browser.get_element(class_name="Attachment-ButtonGroup").click() time.sleep(2) - self.browser.get_element(class_name="Attachment-ButtonGroup").click()#get_button(partial_text="Schedule Your COVID-19 Vaccine").click() # clicking the submit button takes you to a new url https://www.kroger.com/rx/covid-vaccine + self.browser.get_element(class_name="Attachment-ButtonGroup").click() # clicking the submit button takes you to a new url https://www.kroger.com/rx/covid-vaccine time.sleep(2) self.browser.get_element(class_name="kds-Input").fill(self.config.zipcode) time.sleep(2) From a76ce1b776d9a0d9c85b1e9ac3d7828ee5b97fff Mon Sep 17 00:00:00 2001 From: JackRabbit <38199281+jackrabbitbit@users.noreply.github.com> Date: Fri, 30 Apr 2021 08:08:25 -0700 Subject: [PATCH 5/6] Update watcher.py updated timeouts and possible "find appointment" --- vaccinewatcher/watcher.py | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/vaccinewatcher/watcher.py b/vaccinewatcher/watcher.py index 978f7b0..8f14e01 100644 --- a/vaccinewatcher/watcher.py +++ b/vaccinewatcher/watcher.py @@ -120,7 +120,7 @@ def __init__(self, config, freq_secs=600, hook=None, check_walgreens=True, check self.browser = self.api.browser self.alive = True self.dactive = False - logger.log(f'Initialized VaccineWatcher with {self.config}. Will Check every {self.freq} secs. Walgreens: {self._check_wg}. CVS: {self._check_cvs}\nCall .run() to start daemon') + logger.log(f'Initialized VaccineWatcher with {self.config}. Will Check every {self.freq} secs. Walgreens: {self._check_wg}. CVS: {self._check_cvs}. Kroger: {self._check_kroger}.\nCall .run() to start daemon') def _wg_parser(self, resp): data = json.loads(resp.body.decode('utf-8')) @@ -200,31 +200,30 @@ def _kroger_parser(self): def check_kroger(self): self.browser.visit('https://www.kroger.com/rx/covid-eligibility') + time.sleep(3) self.browser.get_element(text='I Agree').click() - time.sleep(2) + time.sleep(3) self.browser.get_element(text='No').click() - time.sleep(2) + time.sleep(3) self.browser.get_element(class_name="kds-Select").get_element(value=self.config.state_abbr).select() - #self.browser.get_element(partial_text='Select...').get_element(value=self.config.state_abbr).select() - time.sleep(2) + time.sleep(3) self.browser.get_element(class_name="kds-Input").fill("01/01/1980") - time.sleep(1) + time.sleep(3) self.browser.get_element(class_name="SearchOverlay").click() - time.sleep(1) + time.sleep(3) self.browser.get_element(class_name="Attachment-ButtonGroup").click() - time.sleep(1) + time.sleep(3) self.browser.get_element(class_name="Attachment-ButtonGroup").click() - time.sleep(2) - self.browser.get_element(class_name="Attachment-ButtonGroup").click()#get_button(partial_text="Schedule Your COVID-19 Vaccine").click() # clicking the submit button takes you to a new url https://www.kroger.com/rx/covid-vaccine - time.sleep(2) + time.sleep(3) + self.browser.get_element(class_name="Attachment-ButtonGroup").click() + time.sleep(4) self.browser.get_element(class_name="kds-Input").fill(self.config.zipcode) time.sleep(2) - self.browser.get_element(class_name="kds-Form-field").click() - if self.browser.get_element(text="None of the locations in your search currently have appointments available for COVID-19 vaccines. Please try another zip code or city within the state in which you're eligible, or check back soon for available appointments.") == True: - return None - return self._kroger_parser() - - + self.browser.get_element(class_name="SearchOverlay").click() #found this to be "SearchOverlay" based on error message, but I am not sure how to verify. + time.sleep(5) #increased timeout to see if that makes a difference + if self.browser.get_element(class_name="MultipleLocationScheduler-sortingButton") == True: #stuck here + return self._kroger_parser() + return None def run(self): if not self.dactive: From 4642e5318d98381ec8fbefcb8e6e4897f49af29d Mon Sep 17 00:00:00 2001 From: Christopher Thompson Date: Fri, 30 Apr 2021 08:24:13 -0700 Subject: [PATCH 6/6] got down to previous vaccine question --- vaccinewatcher/watcher.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/vaccinewatcher/watcher.py b/vaccinewatcher/watcher.py index b3de8d9..f4b3b7f 100644 --- a/vaccinewatcher/watcher.py +++ b/vaccinewatcher/watcher.py @@ -32,7 +32,7 @@ def __init__(self): self.chrome_options = webdriver.ChromeOptions() self.chrome_options.add_argument("--disable-gpu") self.chrome_options.add_argument("--disable-software-rasterizer") - self.chrome_options.add_argument("--headless") + #self.chrome_options.add_argument("--headless") self.chrome_options.add_argument("--disable-dev-shm-usage") self.chrome_options.add_argument("--window-size=1920x1080") self.chrome_options.add_argument("--disable-setuid-sandbox") @@ -206,19 +206,21 @@ def check_kroger(self): time.sleep(2) self.browser.get_element(class_name="kds-Select").get_element(value=self.config.state_abbr).select() time.sleep(2) - self.browser.get_element(class_name="kds-Input").fill("01/01/1980") + self.browser.get_element(name="Date of Birth").fill("01/01/1980") time.sleep(1) - self.browser.get_element(class_name="SearchOverlay").click() + self.browser.get_button(text="Submit").click() + time.sleep(2) + self.browser.get_element(class_name="Attachment-ButtonGroup.not-allowed").get_button(text='No').click() time.sleep(1) - self.browser.get_element(class_name="Attachment-ButtonGroup").click() + #self.browser.get_element(class_name="Attachment-ButtonGroup").click() time.sleep(1) - self.browser.get_element(class_name="Attachment-ButtonGroup").click() + self.browser.get_element(class_name="Attachment-ButtonGroup", wait=10).click() time.sleep(2) self.browser.get_element(class_name="Attachment-ButtonGroup").click() # clicking the submit button takes you to a new url https://www.kroger.com/rx/covid-vaccine time.sleep(2) self.browser.get_element(class_name="kds-Input").fill(self.config.zipcode) time.sleep(2) - self.browser.get_element(class_name="kds-Form-field").click() + if self.browser.get_element(text="None of the locations in your search currently have appointments available for COVID-19 vaccines. Please try another zip code or city within the state in which you're eligible, or check back soon for available appointments.") == True: return None return self._kroger_parser()