atsfill 0.0.5.dev193__tar.gz → 0.0.5.dev194__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 (68) hide show
  1. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/PKG-INFO +1 -1
  2. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/pyproject.toml +1 -1
  3. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/pyproject.toml.orig +1 -1
  4. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/teamtailor.py +6 -2
  5. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/core/adapter.py +9 -1
  6. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/core/commands.py +3 -0
  7. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/LICENSE +0 -0
  8. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/README.md +0 -0
  9. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/__init__.py +0 -0
  10. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/__init__.py +0 -0
  11. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/adp.py +0 -0
  12. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/adpmyjobs.py +0 -0
  13. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/ashby.py +0 -0
  14. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/bamboohr.py +0 -0
  15. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/breezy.py +0 -0
  16. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/dayforce.py +0 -0
  17. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/eightfold.py +0 -0
  18. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/greenhouse.py +0 -0
  19. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/gupy.py +0 -0
  20. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/hireology.py +0 -0
  21. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/hiringthing.py +0 -0
  22. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/hrmdirect.py +0 -0
  23. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/isolvedhire.py +0 -0
  24. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/jazzhr.py +0 -0
  25. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/jobaffinity.py +0 -0
  26. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/jobstreet.py +0 -0
  27. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/join.py +0 -0
  28. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/lever.py +0 -0
  29. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/manatal.py +0 -0
  30. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/oracle.py +0 -0
  31. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/paycor.py +0 -0
  32. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/paylocity.py +0 -0
  33. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/personio.py +0 -0
  34. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/phenom.py +0 -0
  35. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/pinpoint.py +0 -0
  36. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/recruitee.py +0 -0
  37. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/recruiterflow.py +0 -0
  38. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/rippling.py +0 -0
  39. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/smartrecruiters.py +0 -0
  40. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/successfactors.js +0 -0
  41. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/successfactors.py +0 -0
  42. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/taleo.py +0 -0
  43. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/ukg.py +0 -0
  44. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/welcomekit.py +0 -0
  45. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/werecruit.py +0 -0
  46. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/workable.py +0 -0
  47. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/workatastartup.py +0 -0
  48. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/workday.py +0 -0
  49. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/wttj.py +0 -0
  50. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/adapters/zohorecruit.py +0 -0
  51. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/cli.py +0 -0
  52. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/core/__init__.py +0 -0
  53. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/core/accounts.py +0 -0
  54. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/core/attempts.py +0 -0
  55. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/core/browser.py +0 -0
  56. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/core/conditions.py +0 -0
  57. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/core/countries.py +0 -0
  58. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/core/html.py +0 -0
  59. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/core/matching.py +0 -0
  60. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/core/otp.py +0 -0
  61. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/core/passwords.py +0 -0
  62. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/core/paths.py +0 -0
  63. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/core/registry.py +0 -0
  64. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/core/results.py +0 -0
  65. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/core/snapshot.py +0 -0
  66. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/core/telemetry.py +0 -0
  67. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/src/atsfill/py.typed +0 -0
  68. {atsfill-0.0.5.dev193 → atsfill-0.0.5.dev194}/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.dev193
3
+ Version: 0.0.5.dev194
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.dev193"
3
+ version = "0.0.5.dev194"
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.dev193"
3
+ version = "0.0.5.dev194"
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"
@@ -16,7 +16,7 @@ from typing import Any
16
16
  from urllib.parse import urlsplit
17
17
 
18
18
  from atsfill.core import browser, html, matching
19
- from atsfill.core.adapter import Gap, NotTaken, Unsupported, known_gap
19
+ from atsfill.core.adapter import Gap, Held, NotTaken, Unsupported, known_gap
20
20
  from atsfill.core.browser import HTTP_TIMEOUT_S, Locator, Page, Response
21
21
  from atsfill.core.html import Node
22
22
  from atsfill.core.results import Answer, Field, FieldType, Is, SideEffect
@@ -31,6 +31,8 @@ VIDEO = "[data-controller~='questions--tt-video-answer']"
31
31
  UPLOAD = "forms--inputs--upload" # a drop zone's controller
32
32
  CAREER_SITE = "a[href*='//www.teamtailor.com'], script[src*='teamtailor-cdn.com/assets/careersite']"
33
33
  JOB = re.compile(r"/jobs/(\d+)[^/]*")
34
+ EMAIL_LINK = "/applications/email_verification_needed"
35
+ SENT = re.compile(rf"/applications/[^/]+/thanks|{EMAIL_LINK}")
34
36
  SUBMIT_TIMEOUT_MS = 90_000
35
37
  FOLLOW_UP_TIMEOUT_MS = 1_000
36
38
  UPLOAD_TIMEOUT_S = 60
@@ -235,7 +237,9 @@ class Teamtailor:
235
237
 
236
238
  def submit(self, page: Page) -> str | None:
237
239
  browser.click(page.locator(f"{FORM} [type=submit]"))
238
- page.wait_for_url(re.compile(r"/applications/[^/]+/thanks"), timeout=SUBMIT_TIMEOUT_MS)
240
+ page.wait_for_url(SENT, timeout=SUBMIT_TIMEOUT_MS, wait_until="domcontentloaded")
241
+ if page.url.split("?")[0].endswith(EMAIL_LINK):
242
+ raise Held("click the link emailed to the applicant to complete it")
239
243
  return page.url
240
244
 
241
245
  def _fill(self, form: Locator, field_id: str, answer: Answer) -> None:
@@ -15,6 +15,13 @@ class Blocked(Exception):
15
15
  """
16
16
 
17
17
 
18
+ class Held(Exception):
19
+ """Raised by `submit`: the system has the application but holds it until the applicant
20
+ acts, e.g. clicks an emailed link. The send counts as `submitted`, the message is the
21
+ reason's detail (ADR 0006).
22
+ """
23
+
24
+
18
25
  class SignedOut(Blocked):
19
26
  """The system's page asks to sign in although `signed_in` found the session holding: the
20
27
  system ended it its own way. The core signs in again and reads once more (ADR 0015).
@@ -94,7 +101,8 @@ class Adapter(Protocol):
94
101
  def submit(self, page: Page) -> str | None:
95
102
  """Clicks submit; returns the evidence (a URL or a network response), None without.
96
103
 
97
- Raises `Blocked` when the system refuses the send.
104
+ Raises `Blocked` when the system refuses the send, `Held` when it has the application but
105
+ holds it until the applicant acts.
98
106
  """
99
107
  ...
100
108
 
@@ -25,6 +25,7 @@ from atsfill.core.adapter import (
25
25
  Blocked,
26
26
  CreatesAccount,
27
27
  CreatesAccountByOtp,
28
+ Held,
28
29
  KeepsProfile,
29
30
  ListsShownFields,
30
31
  NeedsAccount,
@@ -503,6 +504,8 @@ def _on_page(
503
504
  result.status = Status.SUBMITTED
504
505
  else:
505
506
  result.status, result.reason = Status.UNKNOWN, "no_evidence"
507
+ except Held as e:
508
+ result.status, result.evidence, result.reason = Status.SUBMITTED, page.url, f"held: {e}"
506
509
  except Blocked as e:
507
510
  result.status, result.reason = Status.BLOCKED, str(e)
508
511
  except Exception as e: # after the click, nothing tells a failed send from a sent one
File without changes
File without changes