hh-applicant-tool 0.6.7__tar.gz → 0.6.9__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.

Potentially problematic release.


This version of hh-applicant-tool might be problematic. Click here for more details.

Files changed (31) hide show
  1. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/PKG-INFO +1 -1
  2. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/operations/apply_similar.py +153 -13
  3. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/operations/reply_employers.py +1 -1
  4. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/pyproject.toml +1 -1
  5. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/README.md +0 -0
  6. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/__init__.py +0 -0
  7. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/__main__.py +0 -0
  8. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/ai/__init__.py +0 -0
  9. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/ai/blackbox.py +0 -0
  10. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/api/__init__.py +0 -0
  11. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/api/client.py +0 -0
  12. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/api/errors.py +0 -0
  13. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/color_log.py +0 -0
  14. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/constants.py +0 -0
  15. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/jsonc.py +0 -0
  16. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/main.py +0 -0
  17. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/mixins.py +0 -0
  18. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/operations/__init__.py +0 -0
  19. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/operations/authorize.py +0 -0
  20. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/operations/call_api.py +0 -0
  21. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/operations/clear_negotiations.py +0 -0
  22. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/operations/config.py +0 -0
  23. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/operations/delete_telemetry.py +0 -0
  24. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/operations/get_employer_contacts.py +0 -0
  25. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/operations/list_resumes.py +0 -0
  26. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/operations/refresh_token.py +0 -0
  27. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/operations/update_resumes.py +0 -0
  28. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/operations/whoami.py +0 -0
  29. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/telemetry_client.py +0 -0
  30. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/types.py +0 -0
  31. {hh_applicant_tool-0.6.7 → hh_applicant_tool-0.6.9}/hh_applicant_tool/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hh-applicant-tool
3
- Version: 0.6.7
3
+ Version: 0.6.9
4
4
  Summary:
5
5
  Author: Senior YAML Developer
6
6
  Author-email: yamldeveloper@proton.me
@@ -37,11 +37,43 @@ class Namespace(BaseNamespace):
37
37
  search: str
38
38
  schedule: str
39
39
  dry_run: bool
40
+ # Пошли доп фильтры, которых не было
40
41
  experience: str
41
-
42
+ employment: list[str] | None
43
+ area: list[str] | None
44
+ metro: list[str] | None
45
+ professional_role: list[str] | None
46
+ industry: list[str] | None
47
+ employer_id: list[str] | None
48
+ excluded_employer_id: list[str] | None
49
+ currency: str | None
50
+ salary: int | None
51
+ only_with_salary: bool
52
+ label: list[str] | None
53
+ period: int | None
54
+ date_from: str | None
55
+ date_to: str | None
56
+ top_lat: float | None
57
+ bottom_lat: float | None
58
+ left_lng: float | None
59
+ right_lng: float | None
60
+ sort_point_lat: float | None
61
+ sort_point_lng: float | None
62
+ no_magic: bool
63
+ premium: bool
64
+ responses_count_enabled: bool
65
+
66
+ def _bool(v: bool) -> str:
67
+ return str(v).lower()
68
+
69
+ def _join_list(items: list[Any] | None) -> str:
70
+ return ",".join(f"{v}" for v in items) if items else ""
42
71
 
43
72
  class Operation(BaseOperation, GetResumeIdMixin):
44
- """Откликнуться на все подходящие вакансии."""
73
+ """Откликнуться на все подходящие вакансии.
74
+
75
+ Описание фильтров для поиска вакансий: <https://api.hh.ru/openapi/redoc#tag/Poisk-vakansij-dlya-soiskatelya/operation/get-vacancies-similar-to-resume>
76
+ """
45
77
 
46
78
  def setup_parser(self, parser: argparse.ArgumentParser) -> None:
47
79
  parser.add_argument("--resume-id", help="Идентефикатор резюме")
@@ -121,6 +153,32 @@ class Operation(BaseOperation, GetResumeIdMixin):
121
153
  type=str,
122
154
  default=None,
123
155
  )
156
+ parser.add_argument("--employment", nargs="+", help="Тип занятости (employment)")
157
+ parser.add_argument("--area", nargs="+", help="Регион (area id)")
158
+ parser.add_argument("--metro", nargs="+", help="Станции метро (metro id)")
159
+ parser.add_argument("--professional-role", nargs="+", help="Проф. роль (id)")
160
+ parser.add_argument("--industry", nargs="+", help="Индустрия (industry id)")
161
+ parser.add_argument("--employer-id", nargs="+", help="ID работодателей")
162
+ parser.add_argument("--excluded-employer-id", nargs="+", help="Исключить работодателей")
163
+ parser.add_argument("--currency", help="Код валюты (RUR, USD, EUR)")
164
+ parser.add_argument("--salary", type=int, help="Минимальная зарплата")
165
+ parser.add_argument("--only-with-salary", default=False, action=argparse.BooleanOptionalAction)
166
+ parser.add_argument("--label", nargs="+", help="Метки вакансий (label)")
167
+ parser.add_argument("--period", type=int, help="Искать вакансии за N дней")
168
+ parser.add_argument("--date-from", help="Дата публикации с (YYYY-MM-DD)")
169
+ parser.add_argument("--date-to", help="Дата публикации по (YYYY-MM-DD)")
170
+ parser.add_argument("--top-lat", type=float, help="Гео: верхняя широта")
171
+ parser.add_argument("--bottom-lat", type=float, help="Гео: нижняя широта")
172
+ parser.add_argument("--left-lng", type=float, help="Гео: левая долгота")
173
+ parser.add_argument("--right-lng", type=float, help="Гео: правая долгота")
174
+ parser.add_argument("--sort-point-lat", type=float, help="Координата lat для сортировки по расстоянию")
175
+ parser.add_argument("--sort-point-lng", type=float, help="Координата lng для сортировки по расстоянию")
176
+ parser.add_argument("--no-magic", default=False, action=argparse.BooleanOptionalAction, help="Отключить авторазбор текста запроса")
177
+ parser.add_argument("--premium", default=False, action=argparse.BooleanOptionalAction, help="Только премиум вакансии")
178
+ parser.add_argument("--responses-count-enabled", default=False, action=argparse.BooleanOptionalAction, help="Включить счётчик откликов")
179
+ parser.add_argument("--search-field", nargs="+", help="Поля поиска (name, company_name и т.п.)")
180
+ parser.add_argument("--clusters", action=argparse.BooleanOptionalAction, help="Включить кластеры (по умолчанию None)")
181
+ #parser.add_argument("--describe-arguments", action=argparse.BooleanOptionalAction, help="Вернуть описание параметров запроса")
124
182
 
125
183
  def run(
126
184
  self, args: Namespace, api_client: ApiClient, telemetry_client: TelemetryClient
@@ -165,6 +223,31 @@ class Operation(BaseOperation, GetResumeIdMixin):
165
223
  self.schedule = args.schedule
166
224
  self.dry_run = args.dry_run
167
225
  self.experience = args.experience
226
+ self.search_field = args.search_field
227
+ self.employment = args.employment
228
+ self.area = args.area
229
+ self.metro = args.metro
230
+ self.professional_role = args.professional_role
231
+ self.industry = args.industry
232
+ self.employer_id = args.employer_id
233
+ self.excluded_employer_id = args.excluded_employer_id
234
+ self.currency = args.currency
235
+ self.salary = args.salary
236
+ self.only_with_salary = args.only_with_salary
237
+ self.label = args.label
238
+ self.period = args.period
239
+ self.date_from = args.date_from
240
+ self.date_to = args.date_to
241
+ self.top_lat = args.top_lat
242
+ self.bottom_lat = args.bottom_lat
243
+ self.left_lng = args.left_lng
244
+ self.right_lng = args.right_lng
245
+ self.sort_point_lat = args.sort_point_lat
246
+ self.sort_point_lng = args.sort_point_lng
247
+ self.clusters = args.clusters
248
+ #self.describe_arguments = args.describe_arguments
249
+ self.no_magic = args.no_magic
250
+ self.premium = args.premium
168
251
  self._apply_similar()
169
252
 
170
253
  def _get_application_messages(self, message_list: TextIO | None) -> list[str]:
@@ -366,20 +449,77 @@ class Operation(BaseOperation, GetResumeIdMixin):
366
449
  except TelemetryError as ex:
367
450
  logger.error(ex)
368
451
 
452
+ def _get_search_params(self, page: int, per_page: int) -> dict:
453
+ params = {
454
+ "page": page,
455
+ "per_page": per_page,
456
+ "order_by": self.order_by,
457
+ }
458
+
459
+ if self.search:
460
+ params["text"] = self.search
461
+ if self.schedule:
462
+ params['schedule'] = self.schedule
463
+ if self.experience:
464
+ params['experience'] = self.experience
465
+ if self.currency:
466
+ params["currency"] = self.currency
467
+ if self.salary:
468
+ params["salary"] = self.salary
469
+ if self.period:
470
+ params["period"] = self.period
471
+ if self.date_from:
472
+ params["date_from"] = self.date_from
473
+ if self.date_to:
474
+ params["date_to"] = self.date_to
475
+ if self.top_lat:
476
+ params["top_lat"] = self.top_lat
477
+ if self.bottom_lat:
478
+ params["bottom_lat"] = self.bottom_lat
479
+ if self.left_lng:
480
+ params["left_lng"] = self.left_lng
481
+ if self.right_lng:
482
+ params["right_lng"] = self.right_lng
483
+ if self.sort_point_lat:
484
+ params["sort_point_lat"] = self.sort_point_lat
485
+ if self.sort_point_lng:
486
+ params["sort_point_lng"] = self.sort_point_lng
487
+ if self.search_field:
488
+ params["search_field"] = _join_list(self.search_field)
489
+ if self.employment:
490
+ params["employment"] = _join_list(self.employment)
491
+ if self.area:
492
+ params["area"] = _join_list(self.area)
493
+ if self.metro:
494
+ params["metro"] = _join_list(self.metro)
495
+ if self.professional_role:
496
+ params["professional_role"] = _join_list(self.professional_role)
497
+ if self.industry:
498
+ params["industry"] = _join_list(self.industry)
499
+ if self.employer_id:
500
+ params["employer_id"] = _join_list(self.employer_id)
501
+ if self.excluded_employer_id:
502
+ params["excluded_employer_id"] = _join_list(self.excluded_employer_id)
503
+ if self.label:
504
+ params["label"] = _join_list(self.label)
505
+ if self.only_with_salary is not None:
506
+ params["only_with_salary"] = _bool(self.only_with_salary)
507
+ if self.clusters is not None:
508
+ params["clusters"] = _bool(self.clusters)
509
+ if self.no_magic is not None:
510
+ params["no_magic"] = _bool(self.no_magic)
511
+ if self.premium is not None:
512
+ params["premium"] = _bool(self.premium)
513
+ if self.responses_count_enabled is not None:
514
+ params["responses_count_enabled"] = _bool(self.responses_count_enabled)
515
+
516
+ return params
517
+
369
518
  def _get_vacancies(self, per_page: int = 100) -> list[VacancyItem]:
370
519
  rv = []
520
+ # API отдает только 2000 результатов
371
521
  for page in range(20):
372
- params = {
373
- "page": page,
374
- "per_page": per_page,
375
- "order_by": self.order_by,
376
- }
377
- if self.search:
378
- params["text"] = self.search
379
- if self.schedule:
380
- params['schedule'] = self.schedule
381
- if self.experience:
382
- params['experience'] = self.experience
522
+ params = self._get_search_params(page, per_page)
383
523
  res: ApiListResponse = self.api_client.get(
384
524
  f"/resumes/{self.resume_id}/similar_vacancies", params
385
525
  )
@@ -61,7 +61,7 @@ class Operation(BaseOperation, GetResumeIdMixin):
61
61
  "-m",
62
62
  "--reply-message",
63
63
  "--reply",
64
- help="Отправить сообщение во все чаты, где ожидают ответа либо не прочитали ответ. Еслм не передать сообщение, то нужно будет вводить его в интерактивном режиме.",
64
+ help="Отправить сообщение во все чаты, где ожидают ответа либо не прочитали ответ. Если не передать сообщение, то нужно будет вводить его в интерактивном режиме.",
65
65
  )
66
66
  parser.add_argument(
67
67
  "-p",
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "hh-applicant-tool"
3
- version = "0.6.7"
3
+ version = "0.6.9"
4
4
  description = ""
5
5
  authors = ["Senior YAML Developer <yamldeveloper@proton.me>"]
6
6
  readme = "README.md"