atsfill 0.0.8.dev224__tar.gz → 0.0.8.dev226__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/PKG-INFO +1 -1
  2. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/pyproject.toml +1 -1
  3. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/pyproject.toml.orig +1 -1
  4. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/eightfold.py +37 -3
  5. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/LICENSE +0 -0
  6. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/README.md +0 -0
  7. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/__init__.py +0 -0
  8. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/__init__.py +0 -0
  9. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/adp.py +0 -0
  10. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/adpmyjobs.py +0 -0
  11. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/ashby.py +0 -0
  12. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/bamboohr.py +0 -0
  13. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/breezy.py +0 -0
  14. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/dayforce.py +0 -0
  15. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/greenhouse.py +0 -0
  16. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/gupy.py +0 -0
  17. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/hireology.py +0 -0
  18. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/hiringthing.py +0 -0
  19. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/hrmdirect.py +0 -0
  20. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/isolvedhire.py +0 -0
  21. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/jazzhr.py +0 -0
  22. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/jobaffinity.py +0 -0
  23. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/jobstreet.py +0 -0
  24. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/join.py +0 -0
  25. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/lever.py +0 -0
  26. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/manatal.py +0 -0
  27. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/oracle.py +0 -0
  28. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/paycor.py +0 -0
  29. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/paylocity.py +0 -0
  30. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/personio.py +0 -0
  31. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/phenom.py +0 -0
  32. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/pinpoint.py +0 -0
  33. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/recruitee.py +0 -0
  34. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/recruiterflow.py +0 -0
  35. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/rippling.py +0 -0
  36. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/smartrecruiters.py +0 -0
  37. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/successfactors.js +0 -0
  38. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/successfactors.py +0 -0
  39. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/taleo.py +0 -0
  40. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/teamtailor.py +0 -0
  41. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/ukg.py +0 -0
  42. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/welcomekit.py +0 -0
  43. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/wellfound.py +0 -0
  44. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/werecruit.py +0 -0
  45. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/workable.py +0 -0
  46. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/workatastartup.py +0 -0
  47. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/workday.py +0 -0
  48. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/wttj.py +0 -0
  49. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/adapters/zohorecruit.py +0 -0
  50. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/cli.py +0 -0
  51. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/core/__init__.py +0 -0
  52. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/core/accounts.py +0 -0
  53. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/core/adapter.py +0 -0
  54. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/core/attempts.py +0 -0
  55. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/core/browser.py +0 -0
  56. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/core/commands.py +0 -0
  57. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/core/conditions.py +0 -0
  58. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/core/countries.py +0 -0
  59. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/core/html.py +0 -0
  60. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/core/matching.py +0 -0
  61. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/core/otp.py +0 -0
  62. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/core/passwords.py +0 -0
  63. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/core/paths.py +0 -0
  64. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/core/registry.py +0 -0
  65. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/core/results.py +0 -0
  66. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/core/snapshot.py +0 -0
  67. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/core/telemetry.py +0 -0
  68. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/py.typed +0 -0
  69. {atsfill-0.0.8.dev224 → atsfill-0.0.8.dev226}/src/atsfill/registry.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atsfill
3
- Version: 0.0.8.dev224
3
+ Version: 0.0.8.dev226
4
4
  Summary: Read, dry-run and submit job application forms on hiring systems: Ashby, Greenhouse, Teamtailor, Welcome to the Jungle.
5
5
  Keywords: job-applications,ats,ashby,greenhouse,teamtailor,ai-agents,cli
6
6
  Author: shbernal
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "atsfill"
3
- version = "0.0.8.dev224"
3
+ version = "0.0.8.dev226"
4
4
  description = "Read, dry-run and submit job application forms on hiring systems: Ashby, Greenhouse, Teamtailor, Welcome to the Jungle."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "atsfill"
3
- version = "0.0.8.dev224"
3
+ version = "0.0.8.dev226"
4
4
  description = "Read, dry-run and submit job application forms on hiring systems: Ashby, Greenhouse, Teamtailor, Welcome to the Jungle."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -36,6 +36,9 @@ PICKER = "[data-testid=picker-dialog]"
36
36
  FORM_TIMEOUT_MS = 30_000
37
37
  SHOWN_TIMEOUT_MS = 2_000
38
38
  OPTIONS_TIMEOUT_MS = 5_000
39
+ FOCUS_TIMEOUT_MS = 500
40
+ LIST_CLOSE_TIMEOUT_MS = 2_000 # a list can close late on a page still loading
41
+ PICK_CLICKS = 3 # the list can lose a click on its option
39
42
  UPLOAD_TIMEOUT_MS = 30_000
40
43
  SUBMIT_TIMEOUT_MS = 90_000
41
44
  PARSE_SETTLE_MS = 2_000
@@ -479,18 +482,49 @@ def _pick(page: Page, combo, value: str, exact: bool) -> None:
479
482
  if not option.count():
480
483
  page.keyboard.press("Escape")
481
484
  raise NotTaken(f"no option {value!r}")
482
- option.click()
485
+ _choose(page, combo, option)
486
+
487
+
488
+ def _choose(page: Page, combo, option) -> None:
489
+ """Clicks a combobox's option and waits for its list to close and the combobox to take the
490
+ focus back: typed before that, the next answer lands in the combobox. A list still open over
491
+ an unchanged combobox lost the click, so the option is clicked again; clicked once it took,
492
+ it would clear. A list that stays open is dismissed, as it lies over the next control, for
493
+ the read-back to catch what the combobox holds."""
494
+ listbox = page.locator(f"[id={json.dumps(combo.get_attribute('aria-controls'))}]")
495
+ held = combo.input_value()
496
+ for _ in range(PICK_CLICKS):
497
+ with suppress(Timeout):
498
+ option.click(timeout=LIST_CLOSE_TIMEOUT_MS)
499
+ listbox.wait_for(state="hidden", timeout=LIST_CLOSE_TIMEOUT_MS)
500
+ if listbox.is_hidden():
501
+ with suppress(Timeout):
502
+ page.wait_for_function(
503
+ "el => document.activeElement === el",
504
+ arg=combo.element_handle(),
505
+ timeout=FOCUS_TIMEOUT_MS,
506
+ )
507
+ return
508
+ if combo.input_value() != held:
509
+ break
510
+ combo.press("Escape")
483
511
 
484
512
 
485
513
  def _phone(page: Page, box, value: str) -> None:
486
514
  """Picks the country code the number starts with, the longest that fits, then types the
487
- rest of the number. Where several countries share it, its main country."""
515
+ rest of the number. Where several countries share it, its main country. One already showing
516
+ a code that fits stays: clicking the selected option clears it."""
488
517
  digits = re.sub(r"\D", "", value)
489
518
  combo = box.locator("input[role=combobox]").first
490
519
  number = box.locator("input:not([role=combobox])").first
491
520
  if not value.startswith("+"):
492
521
  number.fill(value)
493
522
  return
523
+ if (held := DIAL.search(combo.input_value())) and digits.startswith(
524
+ code := held[1].replace(" ", "")
525
+ ):
526
+ number.fill(digits[len(code) :])
527
+ return
494
528
  _click(combo)
495
529
  options = page.locator(f"[id={json.dumps(combo.get_attribute('aria-controls'))}] [role=option]")
496
530
  options.first.wait_for(timeout=OPTIONS_TIMEOUT_MS)
@@ -504,7 +538,7 @@ def _phone(page: Page, box, value: str) -> None:
504
538
  number.fill(digits)
505
539
  return
506
540
  _, _, i, code = max(fits)
507
- options.nth(-i).click()
541
+ _choose(page, combo, options.nth(-i))
508
542
  number.fill(digits[len(code) :])
509
543
 
510
544
 
File without changes
File without changes