amochka 0.4.5__py3-none-any.whl → 0.4.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.
amochka/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
  amochka: Библиотека для работы с API amoCRM.
3
3
  """
4
4
 
5
- __version__ = "0.4.5"
5
+ __version__ = "0.4.7"
6
6
 
7
7
  from .client import AmoCRMClient, CacheConfig
8
8
  from .errors import (
amochka/client.py CHANGED
@@ -1098,12 +1098,17 @@ class AmoCRMClient:
1098
1098
  limit: int = 250,
1099
1099
  extra_params: Optional[dict] = None,
1100
1100
  max_pages: Optional[int] = None,
1101
+ query_mode: str = "auto",
1102
+ min_phone_length: Optional[int] = None,
1101
1103
  ) -> Iterator[dict]:
1102
1104
  """
1103
1105
  Итератор контактов с фильтрацией по диапазону обновления, списку ID или query.
1104
1106
 
1105
1107
  :param query: Строка поиска (например, телефон). Перед отправкой очищается до цифр.
1106
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 режимах.
1107
1112
 
1108
1113
  :param max_pages: Максимальное количество страниц для итерации (None = без ограничений)
1109
1114
  :raises ValidationError: Если параметры имеют некорректный тип или значение.
@@ -1130,6 +1135,15 @@ class AmoCRMClient:
1130
1135
  elif not isinstance(contact_ids, str):
1131
1136
  raise ValidationError(f"contact_ids must be int, str or sequence, got {type(contact_ids).__name__}")
1132
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
+
1133
1147
  params = {"limit": limit, "page": 1}
1134
1148
  start_ts = self._to_timestamp(updated_from)
1135
1149
  end_ts = self._to_timestamp(updated_to)
@@ -1141,10 +1155,17 @@ class AmoCRMClient:
1141
1155
  if contact_param:
1142
1156
  params["filter[id][]"] = contact_param
1143
1157
  if query is not None:
1144
- query_digits = "".join(ch for ch in query.strip() if ch.isdigit())
1145
- if query_digits:
1146
- if min_query_length is None or len(query_digits) >= min_query_length:
1147
- params["query"] = query_digits
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
1148
1169
  if extra_params:
1149
1170
  params.update(extra_params)
1150
1171
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amochka
3
- Version: 0.4.5
3
+ Version: 0.4.7
4
4
  Summary: Python library for working with amoCRM API with ETL capabilities
5
5
  Author-email: Timur <timurdt@gmail.com>
6
6
  License: MIT
@@ -0,0 +1,15 @@
1
+ amochka/__init__.py,sha256=1IrHrHoIJWMJgXlmxVzX93HXxTgDL7agZRR7GeP6AiY,925
2
+ amochka/client.py,sha256=zQUy6DhoQfLDnO9q7CxXd28cjLQmwE15MtTGsGYYVSs,91571
3
+ amochka/errors.py,sha256=Rg4U9srjboQwgU3wwhAed0p3vGcc0ZhuyKHIDhYFnp8,1371
4
+ amochka/etl.py,sha256=tzdGPRGxR49aSAjLksic-FqechvDUTfK8ZUUJGuIU7M,8960
5
+ etl/__init__.py,sha256=bp9fPqbKlOc7xzs27diHEvysy1FgBrwlpX6GnR6GL9U,255
6
+ etl/config.py,sha256=BvaGn5BSGMIfvUNNsnap04iy3BHyMOuRX81G7EiLUfE,9032
7
+ etl/extractors.py,sha256=PqjzlmUa8FLZa1z85mP6Y0-s_TH3REqW58632JzRKUc,13047
8
+ etl/loaders.py,sha256=AA4XDCbre-Y8SrIji5Sk8olW9WvREM9CMuuknFVe5j0,32589
9
+ etl/run_etl.py,sha256=6ieidJiiXOILI1hzFzCCyVjUHX6wCJ-D6556NUqKOxw,26847
10
+ etl/transformers.py,sha256=OwYJ_9l3oqvy2Y3-umXjAGweOIqlfRI0iSiCFPrcQ8E,17867
11
+ etl/migrations/001_create_tables.sql,sha256=YrSaZjpofC1smjYx0bM4eHQumboruIBY3fwRDlJLLSo,15749
12
+ amochka-0.4.7.dist-info/METADATA,sha256=W_0rYdYd14YBxn0hp625NmZJtVzPVOY-ERR9hdKsrBQ,7530
13
+ amochka-0.4.7.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
14
+ amochka-0.4.7.dist-info/top_level.txt,sha256=grRX8aLFG-yYKPsAqCD6sUBmdLSQeOMHsc9Dl6S7Lzo,12
15
+ amochka-0.4.7.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
etl/loaders.py CHANGED
@@ -415,12 +415,26 @@ class PostgresLoader:
415
415
  result = cursor.fetchone()
416
416
  return result[0] if result else None
417
417
 
418
- def load_transformed_contact(self, cursor, transformed: TransformedContact) -> int:
418
+ def load_transformed_contact(
419
+ self,
420
+ cursor,
421
+ transformed: TransformedContact,
422
+ user_id_map: Optional[Dict[int, int]] = None,
423
+ ) -> int:
419
424
  """Загружает полностью трансформированный контакт."""
420
425
  contacts_id = self.upsert_contact(cursor, transformed.contact)
421
426
 
422
427
  # Факты по контакту
423
428
  transformed.contact_facts["contacts_id"] = contacts_id
429
+
430
+ # Преобразуем users_id из amoCRM ID во внутренний ID (если найден).
431
+ # Если пользователя нет в маппинге — оставляем внешний ID как есть.
432
+ if user_id_map:
433
+ amo_user_id = transformed.contact_facts.get("users_id")
434
+ if amo_user_id is not None:
435
+ internal_user_id = user_id_map.get(amo_user_id)
436
+ if internal_user_id is not None:
437
+ transformed.contact_facts["users_id"] = internal_user_id
424
438
  registered_id = self._get_or_create_date_id(cursor, transformed.contact_facts.get("created_date"))
425
439
 
426
440
  cursor.execute(
etl/run_etl.py CHANGED
@@ -134,6 +134,7 @@ def sync_leads_with_contacts(
134
134
  with conn.cursor() as cursor:
135
135
  # Проверяем какие контакты уже есть в БД
136
136
  existing_contacts = loader.build_contact_id_map(cursor, mybi_account_id)
137
+ user_id_map = loader.build_user_id_map(cursor, mybi_account_id)
137
138
  missing_contact_ids = contact_ids - set(existing_contacts.keys())
138
139
 
139
140
  if missing_contact_ids:
@@ -141,7 +142,7 @@ def sync_leads_with_contacts(
141
142
 
142
143
  for contact in extractor.iter_contacts(contact_ids=list(missing_contact_ids)):
143
144
  transformed = contact_transformer.transform(contact)
144
- loader.load_transformed_contact(cursor, transformed)
145
+ loader.load_transformed_contact(cursor, transformed, user_id_map)
145
146
  contacts_loaded += 1
146
147
 
147
148
  if contacts_loaded % batch_size == 0:
@@ -292,6 +293,7 @@ def sync_contacts(
292
293
 
293
294
  with loader.connection() as conn:
294
295
  with conn.cursor() as cursor:
296
+ user_id_map = loader.build_user_id_map(cursor, mybi_account_id)
295
297
  for i, contact in enumerate(contacts_iter):
296
298
  contact_id = contact.get("id")
297
299
 
@@ -301,7 +303,7 @@ def sync_contacts(
301
303
  continue
302
304
 
303
305
  transformed = contact_transformer.transform(contact)
304
- loader.load_transformed_contact(cursor, transformed)
306
+ loader.load_transformed_contact(cursor, transformed, user_id_map)
305
307
  loaded_count += 1
306
308
 
307
309
  # Отслеживаем максимальный updated_at
@@ -1,15 +0,0 @@
1
- amochka/__init__.py,sha256=xqqZUwHZXWpqCmIOmBirxG6KoN5wrezaG-ZMT_x6ttM,925
2
- amochka/client.py,sha256=oAsFGXVIVC8IPdkbRdB-d16VFUb78FhZGhOBSFcrxJI,90171
3
- amochka/errors.py,sha256=Rg4U9srjboQwgU3wwhAed0p3vGcc0ZhuyKHIDhYFnp8,1371
4
- amochka/etl.py,sha256=tzdGPRGxR49aSAjLksic-FqechvDUTfK8ZUUJGuIU7M,8960
5
- etl/__init__.py,sha256=bp9fPqbKlOc7xzs27diHEvysy1FgBrwlpX6GnR6GL9U,255
6
- etl/config.py,sha256=BvaGn5BSGMIfvUNNsnap04iy3BHyMOuRX81G7EiLUfE,9032
7
- etl/extractors.py,sha256=PqjzlmUa8FLZa1z85mP6Y0-s_TH3REqW58632JzRKUc,13047
8
- etl/loaders.py,sha256=x8PcDQoq2kjbd52H2VzKKz5vHzyD6DXSsb9X0foPX_U,31941
9
- etl/run_etl.py,sha256=LxKQLE_yUPkg7kaFmmMxrdggz27puS1VdV9NTna9e4Q,26665
10
- etl/transformers.py,sha256=OwYJ_9l3oqvy2Y3-umXjAGweOIqlfRI0iSiCFPrcQ8E,17867
11
- etl/migrations/001_create_tables.sql,sha256=YrSaZjpofC1smjYx0bM4eHQumboruIBY3fwRDlJLLSo,15749
12
- amochka-0.4.5.dist-info/METADATA,sha256=4OROMcyke4Ffs6xli8q1FJaY4xrmvfzimubSfqegZjY,7530
13
- amochka-0.4.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
- amochka-0.4.5.dist-info/top_level.txt,sha256=grRX8aLFG-yYKPsAqCD6sUBmdLSQeOMHsc9Dl6S7Lzo,12
15
- amochka-0.4.5.dist-info/RECORD,,