hh-applicant-tool 0.6.6__py3-none-any.whl → 0.6.8__py3-none-any.whl

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.

@@ -37,10 +37,40 @@ class Namespace(BaseNamespace):
37
37
  search: str
38
38
  schedule: str
39
39
  dry_run: bool
40
-
40
+ # Пошли доп фильтры, которых не было
41
+ experience: str
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()
41
68
 
42
69
  class Operation(BaseOperation, GetResumeIdMixin):
43
- """Откликнуться на все подходящие вакансии."""
70
+ """Откликнуться на все подходящие вакансии.
71
+
72
+ Описание фильтров для поиска вакансий: <https://api.hh.ru/openapi/redoc#tag/Poisk-vakansij-dlya-soiskatelya/operation/get-vacancies-similar-to-resume>
73
+ """
44
74
 
45
75
  def setup_parser(self, parser: argparse.ArgumentParser) -> None:
46
76
  parser.add_argument("--resume-id", help="Идентефикатор резюме")
@@ -114,6 +144,38 @@ class Operation(BaseOperation, GetResumeIdMixin):
114
144
  default=False,
115
145
  action=argparse.BooleanOptionalAction,
116
146
  )
147
+ parser.add_argument(
148
+ "--experience",
149
+ help="Уровень опыта работы в вакансии. Возможные значения: noExperience, between1And3, between3And6, moreThan6",
150
+ type=str,
151
+ default=None,
152
+ )
153
+ parser.add_argument("--employment", nargs="+", help="Тип занятости (employment)")
154
+ parser.add_argument("--area", nargs="+", help="Регион (area id)")
155
+ parser.add_argument("--metro", nargs="+", help="Станции метро (metro id)")
156
+ parser.add_argument("--professional-role", nargs="+", help="Проф. роль (id)")
157
+ parser.add_argument("--industry", nargs="+", help="Индустрия (industry id)")
158
+ parser.add_argument("--employer-id", nargs="+", help="ID работодателей")
159
+ parser.add_argument("--excluded-employer-id", nargs="+", help="Исключить работодателей")
160
+ parser.add_argument("--currency", help="Код валюты (RUR, USD, EUR)")
161
+ parser.add_argument("--salary", type=int, help="Минимальная зарплата")
162
+ parser.add_argument("--only-with-salary", default=False, action=argparse.BooleanOptionalAction)
163
+ parser.add_argument("--label", nargs="+", help="Метки вакансий (label)")
164
+ parser.add_argument("--period", type=int, help="Искать вакансии за N дней")
165
+ parser.add_argument("--date-from", help="Дата публикации с (YYYY-MM-DD)")
166
+ parser.add_argument("--date-to", help="Дата публикации по (YYYY-MM-DD)")
167
+ parser.add_argument("--top-lat", type=float, help="Гео: верхняя широта")
168
+ parser.add_argument("--bottom-lat", type=float, help="Гео: нижняя широта")
169
+ parser.add_argument("--left-lng", type=float, help="Гео: левая долгота")
170
+ parser.add_argument("--right-lng", type=float, help="Гео: правая долгота")
171
+ parser.add_argument("--sort-point-lat", type=float, help="Координата lat для сортировки по расстоянию")
172
+ parser.add_argument("--sort-point-lng", type=float, help="Координата lng для сортировки по расстоянию")
173
+ parser.add_argument("--no-magic", default=False, action=argparse.BooleanOptionalAction, help="Отключить авторазбор текста запроса")
174
+ parser.add_argument("--premium", default=False, action=argparse.BooleanOptionalAction, help="Только премиум вакансии")
175
+ parser.add_argument("--responses-count-enabled", default=False, action=argparse.BooleanOptionalAction, help="Включить счётчик откликов")
176
+ parser.add_argument("--search-field", nargs="+", help="Поля поиска (name, company_name и т.п.)")
177
+ parser.add_argument("--clusters", action=argparse.BooleanOptionalAction, help="Включить кластеры (по умолчанию None)")
178
+ #parser.add_argument("--describe-arguments", action=argparse.BooleanOptionalAction, help="Вернуть описание параметров запроса")
117
179
 
118
180
  def run(
119
181
  self, args: Namespace, api_client: ApiClient, telemetry_client: TelemetryClient
@@ -157,6 +219,32 @@ class Operation(BaseOperation, GetResumeIdMixin):
157
219
  self.search = args.search
158
220
  self.schedule = args.schedule
159
221
  self.dry_run = args.dry_run
222
+ self.experience = args.experience
223
+ self.search_field = args.search_field
224
+ self.employment = args.employment
225
+ self.area = args.area
226
+ self.metro = args.metro
227
+ self.professional_role = args.professional_role
228
+ self.industry = args.industry
229
+ self.employer_id = args.employer_id
230
+ self.excluded_employer_id = args.excluded_employer_id
231
+ self.currency = args.currency
232
+ self.salary = args.salary
233
+ self.only_with_salary = args.only_with_salary
234
+ self.label = args.label
235
+ self.period = args.period
236
+ self.date_from = args.date_from
237
+ self.date_to = args.date_to
238
+ self.top_lat = args.top_lat
239
+ self.bottom_lat = args.bottom_lat
240
+ self.left_lng = args.left_lng
241
+ self.right_lng = args.right_lng
242
+ self.sort_point_lat = args.sort_point_lat
243
+ self.sort_point_lng = args.sort_point_lng
244
+ self.clusters = args.clusters
245
+ #self.describe_arguments = args.describe_arguments
246
+ self.no_magic = args.no_magic
247
+ self.premium = args.premium
160
248
  self._apply_similar()
161
249
 
162
250
  def _get_application_messages(self, message_list: TextIO | None) -> list[str]:
@@ -358,18 +446,73 @@ class Operation(BaseOperation, GetResumeIdMixin):
358
446
  except TelemetryError as ex:
359
447
  logger.error(ex)
360
448
 
449
+ def _get_search_params(self, page: int, per_page: int) -> dict:
450
+ params = {
451
+ "page": page,
452
+ "per_page": per_page,
453
+ "order_by": self.order_by,
454
+ }
455
+ if self.search:
456
+ params["text"] = self.search
457
+ if self.schedule:
458
+ params['schedule'] = self.schedule
459
+ if self.experience:
460
+ params['experience'] = self.experience
461
+ if self.search_field:
462
+ params["search_field"] = self.search_field
463
+ if self.employment:
464
+ params["employment"] = self.employment
465
+ if self.area:
466
+ params["area"] = self.area
467
+ if self.metro:
468
+ params["metro"] = self.metro
469
+ if self.professional_role:
470
+ params["professional_role"] = self.professional_role
471
+ if self.industry:
472
+ params["industry"] = self.industry
473
+ if self.employer_id:
474
+ params["employer_id"] = self.employer_id
475
+ if self.excluded_employer_id:
476
+ params["excluded_employer_id"] = self.excluded_employer_id
477
+ if self.currency:
478
+ params["currency"] = self.currency
479
+ if self.salary:
480
+ params["salary"] = self.salary
481
+ if self.only_with_salary is not None:
482
+ params["only_with_salary"] = _bool(self.only_with_salary)
483
+ if self.label:
484
+ params["label"] = self.label
485
+ if self.period:
486
+ params["period"] = self.period
487
+ if self.date_from:
488
+ params["date_from"] = self.date_from
489
+ if self.date_to:
490
+ params["date_to"] = self.date_to
491
+ if self.top_lat:
492
+ params["top_lat"] = self.top_lat
493
+ if self.bottom_lat:
494
+ params["bottom_lat"] = self.bottom_lat
495
+ if self.left_lng:
496
+ params["left_lng"] = self.left_lng
497
+ if self.right_lng:
498
+ params["right_lng"] = self.right_lng
499
+ if self.sort_point_lat:
500
+ params["sort_point_lat"] = self.sort_point_lat
501
+ if self.sort_point_lng:
502
+ params["sort_point_lng"] = self.sort_point_lng
503
+ if self.clusters is not None:
504
+ params["clusters"] = self.clusters
505
+ if self.no_magic is not None:
506
+ params["no_magic"] = _bool(self.no_magic)
507
+ if self.premium is not None:
508
+ params["premium"] = _bool(self.premium)
509
+ return params
510
+
361
511
  def _get_vacancies(self, per_page: int = 100) -> list[VacancyItem]:
362
512
  rv = []
513
+ # API отдает только 2000 результатов
363
514
  for page in range(20):
364
- params = {
365
- "page": page,
366
- "per_page": per_page,
367
- "order_by": self.order_by,
368
- }
369
- if self.search:
370
- params["text"] = self.search
371
- if self.schedule:
372
- params['schedule'] = self.schedule
515
+ params = self._get_search_params(page, per_page)
373
516
  res: ApiListResponse = self.api_client.get(
374
517
  f"/resumes/{self.resume_id}/similar_vacancies", params
375
518
  )
@@ -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",
@@ -42,3 +42,4 @@ class VacancyItem(TypedDict):
42
42
  sort_point_distance: float
43
43
  type: dict
44
44
  url: str
45
+ experience: dict
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: hh-applicant-tool
3
- Version: 0.6.6
3
+ Version: 0.6.8
4
4
  Summary:
5
5
  Author: Senior YAML Developer
6
6
  Author-email: yamldeveloper@proton.me
@@ -10,6 +10,7 @@ Classifier: Programming Language :: Python :: 3.10
10
10
  Classifier: Programming Language :: Python :: 3.11
11
11
  Classifier: Programming Language :: Python :: 3.12
12
12
  Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
13
14
  Provides-Extra: qt
14
15
  Requires-Dist: prettytable (>=3.6.0,<4.0.0)
15
16
  Requires-Dist: pyqt6 (==6.7.0) ; extra == "qt"
@@ -64,6 +65,8 @@ $$('[data-qa="vacancy-serp__vacancy_response"]').forEach((el) => el.click());
64
65
 
65
66
  ### Установка
66
67
 
68
+ Универсальный с использованием pipx (требует пакета `python-pipx` в Arch):
69
+
67
70
  ```bash
68
71
  # Версия с поддержкой авторизации через запуск окна с браузером (эта версия очень много весит)
69
72
  # Можно использовать обычный pip
@@ -76,6 +79,18 @@ $ pipx install git+https://github.com/s3rgeym/hh-applicant-tool
76
79
  $ pipx upgrade hh-applicant-tool
77
80
  ```
78
81
 
82
+ pipx добавляет исполняемый файл `hh-appplicant-tool` в `~/.local/bin`, делая эту команду доступной. Путь до `~/.local/bin` должен быть в `$PATH` (в большинстве дистрибутивов он добавлен).
83
+
84
+ Традиционный способ для Linux/Mac:
85
+
86
+ ```sh
87
+ mkdir -p ~/.venvs
88
+ python -m venv ~/.venvs/hh-applicant-tool
89
+ # Это придется делать постоянно, чтобы команда hh-applicant-tool стала доступна
90
+ . ~/.venvs/hh-applicant-tool/bin/activate
91
+ pip install 'hh-applicant-tool[qt]'
92
+ ```
93
+
79
94
  Отдельно я распишу процесс установки в **Windows** в подробностях:
80
95
 
81
96
  * Для начала поставьте последнюю версию **Python 3** любым удобным способом.
@@ -88,7 +103,8 @@ $ pipx upgrade hh-applicant-tool
88
103
  ```ps
89
104
  PS> python -m venv hh-applicant-venv
90
105
  PS> .\hh-applicant-venv\Scripts\activate
91
- ```
106
+ ```
107
+
92
108
  * Поставьте все пакеты в виртуальное окружение `hh-applicant-venv`:
93
109
  ```ps
94
110
  (hh-applicant-venv) PS> pip install hh-applicant-tool[qt]
@@ -11,7 +11,7 @@ hh_applicant_tool/jsonc.py,sha256=QNS4gRHfi7SAeOFnffAIuhH7auC4Y4HAkmH12eX5PkI,40
11
11
  hh_applicant_tool/main.py,sha256=A4YPkNXAdZY0GoGm0iigiQtzXTrpR3SaIGo54q9-Dd0,5652
12
12
  hh_applicant_tool/mixins.py,sha256=8VoyrNgdlljy6pLTSFGJOYd9kagWT3yFOZYIGR6MEbI,425
13
13
  hh_applicant_tool/operations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- hh_applicant_tool/operations/apply_similar.py,sha256=VDK3QmdtuSJy4zxwhccDCUz1kEX7ypLpt7Wk8rNbZX8,16058
14
+ hh_applicant_tool/operations/apply_similar.py,sha256=o7co4d3zc9sDCXAOjqs3dQe8_OoXU5M9PaiUtvfLxak,23590
15
15
  hh_applicant_tool/operations/authorize.py,sha256=NYrxe6oemUBcDHioT1t1lJmi9l45V4ZXzQPD_-nf6hk,3328
16
16
  hh_applicant_tool/operations/call_api.py,sha256=o3GZgtqk6w4zpCm-JTHVjFrKVOwW-vsu1HdRi-hqAjo,1423
17
17
  hh_applicant_tool/operations/clear_negotiations.py,sha256=FG_43P5GWmfKUggkKZqDznQ2_iBJ3zrZtv8yEI2XOXQ,4527
@@ -20,13 +20,13 @@ hh_applicant_tool/operations/delete_telemetry.py,sha256=JHdh_l7IJL_qy5AIIy8FQpUu
20
20
  hh_applicant_tool/operations/get_employer_contacts.py,sha256=Sd-x3O08bmKm1OGVLtJ6rcPZ_j1jwjlqKV4z1n_G-38,9918
21
21
  hh_applicant_tool/operations/list_resumes.py,sha256=dILHyBCSEVqdNAvD8SML5f2Lau1R2AzTaKE9B4FG8Wg,1109
22
22
  hh_applicant_tool/operations/refresh_token.py,sha256=v_Fcw9mCfOdE6MLTCQjZQudhJPX0fup3k0BaIM394Qw,834
23
- hh_applicant_tool/operations/reply_employers.py,sha256=uxLN2YpZvz3m_KtnfaaJcfVEZkO-h9RMRndFmiqhLQw,13512
23
+ hh_applicant_tool/operations/reply_employers.py,sha256=40rsTgpoEUaRQUq5ZQYgojKb42smYRkbDDD2S_7Vwtg,13512
24
24
  hh_applicant_tool/operations/update_resumes.py,sha256=_r7HA_vpYMs5DFY-mVP1ZRG9bggsv7ebKYrwteBmJ30,1053
25
25
  hh_applicant_tool/operations/whoami.py,sha256=pNWJMmEQLBk3U6eiGz4CHcX7eXzDXcfezFjX7zLjqyA,711
26
26
  hh_applicant_tool/telemetry_client.py,sha256=1EKZWc5kMx2yERW9SrR9vaf-OB6M_KKcMXeicH5YyY0,3834
27
- hh_applicant_tool/types.py,sha256=q3yaIcq-UOkPzjxws0OFa4w9fTty-yx79_dic70_dUM,843
27
+ hh_applicant_tool/types.py,sha256=sQbt_vGXtWPRJ3UzcUkE87BTHOaGTsFxqdZa_qFghZE,864
28
28
  hh_applicant_tool/utils.py,sha256=3T4A2AykGqTwtGAttmYplIjHwFl3pNAcbWIVuA-OheQ,3080
29
- hh_applicant_tool-0.6.6.dist-info/METADATA,sha256=qQBrCk2-s6rKkEfJ_zMAwkB3oj3uLpsi5V0J-L42BRo,20600
30
- hh_applicant_tool-0.6.6.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
31
- hh_applicant_tool-0.6.6.dist-info/entry_points.txt,sha256=Vb7M2YaYLMtKYJZh8chIrXZApMzSRFT1-rQw-U9r10g,65
32
- hh_applicant_tool-0.6.6.dist-info/RECORD,,
29
+ hh_applicant_tool-0.6.8.dist-info/METADATA,sha256=CTMbI2mzDnQZRxZ71qnP1yM-XZblyQmiQnZ49fypvtg,21401
30
+ hh_applicant_tool-0.6.8.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
31
+ hh_applicant_tool-0.6.8.dist-info/entry_points.txt,sha256=Vb7M2YaYLMtKYJZh8chIrXZApMzSRFT1-rQw-U9r10g,65
32
+ hh_applicant_tool-0.6.8.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.3
2
+ Generator: poetry-core 2.2.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any