amochka 0.4.4__tar.gz → 0.4.6__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.
- {amochka-0.4.4 → amochka-0.4.6}/PKG-INFO +1 -1
- {amochka-0.4.4 → amochka-0.4.6}/amochka/__init__.py +1 -1
- {amochka-0.4.4 → amochka-0.4.6}/amochka/client.py +32 -1
- {amochka-0.4.4 → amochka-0.4.6}/amochka.egg-info/PKG-INFO +1 -1
- {amochka-0.4.4 → amochka-0.4.6}/pyproject.toml +1 -1
- {amochka-0.4.4 → amochka-0.4.6}/tests/test_client.py +76 -0
- {amochka-0.4.4 → amochka-0.4.6}/README.md +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/amochka/errors.py +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/amochka/etl.py +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/amochka.egg-info/SOURCES.txt +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/amochka.egg-info/dependency_links.txt +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/amochka.egg-info/requires.txt +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/amochka.egg-info/top_level.txt +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/etl/__init__.py +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/etl/config.py +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/etl/extractors.py +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/etl/loaders.py +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/etl/migrations/001_create_tables.sql +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/etl/run_etl.py +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/etl/transformers.py +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/setup.cfg +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/tests/test_cache.py +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/tests/test_etl.py +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/tests/test_http.py +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/tests/test_notes_events.py +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/tests/test_security.py +0 -0
- {amochka-0.4.4 → amochka-0.4.6}/tests/test_utils.py +0 -0
|
@@ -1093,12 +1093,22 @@ class AmoCRMClient:
|
|
|
1093
1093
|
updated_from: Optional[Union[int, float, str, datetime]] = None,
|
|
1094
1094
|
updated_to: Optional[Union[int, float, str, datetime]] = None,
|
|
1095
1095
|
contact_ids: Optional[Union[int, Sequence[Union[int, str]]]] = None,
|
|
1096
|
+
query: Optional[str] = None,
|
|
1097
|
+
min_query_length: Optional[int] = None,
|
|
1096
1098
|
limit: int = 250,
|
|
1097
1099
|
extra_params: Optional[dict] = None,
|
|
1098
1100
|
max_pages: Optional[int] = None,
|
|
1101
|
+
query_mode: str = "auto",
|
|
1102
|
+
min_phone_length: Optional[int] = None,
|
|
1099
1103
|
) -> Iterator[dict]:
|
|
1100
1104
|
"""
|
|
1101
|
-
Итератор контактов с фильтрацией по диапазону
|
|
1105
|
+
Итератор контактов с фильтрацией по диапазону обновления, списку ID или query.
|
|
1106
|
+
|
|
1107
|
+
:param query: Строка поиска (например, телефон). Перед отправкой очищается до цифр.
|
|
1108
|
+
:param min_query_length: Минимальная длина query (по числу цифр). Если не достигнута, query не отправляется.
|
|
1109
|
+
Deprecated: используйте min_phone_length.
|
|
1110
|
+
:param query_mode: Режим обработки query: auto | phone | raw.
|
|
1111
|
+
:param min_phone_length: Минимальная длина номера (по числу цифр). Применяется в phone/auto режимах.
|
|
1102
1112
|
|
|
1103
1113
|
:param max_pages: Максимальное количество страниц для итерации (None = без ограничений)
|
|
1104
1114
|
:raises ValidationError: Если параметры имеют некорректный тип или значение.
|
|
@@ -1125,6 +1135,15 @@ class AmoCRMClient:
|
|
|
1125
1135
|
elif not isinstance(contact_ids, str):
|
|
1126
1136
|
raise ValidationError(f"contact_ids must be int, str or sequence, got {type(contact_ids).__name__}")
|
|
1127
1137
|
|
|
1138
|
+
if min_query_length is not None and (not isinstance(min_query_length, int) or min_query_length < 1):
|
|
1139
|
+
raise ValidationError("min_query_length must be a positive int")
|
|
1140
|
+
if min_phone_length is not None and (not isinstance(min_phone_length, int) or min_phone_length < 1):
|
|
1141
|
+
raise ValidationError("min_phone_length must be a positive int")
|
|
1142
|
+
|
|
1143
|
+
mode = (query_mode or "auto").lower()
|
|
1144
|
+
if mode not in {"auto", "phone", "raw"}:
|
|
1145
|
+
raise ValidationError(f"query_mode must be one of: auto, phone, raw (got {query_mode})")
|
|
1146
|
+
|
|
1128
1147
|
params = {"limit": limit, "page": 1}
|
|
1129
1148
|
start_ts = self._to_timestamp(updated_from)
|
|
1130
1149
|
end_ts = self._to_timestamp(updated_to)
|
|
@@ -1135,6 +1154,18 @@ class AmoCRMClient:
|
|
|
1135
1154
|
contact_param = self._format_filter_values(contact_ids)
|
|
1136
1155
|
if contact_param:
|
|
1137
1156
|
params["filter[id][]"] = contact_param
|
|
1157
|
+
if query is not None:
|
|
1158
|
+
query_str = str(query).strip()
|
|
1159
|
+
if query_str:
|
|
1160
|
+
effective_min_len = min_phone_length if min_phone_length is not None else min_query_length
|
|
1161
|
+
is_raw = mode == "raw" or (mode == "auto" and any(ch.isalpha() or ch == "@" for ch in query_str))
|
|
1162
|
+
if is_raw:
|
|
1163
|
+
params["query"] = query_str
|
|
1164
|
+
else:
|
|
1165
|
+
query_digits = "".join(ch for ch in query_str if ch.isdigit())
|
|
1166
|
+
if query_digits:
|
|
1167
|
+
if effective_min_len is None or len(query_digits) >= effective_min_len:
|
|
1168
|
+
params["query"] = query_digits
|
|
1138
1169
|
if extra_params:
|
|
1139
1170
|
params.update(extra_params)
|
|
1140
1171
|
|
|
@@ -20,6 +20,17 @@ from amochka import (
|
|
|
20
20
|
)
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
def make_client():
|
|
24
|
+
import time
|
|
25
|
+
token_data = {"access_token": "token", "expires_at": str(time.time() + 3600)}
|
|
26
|
+
return AmoCRMClient(
|
|
27
|
+
base_url="https://example.amocrm.ru",
|
|
28
|
+
token_file=json.dumps(token_data),
|
|
29
|
+
cache_config=CacheConfig.disabled(),
|
|
30
|
+
disable_logging=True,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
23
34
|
class DummyClient(AmoCRMClient):
|
|
24
35
|
def __init__(self):
|
|
25
36
|
import time
|
|
@@ -260,3 +271,68 @@ def test_export_helpers_write_expected_files(tmp_path):
|
|
|
260
271
|
|
|
261
272
|
notes_call = next(item for item in client.calls if item[1] == "/api/v4/leads/notes")
|
|
262
273
|
assert notes_call[2]["filter[updated_at][from]"] is None or notes_call[2]["filter[updated_at][from]"] >= 0
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def test_iter_contacts_query_param(monkeypatch):
|
|
277
|
+
client = make_client()
|
|
278
|
+
captured = {}
|
|
279
|
+
|
|
280
|
+
def fake_request(method, endpoint, params=None, **kwargs):
|
|
281
|
+
captured["params"] = params
|
|
282
|
+
return {"_embedded": {"contacts": []}, "_page_count": 1}
|
|
283
|
+
|
|
284
|
+
monkeypatch.setattr(client, "_make_request", fake_request)
|
|
285
|
+
list(client.iter_contacts(query="7925", max_pages=1))
|
|
286
|
+
assert captured["params"]["query"] == "7925"
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def test_iter_contacts_query_min_length(monkeypatch):
|
|
290
|
+
client = make_client()
|
|
291
|
+
captured = {}
|
|
292
|
+
|
|
293
|
+
def fake_request(method, endpoint, params=None, **kwargs):
|
|
294
|
+
captured["params"] = params
|
|
295
|
+
return {"_embedded": {"contacts": []}, "_page_count": 1}
|
|
296
|
+
|
|
297
|
+
monkeypatch.setattr(client, "_make_request", fake_request)
|
|
298
|
+
list(client.iter_contacts(query="7925", min_query_length=8, max_pages=1))
|
|
299
|
+
assert "query" not in captured["params"]
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def test_iter_contacts_query_auto_raw_for_name(monkeypatch):
|
|
303
|
+
client = make_client()
|
|
304
|
+
captured = {}
|
|
305
|
+
|
|
306
|
+
def fake_request(method, endpoint, params=None, **kwargs):
|
|
307
|
+
captured["params"] = params
|
|
308
|
+
return {"_embedded": {"contacts": []}, "_page_count": 1}
|
|
309
|
+
|
|
310
|
+
monkeypatch.setattr(client, "_make_request", fake_request)
|
|
311
|
+
list(client.iter_contacts(query="ivan", max_pages=1))
|
|
312
|
+
assert captured["params"]["query"] == "ivan"
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def test_iter_contacts_query_phone_mode_forces_digits(monkeypatch):
|
|
316
|
+
client = make_client()
|
|
317
|
+
captured = {}
|
|
318
|
+
|
|
319
|
+
def fake_request(method, endpoint, params=None, **kwargs):
|
|
320
|
+
captured["params"] = params
|
|
321
|
+
return {"_embedded": {"contacts": []}, "_page_count": 1}
|
|
322
|
+
|
|
323
|
+
monkeypatch.setattr(client, "_make_request", fake_request)
|
|
324
|
+
list(client.iter_contacts(query="ivan 7925", query_mode="phone", max_pages=1))
|
|
325
|
+
assert captured["params"]["query"] == "7925"
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def test_iter_contacts_query_min_phone_length(monkeypatch):
|
|
329
|
+
client = make_client()
|
|
330
|
+
captured = {}
|
|
331
|
+
|
|
332
|
+
def fake_request(method, endpoint, params=None, **kwargs):
|
|
333
|
+
captured["params"] = params
|
|
334
|
+
return {"_embedded": {"contacts": []}, "_page_count": 1}
|
|
335
|
+
|
|
336
|
+
monkeypatch.setattr(client, "_make_request", fake_request)
|
|
337
|
+
list(client.iter_contacts(query="7925", min_phone_length=8, max_pages=1))
|
|
338
|
+
assert "query" not in captured["params"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|