atsfill 0.0.5.dev191__tar.gz → 0.0.5.dev192__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.5.dev191 → atsfill-0.0.5.dev192}/PKG-INFO +1 -1
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/pyproject.toml +1 -1
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/pyproject.toml.orig +1 -1
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/phenom.py +20 -10
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/LICENSE +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/README.md +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/__init__.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/__init__.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/adp.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/adpmyjobs.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/ashby.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/bamboohr.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/breezy.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/dayforce.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/eightfold.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/greenhouse.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/gupy.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/hireology.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/hiringthing.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/hrmdirect.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/isolvedhire.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/jazzhr.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/jobaffinity.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/jobstreet.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/join.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/lever.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/manatal.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/oracle.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/paycor.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/paylocity.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/personio.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/pinpoint.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/recruitee.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/recruiterflow.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/rippling.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/smartrecruiters.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/successfactors.js +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/successfactors.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/taleo.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/teamtailor.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/ukg.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/welcomekit.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/workable.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/workatastartup.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/workday.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/wttj.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/adapters/zohorecruit.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/cli.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/core/__init__.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/core/accounts.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/core/adapter.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/core/attempts.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/core/browser.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/core/commands.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/core/conditions.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/core/countries.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/core/html.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/core/matching.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/core/otp.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/core/passwords.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/core/paths.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/core/registry.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/core/results.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/core/snapshot.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/core/telemetry.py +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/src/atsfill/py.typed +0 -0
- {atsfill-0.0.5.dev191 → atsfill-0.0.5.dev192}/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.
|
|
3
|
+
Version: 0.0.5.dev192
|
|
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
|
|
@@ -25,7 +25,7 @@ from typing import Any
|
|
|
25
25
|
from urllib.parse import parse_qs, urlsplit
|
|
26
26
|
|
|
27
27
|
from atsfill.core import browser, matching, paths
|
|
28
|
-
from atsfill.core.adapter import Gap, NotTaken, Unsupported, known_gap
|
|
28
|
+
from atsfill.core.adapter import Blocked, Gap, NotTaken, Unsupported, known_gap
|
|
29
29
|
from atsfill.core.browser import HTTP_TIMEOUT_S, BrowserError, Locator, Overlaid, Page, Timeout
|
|
30
30
|
from atsfill.core.results import (
|
|
31
31
|
AllOf,
|
|
@@ -238,22 +238,24 @@ _FETCH = """async url => {
|
|
|
238
238
|
return response.json();
|
|
239
239
|
}"""
|
|
240
240
|
|
|
241
|
-
# Posts each body to `/applySubmit`, `most` at a time; null for one that fails.
|
|
241
|
+
# Posts each body to `/applySubmit`, `most` at a time; null for one that fails. With the
|
|
242
|
+
# answers, whether one was refused (403), after which none is posted.
|
|
242
243
|
_POSTS = """async ([bodies, token, most]) => {
|
|
243
244
|
const out = bodies.map(() => null);
|
|
244
|
-
let next = 0;
|
|
245
|
+
let next = 0, refused = false;
|
|
245
246
|
const post = async () => {
|
|
246
|
-
for (let i = next++; i < bodies.length; i = next++) {
|
|
247
|
+
for (let i = next++; i < bodies.length && !refused; i = next++) {
|
|
247
248
|
try {
|
|
248
249
|
const response = await fetch('/applySubmit', {method: 'POST',
|
|
249
250
|
body: JSON.stringify(bodies[i]),
|
|
250
251
|
headers: {'Content-Type': 'application/json', 'x-csrf-token': token}});
|
|
251
252
|
if (response.ok) out[i] = await response.json();
|
|
253
|
+
refused ||= response.status === 403;
|
|
252
254
|
} catch {}
|
|
253
255
|
}
|
|
254
256
|
};
|
|
255
257
|
await Promise.all(Array.from({length: most}, post));
|
|
256
|
-
return out;
|
|
258
|
+
return [out, refused];
|
|
257
259
|
}"""
|
|
258
260
|
|
|
259
261
|
|
|
@@ -350,14 +352,22 @@ class Phenom:
|
|
|
350
352
|
except ValueError as e:
|
|
351
353
|
raise RuntimeError(str(e)) from None
|
|
352
354
|
token = get(ph, "sessionParams.csrfToken") or ""
|
|
355
|
+
|
|
356
|
+
refused = False
|
|
357
|
+
|
|
358
|
+
def posts(bodies: list[dict[str, Any]]) -> list[Any]:
|
|
359
|
+
nonlocal refused
|
|
360
|
+
out, now = page.evaluate(_POSTS, [bodies, token, LOADERS_AT_ONCE])
|
|
361
|
+
refused |= now
|
|
362
|
+
return out
|
|
363
|
+
|
|
353
364
|
try:
|
|
354
|
-
fields, raws = _read(
|
|
355
|
-
ph,
|
|
356
|
-
lambda url: page.evaluate(_FETCH, url),
|
|
357
|
-
lambda bodies: page.evaluate(_POSTS, [bodies, token, LOADERS_AT_ONCE]),
|
|
358
|
-
)
|
|
365
|
+
fields, raws = _read(ph, lambda url: page.evaluate(_FETCH, url), posts)
|
|
359
366
|
except (BrowserError, ValueError, KeyError) as e:
|
|
360
367
|
raise RuntimeError(f"no configuration for the wizard ({e})") from None
|
|
368
|
+
finally: # raised once the read ends, so the answers that came are kept
|
|
369
|
+
if refused:
|
|
370
|
+
raise Blocked("rate_limited: the careers site answers /applySubmit 403")
|
|
361
371
|
self._raws = {raw["id"]: raw for raw in raws}
|
|
362
372
|
return fields
|
|
363
373
|
|
|
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
|