atsfill 0.0.5.dev164__tar.gz → 0.0.5.dev165__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 (65) hide show
  1. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/PKG-INFO +1 -1
  2. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/pyproject.toml +1 -1
  3. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/pyproject.toml.orig +1 -1
  4. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/isolvedhire.py +6 -1
  5. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/oracle.py +7 -1
  6. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/workatastartup.py +8 -2
  7. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/LICENSE +0 -0
  8. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/README.md +0 -0
  9. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/__init__.py +0 -0
  10. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/__init__.py +0 -0
  11. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/adpmyjobs.py +0 -0
  12. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/ashby.py +0 -0
  13. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/bamboohr.py +0 -0
  14. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/breezy.py +0 -0
  15. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/dayforce.py +0 -0
  16. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/eightfold.py +0 -0
  17. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/greenhouse.py +0 -0
  18. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/gupy.py +0 -0
  19. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/hireology.py +0 -0
  20. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/hiringthing.py +0 -0
  21. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/hrmdirect.py +0 -0
  22. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/jazzhr.py +0 -0
  23. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/jobaffinity.py +0 -0
  24. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/jobstreet.py +0 -0
  25. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/join.py +0 -0
  26. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/lever.py +0 -0
  27. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/manatal.py +0 -0
  28. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/paycor.py +0 -0
  29. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/paylocity.py +0 -0
  30. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/personio.py +0 -0
  31. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/phenom.py +0 -0
  32. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/pinpoint.py +0 -0
  33. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/recruitee.py +0 -0
  34. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/recruiterflow.py +0 -0
  35. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/rippling.py +0 -0
  36. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/smartrecruiters.py +0 -0
  37. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/successfactors.js +0 -0
  38. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/successfactors.py +0 -0
  39. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/taleo.py +0 -0
  40. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/teamtailor.py +0 -0
  41. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/ukg.py +0 -0
  42. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/welcomekit.py +0 -0
  43. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/workable.py +0 -0
  44. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/workday.py +0 -0
  45. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/wttj.py +0 -0
  46. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/adapters/zohorecruit.py +0 -0
  47. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/cli.py +0 -0
  48. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/core/__init__.py +0 -0
  49. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/core/accounts.py +0 -0
  50. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/core/adapter.py +0 -0
  51. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/core/attempts.py +0 -0
  52. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/core/browser.py +0 -0
  53. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/core/commands.py +0 -0
  54. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/core/conditions.py +0 -0
  55. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/core/html.py +0 -0
  56. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/core/matching.py +0 -0
  57. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/core/otp.py +0 -0
  58. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/core/passwords.py +0 -0
  59. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/core/paths.py +0 -0
  60. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/core/registry.py +0 -0
  61. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/core/results.py +0 -0
  62. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/core/snapshot.py +0 -0
  63. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/core/telemetry.py +0 -0
  64. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/py.typed +0 -0
  65. {atsfill-0.0.5.dev164 → atsfill-0.0.5.dev165}/src/atsfill/registry.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atsfill
3
- Version: 0.0.5.dev164
3
+ Version: 0.0.5.dev165
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.5.dev164"
3
+ version = "0.0.5.dev165"
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.5.dev164"
3
+ version = "0.0.5.dev165"
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"
@@ -237,7 +237,12 @@ class IsolvedHire:
237
237
  )
238
238
  if country not in _texts(form.locator("#country_id option")):
239
239
  raise Refused(f"sign_up_details: no country {country} in the sign-up's list")
240
- form.locator("#country_id").select_option(label=country)
240
+ countries = form.locator("#country_id") # its states reload once it is picked, the old
241
+ # ones still there meanwhile: the preset country is left as it is
242
+ if countries.locator("option:checked").inner_text().strip() != country:
243
+ stale = form.locator("#state_id option").first.element_handle()
244
+ countries.select_option(label=country)
245
+ page.wait_for_function("o => !o.isConnected", arg=stale, timeout=LIST_TIMEOUT_MS)
241
246
  named = re.compile(rf"^\s*{re.escape(details.state or '')}\s*$", re.I)
242
247
  state = form.locator("#state_id option", has_text=named)
243
248
  try:
@@ -36,6 +36,7 @@ OTP_PAGE = ".apply-flow-pin-code-required"
36
36
  PIN = f"{OTP_PAGE} .pin-code-input__input" # one per digit
37
37
  VERIFY = f"{OTP_PAGE} button[type=submit]"
38
38
  CAPTCHA = "iframe[src*='hcaptcha.com']"
39
+ BANNER = ".cookie-consent-modal" # some tenants' cookie banner, over the email step and the form
39
40
  MANUALLY = ".quick-apply-flow__manually--link:visible" # past an offer to import a profile
40
41
  ATTACHED = ".attachment-upload-button__download, .misc-attachment-list__item-name"
41
42
  REMOVE = ".attachment-upload-button__filled .attachment-upload-button__bottom-button"
@@ -46,6 +47,7 @@ PAGE_TIMEOUT_MS = 15_000 # per click on a page number
46
47
  SUBMIT_TIMEOUT_MS = 90_000
47
48
  KEY_DELAY_MS = 20
48
49
  SETTLE_MS = 1_500
50
+ BANNER_TIMEOUT_MS = 10_000
49
51
  UPLOAD_TIMEOUT_MS = 30_000
50
52
  CHECKBOX = ["Yes", "No"]
51
53
  KINDS = {
@@ -241,7 +243,7 @@ _READ_BACK = """(ids) => {
241
243
  class Oracle:
242
244
  name = "oracle"
243
245
  known_gaps = {"entries": Gap.RENDERING}
244
- filters = ["##.cookie-consent-modal"] # some tenants' banner, over the email step and the form
246
+ filters = [f"##{BANNER}"]
245
247
  side_effects = [
246
248
  SideEffect(
247
249
  "load",
@@ -350,6 +352,10 @@ class Oracle:
350
352
  except browser.Timeout:
351
353
  raise Unsupported("posting_gone: no email step on the apply page") from None
352
354
  _settle(page)
355
+ # the filter hides the banner once the blocker gets to it, a second or more late; one
356
+ # still shown is named by the click it covers
357
+ with suppress(browser.Timeout):
358
+ page.locator(BANNER).first.wait_for(state="hidden", timeout=BANNER_TIMEOUT_MS)
353
359
  terms = page.locator("legal-disclaimer .apply-flow-input-checkbox__label")
354
360
  return _text(terms.first.inner_text()) if terms.count() else None
355
361
 
@@ -332,8 +332,8 @@ class WorkAtAStartup:
332
332
  return {k: v for k, v in values.items() if v not in (None, "", [])}
333
333
 
334
334
  def save(self, page: Page) -> None:
335
- """Save & next posts the step to `/application/next`; a field the page refuses shows
336
- its error and posts nothing."""
335
+ """Save & next posts the step to `/application/next`, then goes to the step it answers;
336
+ a field the page refuses shows its error and posts nothing."""
337
337
  form = page.locator(WIZARD).last
338
338
  try:
339
339
  with page.expect_response(
@@ -360,6 +360,12 @@ class WorkAtAStartup:
360
360
  raise Refused(f"step_refused: {self._step}: {said or response.text()[:200]}")
361
361
  if not response.ok:
362
362
  raise RuntimeError(f"POST /application/next answered {response.status}")
363
+ try:
364
+ to = response.json().get("redirect_to")
365
+ except ValueError:
366
+ to = None
367
+ if to: # the page goes there next: a navigation started meanwhile would be cut short
368
+ page.wait_for_url(f"**{urlsplit(to).path}", timeout=SAVE_TIMEOUT_MS)
363
369
 
364
370
 
365
371
  @dataclass(frozen=True)
File without changes
File without changes