amochka 0.1.2.1__py3-none-any.whl → 0.1.3__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.
- amochka/client.py +21 -87
- {amochka-0.1.2.1.dist-info → amochka-0.1.3.dist-info}/METADATA +1 -1
- amochka-0.1.3.dist-info/RECORD +6 -0
- amochka-0.1.2.1.dist-info/RECORD +0 -6
- {amochka-0.1.2.1.dist-info → amochka-0.1.3.dist-info}/WHEEL +0 -0
- {amochka-0.1.2.1.dist-info → amochka-0.1.3.dist-info}/top_level.txt +0 -0
amochka/client.py
CHANGED
|
@@ -19,15 +19,7 @@ RATE_LIMIT = 7 # Максимум 7 запросов в секунду
|
|
|
19
19
|
class Deal(dict):
|
|
20
20
|
"""
|
|
21
21
|
Объект сделки расширяет стандартный словарь данными из custom_fields_values.
|
|
22
|
-
|
|
23
|
-
Обеспечивает два способа доступа к кастомным полям:
|
|
24
|
-
1. get(key): при обращении по названию (строкой) или по ID поля (integer)
|
|
25
|
-
возвращает текстовое значение поля (например, «Гурина Юлия»).
|
|
26
|
-
2. get_id(key): возвращает идентификатор выбранного варианта (enum_id) для полей типа select.
|
|
27
|
-
Если в данных enum_id отсутствует, производится поиск в переданной конфигурации полей,
|
|
28
|
-
сравнение выполняется без учёта регистра и лишних пробелов.
|
|
29
|
-
|
|
30
|
-
Параметр custom_fields_config – словарь, где ключи – ID полей, а значения – модели полей.
|
|
22
|
+
(Описание класса без изменений)
|
|
31
23
|
"""
|
|
32
24
|
def __init__(self, data, custom_fields_config=None):
|
|
33
25
|
super().__init__(data)
|
|
@@ -80,12 +72,7 @@ class Deal(dict):
|
|
|
80
72
|
def get_id(self, key, default=None):
|
|
81
73
|
"""
|
|
82
74
|
Возвращает идентификатор выбранного варианта (enum_id) для кастомного поля.
|
|
83
|
-
|
|
84
|
-
сравнение значения выполняется без учёта регистра и пробелов.
|
|
85
|
-
|
|
86
|
-
:param key: Название поля (строка) или ID поля (integer).
|
|
87
|
-
:param default: Значение по умолчанию, если enum_id не найден.
|
|
88
|
-
:return: Идентификатор выбранного варианта (целое число) или default.
|
|
75
|
+
(Описание метода без изменений)
|
|
89
76
|
"""
|
|
90
77
|
stored = None
|
|
91
78
|
if isinstance(key, str):
|
|
@@ -99,7 +86,6 @@ class Deal(dict):
|
|
|
99
86
|
enum_id = stored.get("enum_id")
|
|
100
87
|
if enum_id is not None:
|
|
101
88
|
return enum_id
|
|
102
|
-
# Если enum_id отсутствует, пробуем найти его в конфигурации кастомных полей
|
|
103
89
|
if self._custom_config:
|
|
104
90
|
field_def = None
|
|
105
91
|
if isinstance(key, int):
|
|
@@ -112,7 +98,6 @@ class Deal(dict):
|
|
|
112
98
|
if field_def:
|
|
113
99
|
enums = field_def.get("enums") or []
|
|
114
100
|
for enum in enums:
|
|
115
|
-
# Сравниваем текстовое значение без учёта регистра и лишних пробелов
|
|
116
101
|
if enum.get("value", "").lower().strip() == stored.get("value", "").lower().strip():
|
|
117
102
|
return enum.get("id", default)
|
|
118
103
|
return default
|
|
@@ -120,27 +105,13 @@ class Deal(dict):
|
|
|
120
105
|
class AmoCRMClient:
|
|
121
106
|
"""
|
|
122
107
|
Клиент для работы с API amoCRM.
|
|
123
|
-
|
|
124
|
-
Основные функции:
|
|
125
|
-
- load_token: Загружает и проверяет токен авторизации.
|
|
126
|
-
- _make_request: Выполняет HTTP-запрос с учетом ограничения по скорости.
|
|
127
|
-
- get_deal_by_id: Получает данные сделки по ID и возвращает объект Deal.
|
|
128
|
-
- get_custom_fields_mapping: Загружает и кэширует список кастомных полей.
|
|
129
|
-
- find_custom_field_id: Ищет кастомное поле по его названию.
|
|
130
|
-
- update_lead: Обновляет сделку, включая стандартные и кастомные поля.
|
|
131
|
-
|
|
132
|
-
Дополнительно можно задать уровень логирования через параметр log_level,
|
|
133
|
-
либо полностью отключить логирование, установив disable_logging=True.
|
|
108
|
+
(Описание класса без изменений, за исключением добавления параметра use_file_cache)
|
|
134
109
|
"""
|
|
135
|
-
def __init__(self, base_url, token_file=None, cache_file=None, log_level=logging.INFO, disable_logging=False):
|
|
110
|
+
def __init__(self, base_url, token_file=None, cache_file=None, log_level=logging.INFO, disable_logging=False, use_file_cache=True):
|
|
136
111
|
"""
|
|
137
112
|
Инициализирует клиента, задавая базовый URL, токен авторизации и файл кэша для кастомных полей.
|
|
138
|
-
|
|
139
|
-
:param
|
|
140
|
-
:param token_file: Файл, содержащий токен авторизации.
|
|
141
|
-
:param cache_file: Файл для кэширования данных кастомных полей.
|
|
142
|
-
:param log_level: Уровень логирования (например, logging.DEBUG, logging.INFO).
|
|
143
|
-
:param disable_logging: Если True, логирование будет отключено.
|
|
113
|
+
|
|
114
|
+
:param use_file_cache: Если True, кэш будет сохраняться в файл; иначе — только в оперативной памяти.
|
|
144
115
|
"""
|
|
145
116
|
self.base_url = base_url.rstrip('/')
|
|
146
117
|
domain = self.base_url.split("//")[-1].split(".")[0]
|
|
@@ -149,6 +120,7 @@ class AmoCRMClient:
|
|
|
149
120
|
if not cache_file:
|
|
150
121
|
cache_file = f"custom_fields_cache_{self.domain}.json"
|
|
151
122
|
self.cache_file = cache_file
|
|
123
|
+
self.use_file_cache = use_file_cache
|
|
152
124
|
self.token = self.load_token()
|
|
153
125
|
self._custom_fields_mapping = None
|
|
154
126
|
|
|
@@ -160,12 +132,7 @@ class AmoCRMClient:
|
|
|
160
132
|
logger.debug(f"AmoCRMClient initialized for domain {self.domain}")
|
|
161
133
|
|
|
162
134
|
def load_token(self):
|
|
163
|
-
|
|
164
|
-
Загружает токен авторизации из файла или строки, проверяет его срок действия.
|
|
165
|
-
|
|
166
|
-
:return: Действительный access_token.
|
|
167
|
-
:raises Exception: Если токен не найден или истёк.
|
|
168
|
-
"""
|
|
135
|
+
# Метод без изменений
|
|
169
136
|
data = None
|
|
170
137
|
if os.path.exists(self.token_file):
|
|
171
138
|
with open(self.token_file, 'r') as f:
|
|
@@ -193,16 +160,7 @@ class AmoCRMClient:
|
|
|
193
160
|
@sleep_and_retry
|
|
194
161
|
@limits(calls=RATE_LIMIT, period=1)
|
|
195
162
|
def _make_request(self, method, endpoint, params=None, data=None):
|
|
196
|
-
|
|
197
|
-
Выполняет HTTP-запрос к API amoCRM с учетом ограничения по скорости (rate limit).
|
|
198
|
-
|
|
199
|
-
:param method: HTTP-метод (GET, PATCH, POST, DELETE и т.д.).
|
|
200
|
-
:param endpoint: Конечная точка API (начинается с /api/v4/).
|
|
201
|
-
:param params: GET-параметры запроса.
|
|
202
|
-
:param data: Данные, отправляемые в JSON-формате.
|
|
203
|
-
:return: Ответ в формате JSON или None (если статус 204).
|
|
204
|
-
:raises Exception: При получении кода ошибки, отличного от 200/204.
|
|
205
|
-
"""
|
|
163
|
+
# Метод без изменений
|
|
206
164
|
url = f"{self.base_url}{endpoint}"
|
|
207
165
|
headers = {
|
|
208
166
|
"Authorization": f"Bearer {self.token}",
|
|
@@ -218,12 +176,7 @@ class AmoCRMClient:
|
|
|
218
176
|
return response.json()
|
|
219
177
|
|
|
220
178
|
def get_deal_by_id(self, deal_id):
|
|
221
|
-
|
|
222
|
-
Получает данные сделки по её ID и возвращает объект Deal.
|
|
223
|
-
|
|
224
|
-
:param deal_id: ID сделки.
|
|
225
|
-
:return: Объект Deal, включающий данные стандартных и кастомных полей.
|
|
226
|
-
"""
|
|
179
|
+
# Метод без изменений
|
|
227
180
|
endpoint = f"/api/v4/leads/{deal_id}"
|
|
228
181
|
params = {'with': 'contacts,companies,catalog_elements,loss_reason,tags'}
|
|
229
182
|
data = self._make_request("GET", endpoint, params=params)
|
|
@@ -233,10 +186,12 @@ class AmoCRMClient:
|
|
|
233
186
|
|
|
234
187
|
def _save_custom_fields_cache(self, mapping):
|
|
235
188
|
"""
|
|
236
|
-
Сохраняет
|
|
237
|
-
|
|
238
|
-
:param mapping: Словарь с отображением кастомных полей.
|
|
189
|
+
Сохраняет кэш кастомных полей в файл, если используется файловый кэш.
|
|
190
|
+
Если файловый кэш не используется, операция пропускается.
|
|
239
191
|
"""
|
|
192
|
+
if not self.use_file_cache:
|
|
193
|
+
logger.debug("File caching disabled; cache stored in memory only.")
|
|
194
|
+
return
|
|
240
195
|
cache_data = {"last_updated": time.time(), "mapping": mapping}
|
|
241
196
|
with open(self.cache_file, "w") as f:
|
|
242
197
|
json.dump(cache_data, f)
|
|
@@ -244,10 +199,12 @@ class AmoCRMClient:
|
|
|
244
199
|
|
|
245
200
|
def _load_custom_fields_cache(self):
|
|
246
201
|
"""
|
|
247
|
-
Загружает кэш кастомных полей из
|
|
248
|
-
|
|
249
|
-
:return: Кэшированные данные или None, если не удалось загрузить.
|
|
202
|
+
Загружает кэш кастомных полей из файла, если используется файловый кэш.
|
|
203
|
+
Если файловый кэш не используется, возвращает None.
|
|
250
204
|
"""
|
|
205
|
+
if not self.use_file_cache:
|
|
206
|
+
logger.debug("File caching disabled; no cache loaded from file.")
|
|
207
|
+
return None
|
|
251
208
|
if os.path.exists(self.cache_file):
|
|
252
209
|
with open(self.cache_file, "r") as f:
|
|
253
210
|
try:
|
|
@@ -262,12 +219,7 @@ class AmoCRMClient:
|
|
|
262
219
|
def get_custom_fields_mapping(self, force_update=False, cache_duration_hours=24):
|
|
263
220
|
"""
|
|
264
221
|
Возвращает словарь отображения кастомных полей для сделок.
|
|
265
|
-
Если данные кэшированы и не устарели, возвращает кэш; иначе выполняет
|
|
266
|
-
чтобы получить все страницы (с лимитом 250 полей на запрос).
|
|
267
|
-
|
|
268
|
-
:param force_update: Принудительное обновление кэша.
|
|
269
|
-
:param cache_duration_hours: Время жизни кэша в часах.
|
|
270
|
-
:return: Словарь mapping, где ключи – ID полей, а значения – их модели.
|
|
222
|
+
Если данные кэшированы и не устарели, возвращает кэш; иначе выполняет запросы для получения данных.
|
|
271
223
|
"""
|
|
272
224
|
if not force_update:
|
|
273
225
|
if self._custom_fields_mapping:
|
|
@@ -303,9 +255,6 @@ class AmoCRMClient:
|
|
|
303
255
|
def find_custom_field_id(self, search_term):
|
|
304
256
|
"""
|
|
305
257
|
Ищет кастомное поле по заданному названию (или части названия).
|
|
306
|
-
|
|
307
|
-
:param search_term: Строка для поиска по имени поля.
|
|
308
|
-
:return: Кортеж (field_id, field_obj) если найдено, иначе (None, None).
|
|
309
258
|
"""
|
|
310
259
|
mapping = self.get_custom_fields_mapping()
|
|
311
260
|
search_term_lower = search_term.lower().strip()
|
|
@@ -323,18 +272,6 @@ class AmoCRMClient:
|
|
|
323
272
|
def update_lead(self, lead_id, update_fields: dict, tags_to_add: list = None, tags_to_delete: list = None):
|
|
324
273
|
"""
|
|
325
274
|
Обновляет сделку, задавая новые значения для стандартных и кастомных полей.
|
|
326
|
-
|
|
327
|
-
Для кастомных полей:
|
|
328
|
-
- Если значение передается как целое число, оно интерпретируется как идентификатор варианта (enum_id)
|
|
329
|
-
для полей типа select.
|
|
330
|
-
- Если значение передается как строка, используется ключ "value".
|
|
331
|
-
|
|
332
|
-
:param lead_id: ID сделки, которую нужно обновить.
|
|
333
|
-
:param update_fields: Словарь с полями для обновления. Ключи могут быть стандартными или названием кастомного поля.
|
|
334
|
-
:param tags_to_add: Список тегов для добавления к сделке.
|
|
335
|
-
:param tags_to_delete: Список тегов для удаления из сделки.
|
|
336
|
-
:return: Ответ API в формате JSON.
|
|
337
|
-
:raises Exception: Если одно из кастомных полей не найдено.
|
|
338
275
|
"""
|
|
339
276
|
payload = {}
|
|
340
277
|
standard_fields = {
|
|
@@ -347,14 +284,11 @@ class AmoCRMClient:
|
|
|
347
284
|
payload[key] = value
|
|
348
285
|
logger.debug(f"Standard field {key} set to {value}")
|
|
349
286
|
else:
|
|
350
|
-
# Если значение integer, интерпретируем как enum_id для полей типа select,
|
|
351
|
-
# иначе как текстовое значение.
|
|
352
287
|
if isinstance(value, int):
|
|
353
288
|
field_value_dict = {"enum_id": value}
|
|
354
289
|
else:
|
|
355
290
|
field_value_dict = {"value": value}
|
|
356
291
|
try:
|
|
357
|
-
# Если ключ уже число, считаем его field_id
|
|
358
292
|
field_id = int(key)
|
|
359
293
|
custom_fields.append({"field_id": field_id, "values": [field_value_dict]})
|
|
360
294
|
logger.debug(f"Custom field by id {field_id} set to {value}")
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
amochka/__init__.py,sha256=92YIjNK3notY2wfG-l2jmGIIWjfE3ozpamc-e4LEILc,106
|
|
2
|
+
amochka/client.py,sha256=59iYwOQ90XTJvnzQCxRG8sgpbLRaZkFfYkZCN_Zl4Y8,15354
|
|
3
|
+
amochka-0.1.3.dist-info/METADATA,sha256=68jkHlxGXCk6acxCM8OVg4jluTZKy3yUdPfFOqzGK0U,445
|
|
4
|
+
amochka-0.1.3.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
5
|
+
amochka-0.1.3.dist-info/top_level.txt,sha256=y5qXFXJUECmDwO6hyupsuYcTpZKZyByeE9e-1sa2U24,8
|
|
6
|
+
amochka-0.1.3.dist-info/RECORD,,
|
amochka-0.1.2.1.dist-info/RECORD
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
amochka/__init__.py,sha256=92YIjNK3notY2wfG-l2jmGIIWjfE3ozpamc-e4LEILc,106
|
|
2
|
-
amochka/client.py,sha256=ltU7_LwaxVPqAeLFwY-jnMDgDAQ6C1vafyKzZ0BtGjc,20816
|
|
3
|
-
amochka-0.1.2.1.dist-info/METADATA,sha256=Bu4bqvfMsAKcIr9-emUpAG2_3B3iqZkwRTXSStVcjZY,447
|
|
4
|
-
amochka-0.1.2.1.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
5
|
-
amochka-0.1.2.1.dist-info/top_level.txt,sha256=y5qXFXJUECmDwO6hyupsuYcTpZKZyByeE9e-1sa2U24,8
|
|
6
|
-
amochka-0.1.2.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|