atsfill 0.0.5.dev188__tar.gz → 0.0.5.dev189__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 (66) hide show
  1. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/PKG-INFO +1 -1
  2. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/pyproject.toml +1 -1
  3. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/pyproject.toml.orig +1 -1
  4. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/phenom.py +24 -3
  5. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/LICENSE +0 -0
  6. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/README.md +0 -0
  7. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/__init__.py +0 -0
  8. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/__init__.py +0 -0
  9. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/adp.py +0 -0
  10. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/adpmyjobs.py +0 -0
  11. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/ashby.py +0 -0
  12. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/bamboohr.py +0 -0
  13. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/breezy.py +0 -0
  14. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/dayforce.py +0 -0
  15. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/eightfold.py +0 -0
  16. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/greenhouse.py +0 -0
  17. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/gupy.py +0 -0
  18. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/hireology.py +0 -0
  19. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/hiringthing.py +0 -0
  20. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/hrmdirect.py +0 -0
  21. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/isolvedhire.py +0 -0
  22. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/jazzhr.py +0 -0
  23. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/jobaffinity.py +0 -0
  24. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/jobstreet.py +0 -0
  25. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/join.py +0 -0
  26. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/lever.py +0 -0
  27. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/manatal.py +0 -0
  28. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/oracle.py +0 -0
  29. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/paycor.py +0 -0
  30. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/paylocity.py +0 -0
  31. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/personio.py +0 -0
  32. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/pinpoint.py +0 -0
  33. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/recruitee.py +0 -0
  34. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/recruiterflow.py +0 -0
  35. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/rippling.py +0 -0
  36. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/smartrecruiters.py +0 -0
  37. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/successfactors.js +0 -0
  38. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/successfactors.py +0 -0
  39. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/taleo.py +0 -0
  40. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/teamtailor.py +0 -0
  41. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/ukg.py +0 -0
  42. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/welcomekit.py +0 -0
  43. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/workable.py +0 -0
  44. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/workatastartup.py +0 -0
  45. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/workday.py +0 -0
  46. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/wttj.py +0 -0
  47. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/adapters/zohorecruit.py +0 -0
  48. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/cli.py +0 -0
  49. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/core/__init__.py +0 -0
  50. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/core/accounts.py +0 -0
  51. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/core/adapter.py +0 -0
  52. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/core/attempts.py +0 -0
  53. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/core/browser.py +0 -0
  54. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/core/commands.py +0 -0
  55. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/core/conditions.py +0 -0
  56. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/core/html.py +0 -0
  57. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/core/matching.py +0 -0
  58. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/core/otp.py +0 -0
  59. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/core/passwords.py +0 -0
  60. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/core/paths.py +0 -0
  61. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/core/registry.py +0 -0
  62. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/core/results.py +0 -0
  63. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/core/snapshot.py +0 -0
  64. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/core/telemetry.py +0 -0
  65. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/src/atsfill/py.typed +0 -0
  66. {atsfill-0.0.5.dev188 → atsfill-0.0.5.dev189}/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.dev188
3
+ Version: 0.0.5.dev189
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.dev188"
3
+ version = "0.0.5.dev189"
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.dev188"
3
+ version = "0.0.5.dev189"
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"
@@ -73,6 +73,7 @@ YES_NO = ["Yes", "No"]
73
73
  NO_YES = ["No", "Yes"]
74
74
  MONTH = " (MM/YYYY)"
75
75
  YEAR = " (year)"
76
+ NUMBER = " (a number)" # an input that takes only digits
76
77
  PICKER = ".react-datepicker"
77
78
  MONTHS: list[str] = [
78
79
  "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"
@@ -365,7 +366,7 @@ class Phenom:
365
366
  self._open(page, fresh=True)
366
367
  self._accept_terms(page)
367
368
  self._kept, self._shown = {}, {}
368
- if RESUME_ID in answers and page.locator(RESUME).count():
369
+ if RESUME_ID in answers and _shows_upload(page):
369
370
  _upload(page, _one(answers[RESUME_ID]))
370
371
  self._kept[RESUME_ID] = PurePath(_one(answers[RESUME_ID])).name
371
372
  for number in range(1, MAX_STEPS + 1):
@@ -664,7 +665,8 @@ class _Config:
664
665
  if html == "daterangepicker" or widget == "daterangepicker":
665
666
  return self.date_range(p, u, path, nav, when)
666
667
  if p.get("type") == "array" and get(p, "items.properties") and html not in WIDGETS:
667
- if not (int(p.get("minItems") or 0) or int(u.get("minItemsRequired") or 0)):
668
+ shown = p.get("minItems"), p.get("defaultEntries"), u.get("minItemsRequired")
669
+ if not any(int(n or 0) for n in shown):
668
670
  return [] # entries only once Add is clicked
669
671
  return self.object(p["items"], u.get("items") or {}, f"{path}[0].", nav, False, when)
670
672
  if p.get("type") == "object" or html in ("object", "phoneNumber_v2"):
@@ -681,6 +683,7 @@ class _Config:
681
683
  # a signature's date: today's, the only one the page takes
682
684
  "prefilled": date.today().isoformat() if p.get("defaultTodayDate") is True else None,
683
685
  "max_length": p.get("maxLength") if isinstance(p.get("maxLength"), int) else None,
686
+ "number": kind == "text" and _number(p, html, widget),
684
687
  }
685
688
  self.labels[path] = raw["label"]
686
689
  if kind in ("select", "typeahead", "radio", "checkboxes"):
@@ -1084,7 +1087,10 @@ class _Config:
1084
1087
  held = get(
1085
1088
  {"phApp": self.ph}, get(self.config, f"uiSchema.{name}.preFillWindowObj") or ""
1086
1089
  )
1087
- held = held if held is not None else {"locale": self.ph.get("locale")}.get(name)
1090
+ if held is None: # the job's data by its name (`jobCountry`), or the page's locale
1091
+ held = ({"locale": self.ph.get("locale")} | self.ddo).get(name)
1092
+ if "pattern" in test: # a job location naming a county
1093
+ return isinstance(held, str) and re.search(test["pattern"], held) is not None
1088
1094
  return True if values is None else held in values
1089
1095
  return Is(f"{path}{name}", values)
1090
1096
 
@@ -1249,6 +1255,11 @@ def _kind(p: dict, html: Any, widget: Any, path: str) -> str:
1249
1255
  return "text"
1250
1256
 
1251
1257
 
1258
+ def _number(p: dict, html: Any, widget: Any) -> bool:
1259
+ """Whether the input takes only digits (`input[type=number]`)."""
1260
+ return p.get("type") in ("integer", "number") or bool({html, widget} & {"number", "updown"})
1261
+
1262
+
1252
1263
  def _picker(form: str) -> str:
1253
1264
  """The calendar a format picks from: `date` (a day), `month` (`MM/yyyy`), `year`."""
1254
1265
  tokens = {t[0] for t in re.findall(r"y+|m+|d+", form.lower())}
@@ -1312,6 +1323,7 @@ def _field(raw: dict[str, Any]) -> Field:
1312
1323
  if kind == "checkbox": # an optional box starts clear
1313
1324
  options = YES_NO if raw["required"] else NO_YES
1314
1325
  label = raw["label"] + {"month": MONTH, "year": YEAR}.get(kind, "")
1326
+ label += NUMBER if raw.get("number") else ""
1315
1327
  sets = raw.get("option_sets")
1316
1328
  field_type = (
1317
1329
  FieldType.TYPEAHEAD if kind == "typeahead" and not options and not sets else TYPES[kind]
@@ -1469,6 +1481,15 @@ def _one(answer: Answer) -> str:
1469
1481
  return answer if isinstance(answer, str) else answer[0]
1470
1482
 
1471
1483
 
1484
+ def _shows_upload(page: Page) -> bool:
1485
+ """Whether the resume upload shows: on one site a moment after the form, past its terms."""
1486
+ try:
1487
+ page.locator(RESUME).first.wait_for(state="attached", timeout=FIELD_TIMEOUT_MS)
1488
+ except Timeout:
1489
+ return False
1490
+ return True
1491
+
1492
+
1472
1493
  def _upload(page: Page, path: str) -> None:
1473
1494
  """Attaches the resume, taken once Phenom's `/fileUpload` answers or the page shows its
1474
1495
  name, and waits for its parse, which fills and clears fields when it comes back: until
File without changes
File without changes