atsfill 0.0.7.dev208__tar.gz → 0.0.7.dev209__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.
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/PKG-INFO +1 -1
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/pyproject.toml +1 -1
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/pyproject.toml.orig +1 -1
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/join.py +19 -3
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/LICENSE +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/README.md +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/__init__.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/__init__.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/adp.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/adpmyjobs.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/ashby.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/bamboohr.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/breezy.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/dayforce.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/eightfold.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/greenhouse.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/gupy.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/hireology.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/hiringthing.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/hrmdirect.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/isolvedhire.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/jazzhr.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/jobaffinity.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/jobstreet.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/lever.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/manatal.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/oracle.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/paycor.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/paylocity.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/personio.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/phenom.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/pinpoint.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/recruitee.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/recruiterflow.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/rippling.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/smartrecruiters.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/successfactors.js +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/successfactors.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/taleo.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/teamtailor.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/ukg.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/welcomekit.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/wellfound.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/werecruit.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/workable.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/workatastartup.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/workday.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/wttj.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/adapters/zohorecruit.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/cli.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/core/__init__.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/core/accounts.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/core/adapter.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/core/attempts.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/core/browser.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/core/commands.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/core/conditions.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/core/countries.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/core/html.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/core/matching.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/core/otp.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/core/passwords.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/core/paths.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/core/registry.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/core/results.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/core/snapshot.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/core/telemetry.py +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/py.typed +0 -0
- {atsfill-0.0.7.dev208 → atsfill-0.0.7.dev209}/src/atsfill/registry.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: atsfill
|
|
3
|
-
Version: 0.0.7.
|
|
3
|
+
Version: 0.0.7.dev209
|
|
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
|
|
@@ -12,6 +12,7 @@ import json
|
|
|
12
12
|
import re
|
|
13
13
|
import time
|
|
14
14
|
from collections.abc import Callable
|
|
15
|
+
from contextlib import suppress
|
|
15
16
|
from dataclasses import replace
|
|
16
17
|
from typing import Any
|
|
17
18
|
from urllib.parse import urlsplit
|
|
@@ -50,6 +51,7 @@ OPENING_TIMEOUT_S = 20
|
|
|
50
51
|
CONTINUE_TIMEOUT_S = 15
|
|
51
52
|
ADVANCE_TIMEOUT_MS = 3_000 # how long a clicked card takes to move the wizard on by itself
|
|
52
53
|
INPUT_TIMEOUT_MS = 3_000 # how long a step takes to show its own input
|
|
54
|
+
ANSWER_SHOWN = 200 # of an answer that is not the expected one, in the error
|
|
53
55
|
CLICKS = 3 # a click on a step still rendering is lost, on a card or on Continue
|
|
54
56
|
LEAVE_TIMEOUT_MS = 5_000 # how long Continue takes to leave a step, per click
|
|
55
57
|
POLL_MS = 250
|
|
@@ -80,6 +82,7 @@ LACKS = {
|
|
|
80
82
|
"candidate.phoneNumber": "a phone",
|
|
81
83
|
}
|
|
82
84
|
PDF = [".pdf", "application/pdf"]
|
|
85
|
+
REMOVE_FILE = "button:has(svg[name=TrashIcon])" # a stored file's Remove file, in any language
|
|
83
86
|
|
|
84
87
|
# The profile form's controls, by its field ids.
|
|
85
88
|
TEXTS = {
|
|
@@ -136,7 +139,11 @@ class Join:
|
|
|
136
139
|
side_effects = [
|
|
137
140
|
SideEffect("sign_up", "hiring_system", "login --create creates the candidate account"),
|
|
138
141
|
SideEffect("profile", "hiring_system", "profile --answers saves the account profile"),
|
|
139
|
-
SideEffect(
|
|
142
|
+
SideEffect(
|
|
143
|
+
"upload",
|
|
144
|
+
"hiring_system",
|
|
145
|
+
"a file is stored as soon as it is chosen, replacing the one the account keeps",
|
|
146
|
+
),
|
|
140
147
|
SideEffect("submit", "employer", "the application reaches the employer"),
|
|
141
148
|
]
|
|
142
149
|
|
|
@@ -215,7 +222,10 @@ class Join:
|
|
|
215
222
|
"query": QUESTIONS,
|
|
216
223
|
}
|
|
217
224
|
answered = page.evaluate(_FETCH, [f"{GRAPHQL}?operationName=JobScreeningQuestions", body])
|
|
218
|
-
|
|
225
|
+
listed = ((answered or {}).get("data") or {}).get("jobScreeningQuestions")
|
|
226
|
+
if not listed:
|
|
227
|
+
raise RuntimeError(f"JobScreeningQuestions answered {str(answered)[:ANSWER_SHOWN]}")
|
|
228
|
+
questions = listed["questions"]
|
|
219
229
|
self._questions = sorted(questions, key=lambda q: q.get("position") or 0)
|
|
220
230
|
browser.click(page.locator("[data-testid=ApplyButton]").locator("visible=true").first)
|
|
221
231
|
page.wait_for_url("**/apply/**", timeout=STEP_TIMEOUT_MS)
|
|
@@ -383,7 +393,13 @@ class Join:
|
|
|
383
393
|
def _file(self, page: Page, field_id: str, answer: Answer | None) -> None:
|
|
384
394
|
if not isinstance(answer, str):
|
|
385
395
|
return
|
|
386
|
-
page.locator("input[type=file]").
|
|
396
|
+
upload, stored = page.locator("input[type=file]"), page.locator(REMOVE_FILE)
|
|
397
|
+
with suppress(Timeout): # the step before's Remove file stays a moment
|
|
398
|
+
upload.first.wait_for(state="attached", timeout=INPUT_TIMEOUT_MS)
|
|
399
|
+
upload.or_(stored).first.wait_for(state="attached", timeout=STEP_TIMEOUT_MS)
|
|
400
|
+
if not upload.count(): # a file the account keeps: the answer replaces it
|
|
401
|
+
stored.click(timeout=STEP_TIMEOUT_MS)
|
|
402
|
+
upload.first.set_input_files(answer)
|
|
387
403
|
# once stored, the step shows the file in place of its input
|
|
388
404
|
page.wait_for_function(
|
|
389
405
|
"() => !document.querySelector('input[type=file]')", timeout=UPLOAD_TIMEOUT_MS
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|