atsfill 0.0.4.dev67__tar.gz → 0.0.4.dev69__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 (38) hide show
  1. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/PKG-INFO +1 -1
  2. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/pyproject.toml +1 -1
  3. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/pyproject.toml.orig +1 -1
  4. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/dayforce.py +4 -0
  5. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/workable.py +9 -5
  6. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/LICENSE +0 -0
  7. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/README.md +0 -0
  8. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/__init__.py +0 -0
  9. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/__init__.py +0 -0
  10. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/ashby.py +0 -0
  11. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/bamboohr.py +0 -0
  12. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/breezy.py +0 -0
  13. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/greenhouse.py +0 -0
  14. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/hireology.py +0 -0
  15. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/jazzhr.py +0 -0
  16. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/lever.py +0 -0
  17. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/manatal.py +0 -0
  18. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/oracle.py +0 -0
  19. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/paycor.py +0 -0
  20. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/paylocity.py +0 -0
  21. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/phenom.py +0 -0
  22. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/recruitee.py +0 -0
  23. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/smartrecruiters.py +0 -0
  24. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/teamtailor.py +0 -0
  25. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/adapters/wttj.py +0 -0
  26. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/cli.py +0 -0
  27. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/core/__init__.py +0 -0
  28. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/core/accounts.py +0 -0
  29. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/core/adapter.py +0 -0
  30. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/core/attempts.py +0 -0
  31. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/core/browser.py +0 -0
  32. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/core/commands.py +0 -0
  33. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/core/matching.py +0 -0
  34. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/core/paths.py +0 -0
  35. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/core/registry.py +0 -0
  36. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/core/results.py +0 -0
  37. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/src/atsfill/py.typed +0 -0
  38. {atsfill-0.0.4.dev67 → atsfill-0.0.4.dev69}/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.dev67
3
+ Version: 0.0.4.dev69
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.dev67"
3
+ version = "0.0.4.dev69"
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.4.dev67"
3
+ version = "0.0.4.dev69"
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"
@@ -564,6 +564,8 @@ def _entry_fields(
564
564
  fields.append(Field(field_id, label, FieldType.TEXT, required, unfillable=reason))
565
565
  continue
566
566
  options = NO_YES if kind == "boolean" else None
567
+ if kind == "gpa": # a number box: it drops anything else when it loses focus
568
+ label += " (a number)"
567
569
  fields.append(Field(f"{prefix}.{key}", label, ENTRY_KINDS[kind], required, options))
568
570
  return fields
569
571
 
@@ -754,6 +756,8 @@ def _fill_control(page: Page, selector: str, field: Field, answer: Answer) -> No
754
756
  _pick(page, target, [value])
755
757
  else:
756
758
  target.fill(value)
759
+ if target.get_attribute("role") == "spinbutton":
760
+ target.blur() # a number box keeps what it takes of the text once it loses focus
757
761
 
758
762
 
759
763
  def _pick_suggestion(page: Page, target: Locator, value: str) -> None:
@@ -25,6 +25,7 @@ API = "https://apply.workable.com/api/v1/jobs/{code}/form"
25
25
  APPLIED = re.compile(r"/api/v1/jobs/[0-9A-F]{10}/apply$")
26
26
  FORM = "form[data-ui=application-form]"
27
27
  COOKIES = "[data-ui=cookie-consent-decline]"
28
+ UPLOADED = "[data-action=delete]" # an uploading file shows its name and an abort button
28
29
  FORM_TIMEOUT_MS = 30_000
29
30
  COOKIE_TIMEOUT_MS = 5_000
30
31
  OPTIONS_TIMEOUT_MS = 5_000
@@ -101,7 +102,7 @@ _READ = """form => {
101
102
  return found;
102
103
  }"""
103
104
 
104
- # What each field shows: a choice its checked option's label, a file its attached name (a photo
105
+ # What each field shows: a choice its checked option's label, a file its uploaded name (a photo
105
106
  # its preview's alt), the phone its country's dial code then its number.
106
107
  _READ_BACK = """(form, kinds) => {
107
108
  const text = el => el ? el.textContent.replace(/\\s+/g, " ").trim() : null;
@@ -114,7 +115,9 @@ _READ_BACK = """(form, kinds) => {
114
115
  if (kind === "file") {
115
116
  const zone = el.closest("[data-role=dropzone]");
116
117
  const preview = zone?.querySelector("[data-role=preview] img"); // a photo, named by its alt
117
- out[id] = preview ? preview.alt : text(zone?.querySelector("[data-id=filename]"));
118
+ const uploaded = zone?.querySelector("[data-action=delete]"); // not while it uploads
119
+ out[id] = !uploaded ? null
120
+ : preview ? preview.alt : text(zone.querySelector("[data-id=filename]"));
118
121
  } else if (kind === "phone") {
119
122
  const dial = (el.querySelector("button[aria-label]")?.getAttribute("aria-label") || "")
120
123
  .match(/\\((\\+\\d+)\\)/);
@@ -201,13 +204,14 @@ class Workable:
201
204
  field.set_input_files(values[0])
202
205
  zone = field.locator("xpath=ancestor::*[@data-role='dropzone']")
203
206
  crop = page.locator("button[data-ui=crop-image]")
207
+ # attaching another file while one uploads empties it, so each waits
208
+ uploaded = zone.locator(UPLOADED)
204
209
  with suppress(Timeout):
205
- zone.locator("[data-id=filename]").or_(crop).first.wait_for(
206
- timeout=UPLOAD_TIMEOUT_MS
207
- )
210
+ uploaded.or_(crop).first.wait_for(timeout=UPLOAD_TIMEOUT_MS)
208
211
  if crop.is_visible():
209
212
  crop.click()
210
213
  crop.wait_for(state="hidden", timeout=UPLOAD_TIMEOUT_MS)
214
+ uploaded.wait_for(timeout=UPLOAD_TIMEOUT_MS)
211
215
  case "phone": # the number's dial code picks the country
212
216
  number = field.locator("input[type=tel]")
213
217
  number.fill("")
File without changes
File without changes