atsfill 0.0.4.dev37__tar.gz → 0.0.4.dev39__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 (37) hide show
  1. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/PKG-INFO +1 -1
  2. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/pyproject.toml +2 -2
  3. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/pyproject.toml.orig +2 -2
  4. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/bamboohr.py +5 -2
  5. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/dayforce.py +5 -3
  6. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/paylocity.py +2 -1
  7. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/recruitee.py +1 -1
  8. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/LICENSE +0 -0
  9. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/README.md +0 -0
  10. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/__init__.py +0 -0
  11. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/__init__.py +0 -0
  12. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/ashby.py +0 -0
  13. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/breezy.py +0 -0
  14. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/greenhouse.py +0 -0
  15. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/hireology.py +0 -0
  16. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/jazzhr.py +0 -0
  17. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/lever.py +0 -0
  18. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/manatal.py +0 -0
  19. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/oracle.py +0 -0
  20. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/paycor.py +0 -0
  21. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/smartrecruiters.py +0 -0
  22. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/teamtailor.py +0 -0
  23. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/workable.py +0 -0
  24. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/adapters/wttj.py +0 -0
  25. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/cli.py +0 -0
  26. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/core/__init__.py +0 -0
  27. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/core/accounts.py +0 -0
  28. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/core/adapter.py +0 -0
  29. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/core/attempts.py +0 -0
  30. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/core/browser.py +0 -0
  31. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/core/commands.py +0 -0
  32. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/core/matching.py +0 -0
  33. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/core/paths.py +0 -0
  34. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/core/registry.py +0 -0
  35. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/core/results.py +0 -0
  36. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/py.typed +0 -0
  37. {atsfill-0.0.4.dev37 → atsfill-0.0.4.dev39}/src/atsfill/registry.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atsfill
3
- Version: 0.0.4.dev37
3
+ Version: 0.0.4.dev39
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.4.dev37"
3
+ version = "0.0.4.dev39"
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"
@@ -68,7 +68,7 @@ select = [
68
68
  [tool.pytest.ini_options]
69
69
  testpaths = ["tests"]
70
70
  markers = ["live: drives a real hiring system page with data from .env and .tmp/; opt-in with -m live"]
71
- addopts = "-m 'not live' -n 8"
71
+ addopts = "-m 'not live' -n auto --dist loadscope"
72
72
 
73
73
  [[tool.uv.index]]
74
74
  name = "testpypi"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "atsfill"
3
- version = "0.0.4.dev37"
3
+ version = "0.0.4.dev39"
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"
@@ -55,7 +55,7 @@ testpaths = ["tests"]
55
55
  markers = [
56
56
  "live: drives a real hiring system page with data from .env and .tmp/; opt-in with -m live",
57
57
  ]
58
- addopts = "-m 'not live' -n 8"
58
+ addopts = "-m 'not live' -n auto --dist loadscope"
59
59
 
60
60
  [[tool.uv.index]]
61
61
  name = "testpypi"
@@ -30,6 +30,7 @@ CHALLENGE = "iframe[src*='recaptcha/api2/bframe']"
30
30
  FORM_TIMEOUT_MS = 30_000
31
31
  UPLOAD_TIMEOUT_MS = 30_000
32
32
  SHOWN_TIMEOUT_MS = 2_000
33
+ STATES_TIMEOUT_MS = 15_000 # for the state's list a country change refetches
33
34
  SETTLE_MS = 300 # for React to render what a response brought
34
35
  CAPTCHA_TIMEOUT_MS = 20_000
35
36
  SUBMIT_TIMEOUT_MS = 90_000
@@ -215,12 +216,14 @@ class BambooHR:
215
216
  case "file":
216
217
  _attach(form, str(name), answer)
217
218
  case "select" if field_id == "countryId":
219
+ if _shown(control) == value:
220
+ continue # the state's list stays
218
221
  with (
219
222
  suppress(Timeout),
220
223
  page.expect_response(
221
- lambda r: "/ajax/get_states" in r.url, timeout=SHOWN_TIMEOUT_MS
224
+ lambda r: "/ajax/get_states" in r.url, timeout=STATES_TIMEOUT_MS
222
225
  ),
223
- ): # the state's list follows the country, none when it stays
226
+ ): # the state's list follows the country
224
227
  _pick(page, control, value)
225
228
  page.wait_for_timeout(SETTLE_MS)
226
229
  case "select":
@@ -35,6 +35,8 @@ STEP_TIMEOUT_MS = 20_000
35
35
  SUBMIT_TIMEOUT_MS = 90_000
36
36
  UPDATE_TRIES = 3
37
37
  FOLLOWUP_TIMEOUT_MS = 3_000
38
+ DIAL_TIMEOUT_MS = 2_000 # per dial code tried, longest first
39
+ OPTIONS_TIMEOUT_MS = 5_000 # for a dropdown's options to render
38
40
  NEXT_TRIES = 3
39
41
  STEP_POLLS = 16 # of POLL_MS, before Next is clicked again
40
42
  POLL_MS = 500
@@ -700,7 +702,7 @@ def _pick(page: Page, target: Locator, texts: list[str]) -> bool:
700
702
  target.fill("")
701
703
  target.press_sequentially(text[:40], delay=10)
702
704
  with suppress(Timeout):
703
- options.first.wait_for(timeout=5_000)
705
+ options.first.wait_for(timeout=OPTIONS_TIMEOUT_MS)
704
706
  page.evaluate(_REVEAL, [target.get_attribute("id"), [text]])
705
707
  if _click_option(page, target, _plain(text).casefold().__eq__):
706
708
  return True
@@ -750,7 +752,7 @@ def _pick_suggestion(page: Page, target: Locator, value: str) -> None:
750
752
  _open(target)
751
753
  target.press_sequentially(value[:40], delay=10)
752
754
  with suppress(Timeout):
753
- page.locator(_option(target)).first.wait_for(timeout=5_000)
755
+ page.locator(_option(target)).first.wait_for(timeout=OPTIONS_TIMEOUT_MS)
754
756
  if not _click_option(page, target, lambda shown: shown.startswith(_plain(value).casefold())):
755
757
  target.press("Escape")
756
758
 
@@ -769,7 +771,7 @@ def _fill_phone(page: Page, selector: str, value: str) -> None:
769
771
  dial.press_sequentially(f"+{code}", delay=10)
770
772
  options = page.locator(_option(dial)).filter(has_text=re.compile(rf"\+{code}(\s|$)"))
771
773
  with suppress(Timeout):
772
- options.first.wait_for(timeout=2_000)
774
+ options.first.wait_for(timeout=DIAL_TIMEOUT_MS)
773
775
  if options.count():
774
776
  main = options.filter(has_text=MAIN_COUNTRY.get(code, "\0"))
775
777
  (main if main.count() else options).first.click()
@@ -31,6 +31,7 @@ SUBMIT = "[data-automation-id=btnSubmit]"
31
31
  STEP = "#appDetailDiv .type-bold.float-right" # "Step 2 of 5"
32
32
  FORCED_RESUME = ".modal.in #btn-forceResume"
33
33
  FORM_TIMEOUT_MS = 30_000
34
+ COOKIE_TIMEOUT_MS = 5_000
34
35
  STEP_TIMEOUT_MS = 20_000
35
36
  UPLOAD_TIMEOUT_MS = 30_000
36
37
  SUBMIT_TIMEOUT_MS = 90_000
@@ -473,7 +474,7 @@ def _present(page: Page, control: Control) -> bool:
473
474
  def _dismiss_cookies(page: Page) -> None:
474
475
  close = page.locator("#onetrust-banner-sdk .onetrust-close-btn-handler")
475
476
  with suppress(Timeout):
476
- close.first.wait_for(state="visible", timeout=5_000)
477
+ close.first.wait_for(state="visible", timeout=COOKIE_TIMEOUT_MS)
477
478
  close.first.click()
478
479
 
479
480
 
@@ -63,7 +63,7 @@ _READ = """form => {
63
63
  field = {id: name, required: false, options: [], accept: null,
64
64
  maxLength: el.maxLength > 0 ? el.maxLength : null};
65
65
  if (el.type === "radio") field.kind = "radio";
66
- else if (el.type === "checkbox" && /\.(flag|consent)$/.test(name)) {
66
+ else if (el.type === "checkbox" && /\\.(flag|consent)$/.test(name)) {
67
67
  field.kind = "legal";
68
68
  field.label = text(form.querySelector(`[id="${el.getAttribute("aria-labelledby")}"]`));
69
69
  } else if (el.type === "checkbox") field.kind = "checkboxes";
File without changes
File without changes