From b684ab4462175cf9550e5a6e49be143af41317af Mon Sep 17 00:00:00 2001 From: Dreiundzwanzig Date: Sun, 20 Jun 2021 09:32:30 +0200 Subject: [PATCH 1/3] Add option to add additional center or doctor --- README.md | 2 ++ doctoshotgun.py | 17 +++++++++++++++-- test_browser.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f72631d..61ec62a 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,8 @@ Further optional arguments: exclude centers --center-exclude-regex CENTER_EXCLUDE_REGEX exclude centers by regex +--additional-center "name;city;link", -ac "name;city;link" + Add additional centers or doctors e.g. "Corona Impfzentren - Berlin;Berlin;/institut/berlin/ciz-berlin-berlin" --include-neighbor-city, -n include neighboring cities --start-date START_DATE diff --git a/doctoshotgun.py b/doctoshotgun.py index 8e9096e..b95f30d 100755 --- a/doctoshotgun.py +++ b/doctoshotgun.py @@ -293,7 +293,7 @@ def do_login(self, code): return True - def find_centers(self, where, motives=None, page=1): + def find_centers(self, where, motives=None, page=1, additional_centers=None): if motives is None: motives = self.vaccine_motives.keys() for city in where: @@ -335,6 +335,17 @@ def find_centers(self, where, motives=None, page=1): for center in self.find_centers(where, motives, next_page): yield center + if additional_centers: + for additional_center in additional_centers: + splitted = additional_center.split(';') + if len(splitted) == 3: + yield { + "name_with_title": splitted[0], #"Corona Impfzentren - Berlin", + "city": splitted[1], #"Berlin", + "url": splitted[2], #"/institut/berlin/ciz-berlin-berlin" + } + + def get_patients(self): self.master_patient.go() @@ -653,6 +664,8 @@ def main(self, cli_args=None): action='append', help='exclude centers') parser.add_argument('--center-exclude-regex', action='append', help='exclude centers by regex') + parser.add_argument('--additional-center', '-ac', + action='append', help='Add additional centers or doctors: "name;city;link" e.g. "Corona Impfzentren - Berlin;Berlin;/institut/berlin/ciz-berlin-berlin"') parser.add_argument( '--include-neighbor-city', '-n', action='store_true', help='include neighboring cities') parser.add_argument('--start-date', type=str, default=None, @@ -791,7 +804,7 @@ def main(self, cli_args=None): while True: log_ts() try: - for center in docto.find_centers(cities, motives): + for center in docto.find_centers(cities, motives, args.additional_center): if args.center: if center['name_with_title'] not in args.center: logging.debug("Skipping center '%s'" % diff --git a/test_browser.py b/test_browser.py index d493fbc..a568e38 100644 --- a/test_browser.py +++ b/test_browser.py @@ -115,6 +115,38 @@ def test_find_centers_de_returns_502_should_fail(tmp_path): pass +@responses.activate +def test_find_centers_de_should_return_additional_centers(tmp_path): + docto = DoctolibDE("roger.phillibert@gmail.com", + "1234", responses_dirname=tmp_path) + docto.BASEURL = "https://127.0.0.1" + + responses.add( + responses.GET, + "https://127.0.0.1/impfung-covid-19-corona/K%C3%B6ln?ref_visit_motive_ids%5B%5D=6768&ref_visit_motive_ids%5B%5D=6769&ref_visit_motive_ids%5B%5D=6936&ref_visit_motive_ids%5B%5D=6937&ref_visit_motive_ids%5B%5D=7978&ref_visit_motive_ids%5B%5D=7109&ref_visit_motive_ids%5B%5D=7110&page=1", + status=200, + body="{}" + ) + + additional_centers = [ + 'Corona Impfzentren - Köln;Köln;/institut/koeln/ciz-koeln-koeln', + 'Dr. Dre;Köln;/allgemeinmedizin/koeln/dr-dre' + ] + + centers = 0 + for result in docto.find_centers(["Köln"], None, 1, additional_centers): + splitted = additional_centers[centers].split(';') + + assert result['name_with_title'] == splitted[0] + assert result['city'] == splitted[1] + assert result['url'] == splitted[2] + + centers += 1 + + assert centers == len(additional_centers) + assert len(responses.calls) == 1 + + @responses.activate def test_get_next_page_fr_should_return_2_on_page_1(tmp_path): """ From e4d4d009ee4245d52312146c70ca30ae38ccc8b9 Mon Sep 17 00:00:00 2001 From: Marius <2452341+Dreiundzwanzig@users.noreply.github.com> Date: Sat, 17 Jul 2021 18:47:43 +0200 Subject: [PATCH 2/3] Use named capture groups Co-authored-by: gitolicious <26963495+gitolicious@users.noreply.github.com> --- doctoshotgun.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doctoshotgun.py b/doctoshotgun.py index b95f30d..d96a8c0 100755 --- a/doctoshotgun.py +++ b/doctoshotgun.py @@ -337,12 +337,12 @@ def find_centers(self, where, motives=None, page=1, additional_centers=None): if additional_centers: for additional_center in additional_centers: - splitted = additional_center.split(';') - if len(splitted) == 3: + match = re.match(r'(?P.+);(?P.+);(?P.+)', additional_center) + if match: yield { - "name_with_title": splitted[0], #"Corona Impfzentren - Berlin", - "city": splitted[1], #"Berlin", - "url": splitted[2], #"/institut/berlin/ciz-berlin-berlin" + "name_with_title": match['name'], #"Corona Impfzentren - Berlin", + "city": match['city'], #"Berlin", + "url": match['url'], #"/institut/berlin/ciz-berlin-berlin" } From 4c122d4c342f3776a34ab62faaf25248e545942b Mon Sep 17 00:00:00 2001 From: Dreiundzwanzig <2452341+Dreiundzwanzig@users.noreply.github.com> Date: Sun, 18 Jul 2021 19:24:06 +0200 Subject: [PATCH 3/3] for additional center only url is required --- README.md | 4 ++-- doctoshotgun.py | 44 +++++++++++++++++++++----------------------- test_browser.py | 32 -------------------------------- test_cli_args.py | 30 ++++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 61ec62a..7f79c43 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,8 @@ Further optional arguments: exclude centers --center-exclude-regex CENTER_EXCLUDE_REGEX exclude centers by regex ---additional-center "name;city;link", -ac "name;city;link" - Add additional centers or doctors e.g. "Corona Impfzentren - Berlin;Berlin;/institut/berlin/ciz-berlin-berlin" +--additional-center "url", -ac "url" + Add additional centers or doctors e.g. "/institut/berlin/ciz-berlin-berlin" --include-neighbor-city, -n include neighboring cities --start-date START_DATE diff --git a/doctoshotgun.py b/doctoshotgun.py index d96a8c0..6ebbb4c 100755 --- a/doctoshotgun.py +++ b/doctoshotgun.py @@ -293,7 +293,7 @@ def do_login(self, code): return True - def find_centers(self, where, motives=None, page=1, additional_centers=None): + def find_centers(self, where, motives=None, page=1): if motives is None: motives = self.vaccine_motives.keys() for city in where: @@ -335,17 +335,6 @@ def find_centers(self, where, motives=None, page=1, additional_centers=None): for center in self.find_centers(where, motives, next_page): yield center - if additional_centers: - for additional_center in additional_centers: - match = re.match(r'(?P.+);(?P.+);(?P.+)', additional_center) - if match: - yield { - "name_with_title": match['name'], #"Corona Impfzentren - Berlin", - "city": match['city'], #"Berlin", - "url": match['url'], #"/institut/berlin/ciz-berlin-berlin" - } - - def get_patients(self): self.master_patient.go() @@ -628,6 +617,16 @@ def setup_loggers(self, level): logging.root.setLevel(level) logging.root.addHandler(self.create_default_logger()) + def try_to_book_or_sleep(self, docto, center, vaccine_list, start_date, end_date, only_second, only_third, dry_run=False): + if docto.try_to_book(center, vaccine_list, start_date, end_date, only_second, only_third, dry_run): + log('') + log('💉 %s Congratulations.' % + colored('Booked!', 'green', attrs=('bold',))) + return True + + sleep(SLEEP_INTERVAL_AFTER_CENTER) + return False + def main(self, cli_args=None): colorama.init() # needed for windows @@ -665,7 +664,7 @@ def main(self, cli_args=None): parser.add_argument('--center-exclude-regex', action='append', help='exclude centers by regex') parser.add_argument('--additional-center', '-ac', - action='append', help='Add additional centers or doctors: "name;city;link" e.g. "Corona Impfzentren - Berlin;Berlin;/institut/berlin/ciz-berlin-berlin"') + action='append', help='Add additional centers or doctors: "url" e.g. "/institut/berlin/ciz-berlin-berlin"') parser.add_argument( '--include-neighbor-city', '-n', action='store_true', help='include neighboring cities') parser.add_argument('--start-date', type=str, default=None, @@ -682,8 +681,6 @@ def main(self, cli_args=None): parser.add_argument('--code', type=str, default=None, help='2FA code') args = parser.parse_args(cli_args if cli_args else sys.argv[1:]) - from types import SimpleNamespace - if args.debug: responses_dirname = tempfile.mkdtemp(prefix='woob_session_') self.setup_loggers(logging.DEBUG) @@ -804,7 +801,7 @@ def main(self, cli_args=None): while True: log_ts() try: - for center in docto.find_centers(cities, motives, args.additional_center): + for center in docto.find_centers(cities, motives): if args.center: if center['name_with_title'] not in args.center: logging.debug("Skipping center '%s'" % @@ -840,18 +837,19 @@ def main(self, cli_args=None): continue log('') - log('Center %(name_with_title)s (%(city)s):' % center) - if docto.try_to_book(center, vaccine_list, start_date, end_date, args.only_second, args.only_third, args.dry_run): - log('') - log('💉 %s Congratulations.' % - colored('Booked!', 'green', attrs=('bold',))) + if self.try_to_book_or_sleep(docto, center, vaccine_list, start_date, end_date, args.only_second, args.only_third, args.dry_run): return 0 - sleep(SLEEP_INTERVAL_AFTER_CENTER) + if args.additional_center: + for additional_center in args.additional_center: + log('') + log('Additional center %s:', additional_center.split('/')[-1]) + + if self.try_to_book_or_sleep(docto, { "url": additional_center }, vaccine_list, start_date, end_date, args.only_second, args.only_third, args.dry_run): + return 0 - log('') log('No free slots found at selected centers. Trying another round in %s sec...', SLEEP_INTERVAL_AFTER_RUN) sleep(SLEEP_INTERVAL_AFTER_RUN) except CityNotFound as e: diff --git a/test_browser.py b/test_browser.py index a568e38..d493fbc 100644 --- a/test_browser.py +++ b/test_browser.py @@ -115,38 +115,6 @@ def test_find_centers_de_returns_502_should_fail(tmp_path): pass -@responses.activate -def test_find_centers_de_should_return_additional_centers(tmp_path): - docto = DoctolibDE("roger.phillibert@gmail.com", - "1234", responses_dirname=tmp_path) - docto.BASEURL = "https://127.0.0.1" - - responses.add( - responses.GET, - "https://127.0.0.1/impfung-covid-19-corona/K%C3%B6ln?ref_visit_motive_ids%5B%5D=6768&ref_visit_motive_ids%5B%5D=6769&ref_visit_motive_ids%5B%5D=6936&ref_visit_motive_ids%5B%5D=6937&ref_visit_motive_ids%5B%5D=7978&ref_visit_motive_ids%5B%5D=7109&ref_visit_motive_ids%5B%5D=7110&page=1", - status=200, - body="{}" - ) - - additional_centers = [ - 'Corona Impfzentren - Köln;Köln;/institut/koeln/ciz-koeln-koeln', - 'Dr. Dre;Köln;/allgemeinmedizin/koeln/dr-dre' - ] - - centers = 0 - for result in docto.find_centers(["Köln"], None, 1, additional_centers): - splitted = additional_centers[centers].split(';') - - assert result['name_with_title'] == splitted[0] - assert result['city'] == splitted[1] - assert result['url'] == splitted[2] - - centers += 1 - - assert centers == len(additional_centers) - assert len(responses.calls) == 1 - - @responses.activate def test_get_next_page_fr_should_return_2_on_page_1(tmp_path): """ diff --git a/test_cli_args.py b/test_cli_args.py index 3fcb562..20b4d6e 100644 --- a/test_cli_args.py +++ b/test_cli_args.py @@ -63,6 +63,36 @@ def test_center_exclude_arg_should_filter_excluded_centers(MockDoctolibDE, tmp_p assert call_args_list.args[0]['city'] == city +@responses.activate +@patch('doctoshotgun.DoctolibDE') +def test_additional_centers_arg_should_processed(MockDoctolibDE, tmp_path): + """ + Check that additional centers are processed + """ + # prepare + mock_doctolib_de = get_mocked_doctolib(MockDoctolibDE) + + # patch find_centers to force usage of additional centers + mock_doctolib_de.find_centers.return_value = {} + + # call for every additional center as every booking is successfull + city = '' + additional_centers = [ + '/institut/koeln/ciz-koeln-koeln', + '/allgemeinmedizin/koeln/dr-dre' + ] + + for additional_center in additional_centers: + call_application(city, cli_args=['--additional-center', additional_center]) + + # assert + assert mock_doctolib_de.get_patients.called + assert mock_doctolib_de.try_to_book.called + + for i, call_args_list in enumerate(mock_doctolib_de.try_to_book.call_args_list): + assert call_args_list[0][0]['url'] == additional_centers[i] + + def get_mocked_doctolib(MockDoctolibDE): mock_doctolib_de = MagicMock(wraps=DoctolibDE) MockDoctolibDE.return_value = mock_doctolib_de