amochka 0.3.0__tar.gz → 0.3.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amochka
3
- Version: 0.3.0
3
+ Version: 0.3.2
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
@@ -26,7 +26,6 @@ Classifier: Topic :: Internet :: WWW/HTTP
26
26
  Requires-Python: >=3.6
27
27
  Description-Content-Type: text/markdown
28
28
  Requires-Dist: requests>=2.25.0
29
- Requires-Dist: ratelimit>=2.2.0
30
29
  Requires-Dist: psycopg2-binary>=2.9.0
31
30
  Requires-Dist: python-dotenv>=1.0.0
32
31
 
@@ -39,88 +38,167 @@ Requires-Dist: python-dotenv>=1.0.0
39
38
  - Редактирование сделок, включая обновление стандартных и кастомных полей
40
39
  - Поддержку нескольких amoCRM-аккаунтов с персистентным кэшированием кастомных полей для каждого аккаунта отдельно
41
40
  - Ограничение запросов (7 запросов в секунду) с использованием декораторов из библиотеки `ratelimit`
41
+ - **Полнофункциональный ETL модуль** для синхронизации данных amoCRM в PostgreSQL
42
42
 
43
- ### Основные функции
43
+ ## Возможности
44
+
45
+ ### API клиент
44
46
 
45
47
  - `get_deal_by_id(deal_id)` — получение детальной информации по сделке
46
48
  - `get_pipelines()` — список воронок и статусов
47
49
  - `fetch_updated_leads_raw(pipeline_id, updated_from, ...)` — выгрузка необработанных сделок за период
48
50
 
51
+ ### ETL модуль
52
+
53
+ - **Extractors**: извлечение данных из amoCRM (сделки, контакты, события, примечания)
54
+ - **Transformers**: преобразование в табличный формат для БД
55
+ - **Loaders**: загрузка в PostgreSQL с UPSERT логикой и сохранением внутренних ID
56
+ - **Migrations**: автоматическое создание таблиц и схем
57
+ - **Incremental sync**: инкрементальная синхронизация по updated_at
58
+ - Интеграция с **Apache Airflow** для автоматизации ETL процессов
59
+
49
60
  ## Требования к окружению
50
61
 
51
- Python 3.8 или новее. Потребуются пакеты `requests` и `ratelimit`.
62
+ Python 3.6 или новее.
52
63
 
53
64
  ## Установка
54
65
 
55
- Установите зависимости командой:
56
-
57
66
  ```bash
58
- pip install requests ratelimit
67
+ pip install amochka
59
68
  ```
60
69
 
61
- Затем скопируйте репозиторий или установите пакет из PyPI (после публикации):
70
+ Для использования ETL модуля установите дополнительные зависимости:
62
71
 
63
72
  ```bash
64
- pip install amochka
73
+ pip install amochka psycopg2-binary python-dotenv
65
74
  ```
66
75
 
67
76
  ## Кэширование кастомных полей
68
77
 
69
78
  Для уменьшения количества запросов к API кастомные поля кэшируются персистентно. Если параметр cache_file не указан, имя файла кэша генерируется автоматически на основе домена amoCRM-аккаунта. Вы можете обновлять кэш принудительно, передавая параметр force_update=True в метод get_custom_fields_mapping() или настроить время жизни кэша (по умолчанию — 24 часа).
70
79
 
71
- ## Выгрузка обновленных сделок
80
+ ## Примеры использования
72
81
 
73
- Метод `fetch_updated_leads_raw()` позволяет получить все сделки из указанной воронки, которые были изменены в заданный промежуток времени. Результат можно сохранить в JSON-файл без какой‑либо обработки:
82
+ ### Быстрый старт: выгрузка обновленных сделок
74
83
 
75
84
  ```python
76
85
  from datetime import datetime, timedelta
77
86
  from amochka import AmoCRMClient, CacheConfig
78
87
 
79
88
  client = AmoCRMClient(
80
- base_url="https://bneginskogo.amocrm.ru",
81
- token_file="/path/to/token.json",
89
+ base_url="https://example.amocrm.ru",
90
+ token_file="token.json",
82
91
  cache_config=CacheConfig.disabled(),
83
92
  disable_logging=True
84
93
  )
85
94
 
86
95
  three_hours_ago = datetime.utcnow() - timedelta(hours=3)
87
- client.fetch_updated_leads_raw(6241334, updated_from=three_hours_ago, save_to_file="leads.json")
96
+ leads = client.fetch_updated_leads_raw(
97
+ pipeline_id=123456,
98
+ updated_from=three_hours_ago,
99
+ save_to_file="leads.json",
100
+ include_contacts=True
101
+ )
88
102
  ```
89
103
 
90
- Пример получаемого JSON (укороченный):
104
+ ### ETL: синхронизация в PostgreSQL
105
+
106
+ ```python
107
+ from etl.config import DatabaseConfig, AmoCRMAccount
108
+ from etl.extractors import AmoCRMExtractor
109
+ from etl.loaders import PostgresLoader
110
+ from etl.run_etl import sync_leads_with_contacts
111
+ from datetime import datetime, timezone
112
+
113
+ # Настройка БД
114
+ db_config = DatabaseConfig(
115
+ host="localhost",
116
+ port=5432,
117
+ dbname="amocrm",
118
+ user="postgres",
119
+ password="password",
120
+ schema="public"
121
+ )
122
+
123
+ # Настройка amoCRM аккаунта
124
+ account = AmoCRMAccount(
125
+ id=1,
126
+ name="main",
127
+ base_url="https://example.amocrm.ru",
128
+ token_path="token.json",
129
+ mybi_account_id=1,
130
+ pipeline_ids=[123456]
131
+ )
132
+
133
+ # ETL процесс
134
+ loader = PostgresLoader(db_config)
135
+ extractor = AmoCRMExtractor(account)
136
+
137
+ result = sync_leads_with_contacts(
138
+ extractor=extractor,
139
+ loader=loader,
140
+ mybi_account_id=1,
141
+ updated_from=datetime(2025, 1, 1, tzinfo=timezone.utc),
142
+ updated_to=datetime.now(timezone.utc),
143
+ pipeline_ids=[123456]
144
+ )
145
+
146
+ print(f"Загружено сделок: {result['leads_count']}")
147
+ print(f"Загружено контактов: {result['contacts_count']}")
148
+ ```
149
+
150
+ ### Пример структуры данных
91
151
 
92
152
  ```json
93
153
  [
94
154
  {
95
- "id": 26282337,
96
- "name": "Автосделка: Заявка от (Максим Брокер Дубай Бюро Негинского)",
155
+ "id": 12345678,
156
+ "name": "Сделка: Заявка от клиента",
97
157
  "custom_fields_values": [
98
158
  {
99
- "field_name": "roistat",
100
- "values": [{"value": "2026"}]
159
+ "field_name": "utm_source",
160
+ "values": [{"value": "google"}]
101
161
  }
102
162
  ],
103
163
  "_embedded": {
104
164
  "tags": [
105
- {"id": 179813, "name": "WZ (Федор 971568113315)"}
165
+ {"id": 123, "name": "Приоритетный клиент"}
106
166
  ]
107
167
  }
108
168
  }
109
169
  ]
110
170
  ```
111
171
 
112
- Для подключения к реальному аккаунту сохраните JSON с OAuth‑токеном и укажите его путь в параметре `token_file` при создании клиента. Базовый URL можно взять из переменной окружения `AMO_BASE_URL`.
172
+ ## Интеграция с Apache Airflow
173
+
174
+ Модуль ETL разработан для использования в Airflow DAG. Пример минимального DAG:
175
+
176
+ ```python
177
+ from airflow.decorators import dag, task
178
+ from etl.config import DatabaseConfig, AmoCRMAccount
179
+ from etl.run_etl import sync_leads_with_contacts
180
+
181
+ @dag(schedule_interval=None)
182
+ def amocrm_sync():
183
+ @task
184
+ def sync_data():
185
+ db_config = DatabaseConfig.from_env()
186
+ account = AmoCRMAccount.from_env()
187
+ # ... ETL процесс
188
+
189
+ amocrm_sync()
190
+ ```
113
191
 
114
192
  ## Тесты
115
193
 
116
- Файл `tests/test_client.py` содержит небольшой набор автоматических тестов, написанных на [pytest](https://docs.pytest.org/). Они запускают методы клиента на подставном классе `DummyClient` и проверяют, что функции работают так, как ожидается. Запустить тесты можно командой:
194
+ Запустить тесты можно командой:
117
195
 
118
196
  ```bash
119
197
  pytest -q
120
198
  ```
121
199
 
122
- Эти тесты помогают убедиться, что изменения в коде не ломают основную функциональность.
200
+ Тесты проверяют основную функциональность API клиента и помогают убедиться, что изменения в коде не ломают работу библиотеки.
123
201
 
124
- ## Пример использования `fetch_updated_leads_raw`
202
+ ## Лицензия
125
203
 
126
- Кроме примера в разделе выше, код из `example_fetch.py` демонстрирует полный процесс получения сделок и сохранения их в файл.
204
+ MIT
@@ -0,0 +1,173 @@
1
+ # amochka
2
+
3
+ Официальная документация API amocrm - https://www.amocrm.ru/developers/content/crm_platform/api-reference
4
+
5
+ **amochka** — библиотека для работы с API amoCRM на Python. Она поддерживает:
6
+ - Получение данных сделок с вложенными сущностями (контакты, компании, теги, и т.д.)
7
+ - Редактирование сделок, включая обновление стандартных и кастомных полей
8
+ - Поддержку нескольких amoCRM-аккаунтов с персистентным кэшированием кастомных полей для каждого аккаунта отдельно
9
+ - Ограничение запросов (7 запросов в секунду) с использованием декораторов из библиотеки `ratelimit`
10
+ - **Полнофункциональный ETL модуль** для синхронизации данных amoCRM в PostgreSQL
11
+
12
+ ## Возможности
13
+
14
+ ### API клиент
15
+
16
+ - `get_deal_by_id(deal_id)` — получение детальной информации по сделке
17
+ - `get_pipelines()` — список воронок и статусов
18
+ - `fetch_updated_leads_raw(pipeline_id, updated_from, ...)` — выгрузка необработанных сделок за период
19
+
20
+ ### ETL модуль
21
+
22
+ - **Extractors**: извлечение данных из amoCRM (сделки, контакты, события, примечания)
23
+ - **Transformers**: преобразование в табличный формат для БД
24
+ - **Loaders**: загрузка в PostgreSQL с UPSERT логикой и сохранением внутренних ID
25
+ - **Migrations**: автоматическое создание таблиц и схем
26
+ - **Incremental sync**: инкрементальная синхронизация по updated_at
27
+ - Интеграция с **Apache Airflow** для автоматизации ETL процессов
28
+
29
+ ## Требования к окружению
30
+
31
+ Python 3.6 или новее.
32
+
33
+ ## Установка
34
+
35
+ ```bash
36
+ pip install amochka
37
+ ```
38
+
39
+ Для использования ETL модуля установите дополнительные зависимости:
40
+
41
+ ```bash
42
+ pip install amochka psycopg2-binary python-dotenv
43
+ ```
44
+
45
+ ## Кэширование кастомных полей
46
+
47
+ Для уменьшения количества запросов к API кастомные поля кэшируются персистентно. Если параметр cache_file не указан, имя файла кэша генерируется автоматически на основе домена amoCRM-аккаунта. Вы можете обновлять кэш принудительно, передавая параметр force_update=True в метод get_custom_fields_mapping() или настроить время жизни кэша (по умолчанию — 24 часа).
48
+
49
+ ## Примеры использования
50
+
51
+ ### Быстрый старт: выгрузка обновленных сделок
52
+
53
+ ```python
54
+ from datetime import datetime, timedelta
55
+ from amochka import AmoCRMClient, CacheConfig
56
+
57
+ client = AmoCRMClient(
58
+ base_url="https://example.amocrm.ru",
59
+ token_file="token.json",
60
+ cache_config=CacheConfig.disabled(),
61
+ disable_logging=True
62
+ )
63
+
64
+ three_hours_ago = datetime.utcnow() - timedelta(hours=3)
65
+ leads = client.fetch_updated_leads_raw(
66
+ pipeline_id=123456,
67
+ updated_from=three_hours_ago,
68
+ save_to_file="leads.json",
69
+ include_contacts=True
70
+ )
71
+ ```
72
+
73
+ ### ETL: синхронизация в PostgreSQL
74
+
75
+ ```python
76
+ from etl.config import DatabaseConfig, AmoCRMAccount
77
+ from etl.extractors import AmoCRMExtractor
78
+ from etl.loaders import PostgresLoader
79
+ from etl.run_etl import sync_leads_with_contacts
80
+ from datetime import datetime, timezone
81
+
82
+ # Настройка БД
83
+ db_config = DatabaseConfig(
84
+ host="localhost",
85
+ port=5432,
86
+ dbname="amocrm",
87
+ user="postgres",
88
+ password="password",
89
+ schema="public"
90
+ )
91
+
92
+ # Настройка amoCRM аккаунта
93
+ account = AmoCRMAccount(
94
+ id=1,
95
+ name="main",
96
+ base_url="https://example.amocrm.ru",
97
+ token_path="token.json",
98
+ mybi_account_id=1,
99
+ pipeline_ids=[123456]
100
+ )
101
+
102
+ # ETL процесс
103
+ loader = PostgresLoader(db_config)
104
+ extractor = AmoCRMExtractor(account)
105
+
106
+ result = sync_leads_with_contacts(
107
+ extractor=extractor,
108
+ loader=loader,
109
+ mybi_account_id=1,
110
+ updated_from=datetime(2025, 1, 1, tzinfo=timezone.utc),
111
+ updated_to=datetime.now(timezone.utc),
112
+ pipeline_ids=[123456]
113
+ )
114
+
115
+ print(f"Загружено сделок: {result['leads_count']}")
116
+ print(f"Загружено контактов: {result['contacts_count']}")
117
+ ```
118
+
119
+ ### Пример структуры данных
120
+
121
+ ```json
122
+ [
123
+ {
124
+ "id": 12345678,
125
+ "name": "Сделка: Заявка от клиента",
126
+ "custom_fields_values": [
127
+ {
128
+ "field_name": "utm_source",
129
+ "values": [{"value": "google"}]
130
+ }
131
+ ],
132
+ "_embedded": {
133
+ "tags": [
134
+ {"id": 123, "name": "Приоритетный клиент"}
135
+ ]
136
+ }
137
+ }
138
+ ]
139
+ ```
140
+
141
+ ## Интеграция с Apache Airflow
142
+
143
+ Модуль ETL разработан для использования в Airflow DAG. Пример минимального DAG:
144
+
145
+ ```python
146
+ from airflow.decorators import dag, task
147
+ from etl.config import DatabaseConfig, AmoCRMAccount
148
+ from etl.run_etl import sync_leads_with_contacts
149
+
150
+ @dag(schedule_interval=None)
151
+ def amocrm_sync():
152
+ @task
153
+ def sync_data():
154
+ db_config = DatabaseConfig.from_env()
155
+ account = AmoCRMAccount.from_env()
156
+ # ... ETL процесс
157
+
158
+ amocrm_sync()
159
+ ```
160
+
161
+ ## Тесты
162
+
163
+ Запустить тесты можно командой:
164
+
165
+ ```bash
166
+ pytest -q
167
+ ```
168
+
169
+ Тесты проверяют основную функциональность API клиента и помогают убедиться, что изменения в коде не ломают работу библиотеки.
170
+
171
+ ## Лицензия
172
+
173
+ MIT
@@ -5,7 +5,7 @@ import requests
5
5
  import logging
6
6
  from datetime import datetime
7
7
  from typing import Iterator, List, Optional, Sequence, Union
8
- from ratelimit import limits, sleep_and_retry
8
+ # ratelimit больше не используется - rate limiting реализован вручную через self.rate_limit
9
9
 
10
10
  # Создаём базовый логгер
11
11
  logger = logging.getLogger(__name__)
@@ -15,7 +15,7 @@ if not logger.handlers:
15
15
  ch.setFormatter(formatter)
16
16
  logger.addHandler(ch)
17
17
 
18
- RATE_LIMIT = 7 # Максимум 7 запросов в секунду
18
+ DEFAULT_RATE_LIMIT = 7 # Максимум запросов в секунду по умолчанию
19
19
 
20
20
  class Deal(dict):
21
21
  """
@@ -262,12 +262,13 @@ class AmoCRMClient:
262
262
  либо полностью отключить логирование, установив disable_logging=True.
263
263
  """
264
264
  def __init__(
265
- self,
266
- base_url,
267
- token_file=None,
268
- cache_config=None,
269
- log_level=logging.INFO,
265
+ self,
266
+ base_url,
267
+ token_file=None,
268
+ cache_config=None,
269
+ log_level=logging.INFO,
270
270
  disable_logging=False,
271
+ rate_limit: Optional[int] = None,
271
272
  *,
272
273
  client_id: Optional[str] = None,
273
274
  client_secret: Optional[str] = None,
@@ -275,14 +276,17 @@ class AmoCRMClient:
275
276
  ):
276
277
  """
277
278
  Инициализирует клиента, задавая базовый URL, токен авторизации и настройки кэша для кастомных полей.
278
-
279
+
279
280
  :param base_url: Базовый URL API amoCRM.
280
281
  :param token_file: Файл, содержащий токен авторизации.
281
282
  :param cache_config: Конфигурация кэширования (объект CacheConfig или None для значений по умолчанию)
282
283
  :param log_level: Уровень логирования (например, logging.DEBUG, logging.INFO).
283
284
  :param disable_logging: Если True, логирование будет отключено.
285
+ :param rate_limit: Максимальное количество запросов в секунду (по умолчанию 7).
284
286
  """
285
287
  self.base_url = base_url.rstrip('/')
288
+ self.rate_limit = rate_limit if rate_limit is not None else DEFAULT_RATE_LIMIT
289
+ self._request_times: List[float] = [] # Для отслеживания времени запросов
286
290
  domain = self.base_url.split("//")[-1].split(".")[0]
287
291
  self.domain = domain
288
292
  self.token_file = token_file or os.path.join(os.path.expanduser('~'), '.amocrm_token.json')
@@ -378,8 +382,24 @@ class AmoCRMClient:
378
382
 
379
383
  raise Exception("Токен истёк или некорректен, и нет данных для refresh_token. Обновите токен.")
380
384
 
381
- @sleep_and_retry
382
- @limits(calls=RATE_LIMIT, period=1)
385
+ def _wait_for_rate_limit(self):
386
+ """Ожидает, если превышен лимит запросов в секунду."""
387
+ now = time.time()
388
+ # Удаляем запросы старше 1 секунды
389
+ self._request_times = [t for t in self._request_times if now - t < 1.0]
390
+
391
+ if len(self._request_times) >= self.rate_limit:
392
+ # Ждём до освобождения слота
393
+ sleep_time = 1.0 - (now - self._request_times[0])
394
+ if sleep_time > 0:
395
+ self.logger.debug(f"Rate limit: ожидание {sleep_time:.3f}с (лимит {self.rate_limit} req/s)")
396
+ time.sleep(sleep_time)
397
+ # Очищаем старые записи после ожидания
398
+ now = time.time()
399
+ self._request_times = [t for t in self._request_times if now - t < 1.0]
400
+
401
+ self._request_times.append(time.time())
402
+
383
403
  def _make_request(self, method, endpoint, params=None, data=None, timeout=10):
384
404
  """
385
405
  Выполняет HTTP-запрос к API amoCRM с учетом ограничения по скорости (rate limit).
@@ -392,6 +412,9 @@ class AmoCRMClient:
392
412
  :return: Ответ в формате JSON или None (если статус 204).
393
413
  :raises Exception: При получении кода ошибки, отличного от 200/204.
394
414
  """
415
+ # Ручной rate limiting
416
+ self._wait_for_rate_limit()
417
+
395
418
  url = f"{self.base_url}{endpoint}"
396
419
  headers = {
397
420
  "Authorization": f"Bearer {self.token}",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amochka
3
- Version: 0.3.0
3
+ Version: 0.3.2
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
@@ -26,7 +26,6 @@ Classifier: Topic :: Internet :: WWW/HTTP
26
26
  Requires-Python: >=3.6
27
27
  Description-Content-Type: text/markdown
28
28
  Requires-Dist: requests>=2.25.0
29
- Requires-Dist: ratelimit>=2.2.0
30
29
  Requires-Dist: psycopg2-binary>=2.9.0
31
30
  Requires-Dist: python-dotenv>=1.0.0
32
31
 
@@ -39,88 +38,167 @@ Requires-Dist: python-dotenv>=1.0.0
39
38
  - Редактирование сделок, включая обновление стандартных и кастомных полей
40
39
  - Поддержку нескольких amoCRM-аккаунтов с персистентным кэшированием кастомных полей для каждого аккаунта отдельно
41
40
  - Ограничение запросов (7 запросов в секунду) с использованием декораторов из библиотеки `ratelimit`
41
+ - **Полнофункциональный ETL модуль** для синхронизации данных amoCRM в PostgreSQL
42
42
 
43
- ### Основные функции
43
+ ## Возможности
44
+
45
+ ### API клиент
44
46
 
45
47
  - `get_deal_by_id(deal_id)` — получение детальной информации по сделке
46
48
  - `get_pipelines()` — список воронок и статусов
47
49
  - `fetch_updated_leads_raw(pipeline_id, updated_from, ...)` — выгрузка необработанных сделок за период
48
50
 
51
+ ### ETL модуль
52
+
53
+ - **Extractors**: извлечение данных из amoCRM (сделки, контакты, события, примечания)
54
+ - **Transformers**: преобразование в табличный формат для БД
55
+ - **Loaders**: загрузка в PostgreSQL с UPSERT логикой и сохранением внутренних ID
56
+ - **Migrations**: автоматическое создание таблиц и схем
57
+ - **Incremental sync**: инкрементальная синхронизация по updated_at
58
+ - Интеграция с **Apache Airflow** для автоматизации ETL процессов
59
+
49
60
  ## Требования к окружению
50
61
 
51
- Python 3.8 или новее. Потребуются пакеты `requests` и `ratelimit`.
62
+ Python 3.6 или новее.
52
63
 
53
64
  ## Установка
54
65
 
55
- Установите зависимости командой:
56
-
57
66
  ```bash
58
- pip install requests ratelimit
67
+ pip install amochka
59
68
  ```
60
69
 
61
- Затем скопируйте репозиторий или установите пакет из PyPI (после публикации):
70
+ Для использования ETL модуля установите дополнительные зависимости:
62
71
 
63
72
  ```bash
64
- pip install amochka
73
+ pip install amochka psycopg2-binary python-dotenv
65
74
  ```
66
75
 
67
76
  ## Кэширование кастомных полей
68
77
 
69
78
  Для уменьшения количества запросов к API кастомные поля кэшируются персистентно. Если параметр cache_file не указан, имя файла кэша генерируется автоматически на основе домена amoCRM-аккаунта. Вы можете обновлять кэш принудительно, передавая параметр force_update=True в метод get_custom_fields_mapping() или настроить время жизни кэша (по умолчанию — 24 часа).
70
79
 
71
- ## Выгрузка обновленных сделок
80
+ ## Примеры использования
72
81
 
73
- Метод `fetch_updated_leads_raw()` позволяет получить все сделки из указанной воронки, которые были изменены в заданный промежуток времени. Результат можно сохранить в JSON-файл без какой‑либо обработки:
82
+ ### Быстрый старт: выгрузка обновленных сделок
74
83
 
75
84
  ```python
76
85
  from datetime import datetime, timedelta
77
86
  from amochka import AmoCRMClient, CacheConfig
78
87
 
79
88
  client = AmoCRMClient(
80
- base_url="https://bneginskogo.amocrm.ru",
81
- token_file="/path/to/token.json",
89
+ base_url="https://example.amocrm.ru",
90
+ token_file="token.json",
82
91
  cache_config=CacheConfig.disabled(),
83
92
  disable_logging=True
84
93
  )
85
94
 
86
95
  three_hours_ago = datetime.utcnow() - timedelta(hours=3)
87
- client.fetch_updated_leads_raw(6241334, updated_from=three_hours_ago, save_to_file="leads.json")
96
+ leads = client.fetch_updated_leads_raw(
97
+ pipeline_id=123456,
98
+ updated_from=three_hours_ago,
99
+ save_to_file="leads.json",
100
+ include_contacts=True
101
+ )
88
102
  ```
89
103
 
90
- Пример получаемого JSON (укороченный):
104
+ ### ETL: синхронизация в PostgreSQL
105
+
106
+ ```python
107
+ from etl.config import DatabaseConfig, AmoCRMAccount
108
+ from etl.extractors import AmoCRMExtractor
109
+ from etl.loaders import PostgresLoader
110
+ from etl.run_etl import sync_leads_with_contacts
111
+ from datetime import datetime, timezone
112
+
113
+ # Настройка БД
114
+ db_config = DatabaseConfig(
115
+ host="localhost",
116
+ port=5432,
117
+ dbname="amocrm",
118
+ user="postgres",
119
+ password="password",
120
+ schema="public"
121
+ )
122
+
123
+ # Настройка amoCRM аккаунта
124
+ account = AmoCRMAccount(
125
+ id=1,
126
+ name="main",
127
+ base_url="https://example.amocrm.ru",
128
+ token_path="token.json",
129
+ mybi_account_id=1,
130
+ pipeline_ids=[123456]
131
+ )
132
+
133
+ # ETL процесс
134
+ loader = PostgresLoader(db_config)
135
+ extractor = AmoCRMExtractor(account)
136
+
137
+ result = sync_leads_with_contacts(
138
+ extractor=extractor,
139
+ loader=loader,
140
+ mybi_account_id=1,
141
+ updated_from=datetime(2025, 1, 1, tzinfo=timezone.utc),
142
+ updated_to=datetime.now(timezone.utc),
143
+ pipeline_ids=[123456]
144
+ )
145
+
146
+ print(f"Загружено сделок: {result['leads_count']}")
147
+ print(f"Загружено контактов: {result['contacts_count']}")
148
+ ```
149
+
150
+ ### Пример структуры данных
91
151
 
92
152
  ```json
93
153
  [
94
154
  {
95
- "id": 26282337,
96
- "name": "Автосделка: Заявка от (Максим Брокер Дубай Бюро Негинского)",
155
+ "id": 12345678,
156
+ "name": "Сделка: Заявка от клиента",
97
157
  "custom_fields_values": [
98
158
  {
99
- "field_name": "roistat",
100
- "values": [{"value": "2026"}]
159
+ "field_name": "utm_source",
160
+ "values": [{"value": "google"}]
101
161
  }
102
162
  ],
103
163
  "_embedded": {
104
164
  "tags": [
105
- {"id": 179813, "name": "WZ (Федор 971568113315)"}
165
+ {"id": 123, "name": "Приоритетный клиент"}
106
166
  ]
107
167
  }
108
168
  }
109
169
  ]
110
170
  ```
111
171
 
112
- Для подключения к реальному аккаунту сохраните JSON с OAuth‑токеном и укажите его путь в параметре `token_file` при создании клиента. Базовый URL можно взять из переменной окружения `AMO_BASE_URL`.
172
+ ## Интеграция с Apache Airflow
173
+
174
+ Модуль ETL разработан для использования в Airflow DAG. Пример минимального DAG:
175
+
176
+ ```python
177
+ from airflow.decorators import dag, task
178
+ from etl.config import DatabaseConfig, AmoCRMAccount
179
+ from etl.run_etl import sync_leads_with_contacts
180
+
181
+ @dag(schedule_interval=None)
182
+ def amocrm_sync():
183
+ @task
184
+ def sync_data():
185
+ db_config = DatabaseConfig.from_env()
186
+ account = AmoCRMAccount.from_env()
187
+ # ... ETL процесс
188
+
189
+ amocrm_sync()
190
+ ```
113
191
 
114
192
  ## Тесты
115
193
 
116
- Файл `tests/test_client.py` содержит небольшой набор автоматических тестов, написанных на [pytest](https://docs.pytest.org/). Они запускают методы клиента на подставном классе `DummyClient` и проверяют, что функции работают так, как ожидается. Запустить тесты можно командой:
194
+ Запустить тесты можно командой:
117
195
 
118
196
  ```bash
119
197
  pytest -q
120
198
  ```
121
199
 
122
- Эти тесты помогают убедиться, что изменения в коде не ломают основную функциональность.
200
+ Тесты проверяют основную функциональность API клиента и помогают убедиться, что изменения в коде не ломают работу библиотеки.
123
201
 
124
- ## Пример использования `fetch_updated_leads_raw`
202
+ ## Лицензия
125
203
 
126
- Кроме примера в разделе выше, код из `example_fetch.py` демонстрирует полный процесс получения сделок и сохранения их в файл.
204
+ MIT
@@ -1,4 +1,3 @@
1
1
  requests>=2.25.0
2
- ratelimit>=2.2.0
3
2
  psycopg2-binary>=2.9.0
4
3
  python-dotenv>=1.0.0
@@ -107,6 +107,7 @@ class AmoCRMAccount:
107
107
  mybi_account_id: int # Внутренний account_id как в mybi.ru (для совместимости)
108
108
  pipeline_ids: Optional[List[int]] = None # None = все воронки
109
109
  cache_dir: Optional[Path] = None
110
+ rate_limit: int = 7 # Максимум запросов в секунду (по умолчанию 7)
110
111
 
111
112
  def __post_init__(self):
112
113
  if isinstance(self.token_path, str):
@@ -125,6 +126,7 @@ class AmoCRMAccount:
125
126
  mybi_account_id=int(d.get("mybi_account_id", 0)),
126
127
  pipeline_ids=d.get("pipeline_ids"),
127
128
  cache_dir=Path(d.get("cache_dir", ".cache")) if d.get("cache_dir") else None,
129
+ rate_limit=int(d.get("rate_limit", 7)),
128
130
  )
129
131
 
130
132
 
@@ -48,6 +48,7 @@ class AmoCRMExtractor:
48
48
  base_url=self.account.base_url,
49
49
  token_file=str(self.account.token_path),
50
50
  cache_config=cache_config,
51
+ rate_limit=self.account.rate_limit,
51
52
  )
52
53
 
53
54
  @property
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "amochka"
7
- version = "0.3.0"
7
+ version = "0.3.2"
8
8
  description = "Python library for working with amoCRM API with ETL capabilities"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -31,7 +31,6 @@ keywords = ["amocrm", "crm", "api", "client", "automation", "etl"]
31
31
  requires-python = ">=3.6"
32
32
  dependencies = [
33
33
  "requests>=2.25.0",
34
- "ratelimit>=2.2.0",
35
34
  "psycopg2-binary>=2.9.0",
36
35
  "python-dotenv>=1.0.0"
37
36
  ]
amochka-0.3.0/README.md DELETED
@@ -1,94 +0,0 @@
1
- # amochka
2
-
3
- Официальная документация API amocrm - https://www.amocrm.ru/developers/content/crm_platform/api-reference
4
-
5
- **amochka** — библиотека для работы с API amoCRM на Python. Она поддерживает:
6
- - Получение данных сделок с вложенными сущностями (контакты, компании, теги, и т.д.)
7
- - Редактирование сделок, включая обновление стандартных и кастомных полей
8
- - Поддержку нескольких amoCRM-аккаунтов с персистентным кэшированием кастомных полей для каждого аккаунта отдельно
9
- - Ограничение запросов (7 запросов в секунду) с использованием декораторов из библиотеки `ratelimit`
10
-
11
- ### Основные функции
12
-
13
- - `get_deal_by_id(deal_id)` — получение детальной информации по сделке
14
- - `get_pipelines()` — список воронок и статусов
15
- - `fetch_updated_leads_raw(pipeline_id, updated_from, ...)` — выгрузка необработанных сделок за период
16
-
17
- ## Требования к окружению
18
-
19
- Python 3.8 или новее. Потребуются пакеты `requests` и `ratelimit`.
20
-
21
- ## Установка
22
-
23
- Установите зависимости командой:
24
-
25
- ```bash
26
- pip install requests ratelimit
27
- ```
28
-
29
- Затем скопируйте репозиторий или установите пакет из PyPI (после публикации):
30
-
31
- ```bash
32
- pip install amochka
33
- ```
34
-
35
- ## Кэширование кастомных полей
36
-
37
- Для уменьшения количества запросов к API кастомные поля кэшируются персистентно. Если параметр cache_file не указан, имя файла кэша генерируется автоматически на основе домена amoCRM-аккаунта. Вы можете обновлять кэш принудительно, передавая параметр force_update=True в метод get_custom_fields_mapping() или настроить время жизни кэша (по умолчанию — 24 часа).
38
-
39
- ## Выгрузка обновленных сделок
40
-
41
- Метод `fetch_updated_leads_raw()` позволяет получить все сделки из указанной воронки, которые были изменены в заданный промежуток времени. Результат можно сохранить в JSON-файл без какой‑либо обработки:
42
-
43
- ```python
44
- from datetime import datetime, timedelta
45
- from amochka import AmoCRMClient, CacheConfig
46
-
47
- client = AmoCRMClient(
48
- base_url="https://bneginskogo.amocrm.ru",
49
- token_file="/path/to/token.json",
50
- cache_config=CacheConfig.disabled(),
51
- disable_logging=True
52
- )
53
-
54
- three_hours_ago = datetime.utcnow() - timedelta(hours=3)
55
- client.fetch_updated_leads_raw(6241334, updated_from=three_hours_ago, save_to_file="leads.json")
56
- ```
57
-
58
- Пример получаемого JSON (укороченный):
59
-
60
- ```json
61
- [
62
- {
63
- "id": 26282337,
64
- "name": "Автосделка: Заявка от (Максим Брокер Дубай Бюро Негинского)",
65
- "custom_fields_values": [
66
- {
67
- "field_name": "roistat",
68
- "values": [{"value": "2026"}]
69
- }
70
- ],
71
- "_embedded": {
72
- "tags": [
73
- {"id": 179813, "name": "WZ (Федор 971568113315)"}
74
- ]
75
- }
76
- }
77
- ]
78
- ```
79
-
80
- Для подключения к реальному аккаунту сохраните JSON с OAuth‑токеном и укажите его путь в параметре `token_file` при создании клиента. Базовый URL можно взять из переменной окружения `AMO_BASE_URL`.
81
-
82
- ## Тесты
83
-
84
- Файл `tests/test_client.py` содержит небольшой набор автоматических тестов, написанных на [pytest](https://docs.pytest.org/). Они запускают методы клиента на подставном классе `DummyClient` и проверяют, что функции работают так, как ожидается. Запустить тесты можно командой:
85
-
86
- ```bash
87
- pytest -q
88
- ```
89
-
90
- Эти тесты помогают убедиться, что изменения в коде не ломают основную функциональность.
91
-
92
- ## Пример использования `fetch_updated_leads_raw`
93
-
94
- Кроме примера в разделе выше, код из `example_fetch.py` демонстрирует полный процесс получения сделок и сохранения их в файл.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes