hh-applicant-tool 1.4.7__py3-none-any.whl → 1.5.7__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.
Files changed (49) hide show
  1. hh_applicant_tool/__main__.py +1 -1
  2. hh_applicant_tool/ai/__init__.py +1 -0
  3. hh_applicant_tool/ai/openai.py +30 -14
  4. hh_applicant_tool/api/__init__.py +4 -2
  5. hh_applicant_tool/api/client.py +32 -17
  6. hh_applicant_tool/{constants.py → api/client_keys.py} +3 -3
  7. hh_applicant_tool/{datatypes.py → api/datatypes.py} +2 -0
  8. hh_applicant_tool/api/errors.py +8 -2
  9. hh_applicant_tool/{utils → api}/user_agent.py +1 -1
  10. hh_applicant_tool/main.py +63 -38
  11. hh_applicant_tool/operations/apply_similar.py +136 -52
  12. hh_applicant_tool/operations/authorize.py +97 -28
  13. hh_applicant_tool/operations/call_api.py +3 -3
  14. hh_applicant_tool/operations/{check_negotiations.py → clear_negotiations.py} +40 -26
  15. hh_applicant_tool/operations/list_resumes.py +5 -7
  16. hh_applicant_tool/operations/query.py +5 -3
  17. hh_applicant_tool/operations/refresh_token.py +9 -2
  18. hh_applicant_tool/operations/reply_employers.py +80 -40
  19. hh_applicant_tool/operations/settings.py +2 -2
  20. hh_applicant_tool/operations/update_resumes.py +5 -4
  21. hh_applicant_tool/operations/whoami.py +3 -3
  22. hh_applicant_tool/storage/__init__.py +5 -1
  23. hh_applicant_tool/storage/facade.py +2 -2
  24. hh_applicant_tool/storage/models/base.py +9 -4
  25. hh_applicant_tool/storage/models/contacts.py +42 -0
  26. hh_applicant_tool/storage/queries/schema.sql +23 -10
  27. hh_applicant_tool/storage/repositories/base.py +69 -15
  28. hh_applicant_tool/storage/repositories/contacts.py +5 -10
  29. hh_applicant_tool/storage/repositories/employers.py +1 -0
  30. hh_applicant_tool/storage/repositories/errors.py +19 -0
  31. hh_applicant_tool/storage/repositories/negotiations.py +1 -0
  32. hh_applicant_tool/storage/repositories/resumes.py +2 -7
  33. hh_applicant_tool/storage/repositories/settings.py +1 -0
  34. hh_applicant_tool/storage/repositories/vacancies.py +1 -0
  35. hh_applicant_tool/storage/utils.py +12 -15
  36. hh_applicant_tool/utils/__init__.py +3 -3
  37. hh_applicant_tool/utils/config.py +1 -1
  38. hh_applicant_tool/utils/log.py +6 -3
  39. hh_applicant_tool/utils/mixins.py +28 -46
  40. hh_applicant_tool/utils/string.py +15 -0
  41. hh_applicant_tool/utils/terminal.py +115 -0
  42. {hh_applicant_tool-1.4.7.dist-info → hh_applicant_tool-1.5.7.dist-info}/METADATA +384 -162
  43. hh_applicant_tool-1.5.7.dist-info/RECORD +68 -0
  44. {hh_applicant_tool-1.4.7.dist-info → hh_applicant_tool-1.5.7.dist-info}/WHEEL +1 -1
  45. hh_applicant_tool/storage/models/contact.py +0 -16
  46. hh_applicant_tool-1.4.7.dist-info/RECORD +0 -67
  47. /hh_applicant_tool/utils/{dateutil.py → date.py} +0 -0
  48. /hh_applicant_tool/utils/{jsonutil.py → json.py} +0 -0
  49. {hh_applicant_tool-1.4.7.dist-info → hh_applicant_tool-1.5.7.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ import base64
1
4
  import ctypes
5
+ import io
6
+ import os
2
7
  import platform
8
+ import sys
9
+
10
+ try:
11
+ from PIL import Image
12
+ except ImportError:
13
+
14
+ class Image:
15
+ pass
16
+
17
+
18
+ ESC = "\x1b"
3
19
 
4
20
 
5
21
  def setup_terminal() -> None:
@@ -17,3 +33,102 @@ def setup_terminal() -> None:
17
33
  # Если что-то пошло не так (старая Windows или нет прав),
18
34
  # просто продолжаем работу без цветов
19
35
  pass
36
+
37
+
38
+ def print_kitty_image(data: bytes) -> None:
39
+ # Кодируем весь файл целиком (он уже сжат в PNG)
40
+ b64data = base64.b64encode(data).decode("ascii")
41
+
42
+ # f=100 говорит терминалу: "это PNG, разберись сам с размерами"
43
+ # Нам больше не нужно указывать s=... и v=...
44
+ sys.stdout.write(f"\033_Ga=T,f=100;{b64data}\033\\")
45
+ sys.stdout.flush()
46
+ print()
47
+
48
+
49
+ def print_sixel_mage(image_bytes: bytes) -> None:
50
+ img = Image.open(io.BytesIO(image_bytes))
51
+
52
+ # Рекомендуется оставить ограничение размера,
53
+ # иначе Zellij может "лагать" на огромных картинках
54
+ # max_size = (800, 600)
55
+ # img.thumbnail(max_size, Image.Resampling.LANCZOS)
56
+ img = img.convert("RGB")
57
+
58
+ try:
59
+ img = img.quantize(colors=256, method=Image.Quantize.MAXCOVERAGE)
60
+ except Exception:
61
+ img = img.quantize(colors=256)
62
+
63
+ palette = img.getpalette()[: 256 * 3]
64
+ width, height = img.size
65
+ pixels = img.load()
66
+
67
+ is_multiplexer = "ZELLIJ" in os.environ or "TMUX" in os.environ
68
+
69
+ # Собираем всё в список строк, чтобы минимизировать количество вызовов write
70
+ out = []
71
+
72
+ # 1. Начало (Обертка для Zellij)
73
+ if is_multiplexer:
74
+ out.append(f"{ESC}P+p")
75
+
76
+ # 2. Sixel заголовок + Растр
77
+ out.append(f'{ESC}Pq"1;1;{width};{height}')
78
+
79
+ # 3. Палитра
80
+ for i in range(256):
81
+ r, g, b = palette[i * 3 : i * 3 + 3]
82
+ out.append(f"#{i};2;{r * 100 // 255};{g * 100 // 255};{b * 100 // 255}")
83
+
84
+ # 4. Отрисовка
85
+ for y in range(0, height, 6):
86
+ h_chunk = min(6, height - y)
87
+
88
+ # Считаем уникальные цвета в полосе (быстрее, чем перебирать всю палитру)
89
+ colors_in_band = set()
90
+ for dy in range(h_chunk):
91
+ for x in range(width):
92
+ colors_in_band.add(pixels[x, y + dy])
93
+
94
+ for color in colors_in_band:
95
+ out.append(f"#{color}")
96
+ last_char = ""
97
+ count = 0
98
+
99
+ for x in range(width):
100
+ bits = 0
101
+ for dy in range(h_chunk):
102
+ if pixels[x, y + dy] == color:
103
+ bits |= 1 << dy
104
+
105
+ char = chr(63 + bits)
106
+ if char == last_char:
107
+ count += 1
108
+ else:
109
+ if count > 0:
110
+ out.append(
111
+ f"!{count}{last_char}"
112
+ if count > 3
113
+ else last_char * count
114
+ )
115
+ last_char, count = char, 1
116
+
117
+ if count > 0:
118
+ out.append(
119
+ f"!{count}{last_char}" if count > 3 else last_char * count
120
+ )
121
+ out.append("$")
122
+ out.append("-")
123
+
124
+ # 5. Конец Sixel
125
+ out.append(f"{ESC}\\")
126
+
127
+ # 6. Конец обертки мультиплексора
128
+ if is_multiplexer:
129
+ out.append(f"{ESC}\\")
130
+
131
+ # ВЫВОД: Одной строкой БЕЗ лишних переносов в конце
132
+ sys.stdout.write("".join(out))
133
+ sys.stdout.flush()
134
+ print()