amochka 0.1.7__py3-none-any.whl → 0.1.9__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/etl.py DELETED
@@ -1,302 +0,0 @@
1
- import json
2
- from pathlib import Path
3
- from typing import Callable, Iterable, List, Optional, Sequence, Set, Union
4
-
5
- from .client import AmoCRMClient
6
-
7
-
8
- def _ensure_path(path: Union[str, Path]) -> Path:
9
- output_path = Path(path)
10
- output_path.parent.mkdir(parents=True, exist_ok=True)
11
- return output_path
12
-
13
-
14
- def _resolve_timestamp(record: dict, timestamp_fields: Sequence[str]) -> Optional[Union[int, float, str]]:
15
- for field in timestamp_fields:
16
- if not field:
17
- continue
18
- value = record.get(field)
19
- if value is not None:
20
- return value
21
- return None
22
-
23
-
24
- def write_ndjson(
25
- records: Iterable[dict],
26
- output_path: Union[str, Path],
27
- *,
28
- entity: str,
29
- account_id: Optional[Union[int, str]] = None,
30
- timestamp_fields: Sequence[str] = ("updated_at", "created_at"),
31
- transform: Optional[Callable[[dict], dict]] = None,
32
- on_record: Optional[Callable[[dict], None]] = None,
33
- ) -> int:
34
- """
35
- Записывает переданные записи в формат NDJSON.
36
-
37
- Возвращает количество записанных строк.
38
- """
39
- path = _ensure_path(output_path)
40
- count = 0
41
- with path.open("w", encoding="utf-8") as handler:
42
- for original in records:
43
- payload = transform(original) if transform else original
44
- timestamp = _resolve_timestamp(original, timestamp_fields)
45
- line = {
46
- "entity": entity,
47
- "account_id": account_id,
48
- "updated_at": timestamp,
49
- "payload": payload,
50
- }
51
- handler.write(json.dumps(line, ensure_ascii=False))
52
- handler.write("\n")
53
- count += 1
54
- if on_record:
55
- on_record(original)
56
- return count
57
-
58
-
59
- def export_leads_to_ndjson(
60
- client: AmoCRMClient,
61
- output_path: Union[str, Path],
62
- account_id: Union[int, str],
63
- *,
64
- start=None,
65
- end=None,
66
- pipeline_ids=None,
67
- include_contacts: bool = True,
68
- include=None,
69
- limit: int = 250,
70
- extra_params: Optional[dict] = None,
71
- on_record: Optional[Callable[[dict], None]] = None,
72
- ) -> int:
73
- """
74
- Выгружает сделки и записывает их в NDJSON.
75
- """
76
- records = client.iter_leads(
77
- updated_from=start,
78
- updated_to=end,
79
- pipeline_ids=pipeline_ids,
80
- include_contacts=include_contacts,
81
- include=include,
82
- limit=limit,
83
- extra_params=extra_params,
84
- )
85
- return write_ndjson(
86
- records,
87
- output_path,
88
- entity="lead",
89
- account_id=account_id,
90
- timestamp_fields=("updated_at", "created_at"),
91
- on_record=on_record,
92
- )
93
-
94
-
95
- def export_contacts_to_ndjson(
96
- client: AmoCRMClient,
97
- output_path: Union[str, Path],
98
- account_id: Union[int, str],
99
- *,
100
- start=None,
101
- end=None,
102
- contact_ids=None,
103
- limit: int = 250,
104
- extra_params: Optional[dict] = None,
105
- on_record: Optional[Callable[[dict], None]] = None,
106
- ) -> int:
107
- """
108
- Выгружает контакты и записывает их в NDJSON.
109
- """
110
- contact_id_list: Optional[List[int]] = None
111
- if contact_ids is not None:
112
- if isinstance(contact_ids, (list, tuple, set)):
113
- contact_id_list = [int(cid) for cid in contact_ids if cid is not None]
114
- else:
115
- contact_id_list = [int(contact_ids)]
116
-
117
- def _iter_contacts():
118
- seen: Set[int] = set()
119
- if contact_id_list:
120
- params = dict(extra_params or {})
121
- params["filter[id][]"] = [str(cid) for cid in contact_id_list]
122
- params["page"] = 1
123
- params["limit"] = limit
124
- while True:
125
- response = client._make_request("GET", "/api/v4/contacts", params=params)
126
- embedded = (response or {}).get("_embedded", {})
127
- contacts = embedded.get("contacts") or []
128
- if not contacts:
129
- break
130
- for contact in contacts:
131
- cid = contact.get("id")
132
- if cid is not None:
133
- seen.add(int(cid))
134
- yield contact
135
- total_pages = response.get("_page_count", params["page"])
136
- if params["page"] >= total_pages:
137
- break
138
- params["page"] += 1
139
- else:
140
- for contact in client.iter_contacts(
141
- updated_from=start,
142
- updated_to=end,
143
- contact_ids=None,
144
- limit=limit,
145
- extra_params=extra_params,
146
- ):
147
- cid = contact.get("id")
148
- if cid is not None:
149
- seen.add(int(cid))
150
- yield contact
151
-
152
- if contact_id_list:
153
- missing = [cid for cid in contact_id_list if cid not in seen]
154
- for cid in missing:
155
- try:
156
- contact = client.get_contact_by_id(cid)
157
- except Exception:
158
- continue
159
- retrieved_id = contact.get("id")
160
- if retrieved_id is not None and int(retrieved_id) not in seen:
161
- seen.add(int(retrieved_id))
162
- yield contact
163
-
164
- return write_ndjson(
165
- _iter_contacts(),
166
- output_path,
167
- entity="contact",
168
- account_id=account_id,
169
- timestamp_fields=("updated_at", "created_at"),
170
- on_record=on_record,
171
- )
172
-
173
-
174
- def export_notes_to_ndjson(
175
- client: AmoCRMClient,
176
- output_path: Union[str, Path],
177
- account_id: Union[int, str],
178
- *,
179
- entity: str = "lead",
180
- start=None,
181
- end=None,
182
- note_type=None,
183
- entity_ids=None,
184
- limit: int = 250,
185
- extra_params: Optional[dict] = None,
186
- on_record: Optional[Callable[[dict], None]] = None,
187
- ) -> int:
188
- """
189
- Выгружает примечания и записывает их в NDJSON.
190
- """
191
- records = client.iter_notes(
192
- entity=entity,
193
- updated_from=start,
194
- updated_to=end,
195
- note_type=note_type,
196
- entity_ids=entity_ids,
197
- limit=limit,
198
- extra_params=extra_params,
199
- )
200
- entity_name = f"{entity}_note" if entity else "note"
201
- return write_ndjson(
202
- records,
203
- output_path,
204
- entity=entity_name,
205
- account_id=account_id,
206
- timestamp_fields=("updated_at", "created_at"),
207
- on_record=on_record,
208
- )
209
-
210
-
211
- def export_events_to_ndjson(
212
- client: AmoCRMClient,
213
- output_path: Union[str, Path],
214
- account_id: Union[int, str],
215
- *,
216
- entity: Optional[str] = "lead",
217
- start=None,
218
- end=None,
219
- event_type=None,
220
- entity_ids=None,
221
- limit: int = 250,
222
- extra_params: Optional[dict] = None,
223
- on_record: Optional[Callable[[dict], None]] = None,
224
- ) -> int:
225
- """
226
- Выгружает события и записывает их в NDJSON.
227
- """
228
- records = client.iter_events(
229
- entity=entity,
230
- entity_ids=entity_ids,
231
- event_type=event_type,
232
- created_from=start,
233
- created_to=end,
234
- limit=limit,
235
- extra_params=extra_params,
236
- )
237
- entity_name = f"{entity}_event" if entity else "event"
238
- return write_ndjson(
239
- records,
240
- output_path,
241
- entity=entity_name,
242
- account_id=account_id,
243
- timestamp_fields=("created_at", "updated_at"),
244
- on_record=on_record,
245
- )
246
-
247
-
248
- def export_users_to_ndjson(
249
- client: AmoCRMClient,
250
- output_path: Union[str, Path],
251
- account_id: Union[int, str],
252
- *,
253
- limit: int = 250,
254
- extra_params: Optional[dict] = None,
255
- on_record: Optional[Callable[[dict], None]] = None,
256
- ) -> int:
257
- """
258
- Выгружает пользователей и записывает их в NDJSON.
259
- """
260
- records = client.iter_users(limit=limit, extra_params=extra_params)
261
- return write_ndjson(
262
- records,
263
- output_path,
264
- entity="user",
265
- account_id=account_id,
266
- timestamp_fields=("updated_at", "created_at"),
267
- on_record=on_record,
268
- )
269
-
270
-
271
- def export_pipelines_to_ndjson(
272
- client: AmoCRMClient,
273
- output_path: Union[str, Path],
274
- account_id: Union[int, str],
275
- *,
276
- limit: int = 250,
277
- extra_params: Optional[dict] = None,
278
- on_record: Optional[Callable[[dict], None]] = None,
279
- ) -> int:
280
- """
281
- Выгружает воронки и записывает их в NDJSON.
282
- """
283
- records = client.iter_pipelines(limit=limit, extra_params=extra_params)
284
- return write_ndjson(
285
- records,
286
- output_path,
287
- entity="pipeline",
288
- account_id=account_id,
289
- timestamp_fields=("updated_at", "created_at"),
290
- on_record=on_record,
291
- )
292
-
293
-
294
- __all__ = [
295
- "write_ndjson",
296
- "export_leads_to_ndjson",
297
- "export_contacts_to_ndjson",
298
- "export_notes_to_ndjson",
299
- "export_events_to_ndjson",
300
- "export_users_to_ndjson",
301
- "export_pipelines_to_ndjson",
302
- ]
@@ -1,40 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: amochka
3
- Version: 0.1.7
4
- Summary: Библиотека для работы с API amoCRM
5
- Home-page:
6
- Author: Timurka
7
- Author-email: timurdt@gmail.com
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.6
12
- Description-Content-Type: text/markdown
13
- Requires-Dist: requests
14
- Requires-Dist: ratelimit
15
- Dynamic: author
16
- Dynamic: author-email
17
- Dynamic: classifier
18
- Dynamic: description
19
- Dynamic: description-content-type
20
- Dynamic: requires-dist
21
- Dynamic: requires-python
22
- Dynamic: summary
23
-
24
- # amochka
25
-
26
- **amochka** — библиотека для работы с API amoCRM на Python. Она поддерживает:
27
- - Получение данных сделок с вложенными сущностями (контакты, компании, теги, и т.д.)
28
- - Редактирование сделок, включая обновление стандартных и кастомных полей
29
- - Поддержку нескольких amoCRM-аккаунтов с персистентным кэшированием кастомных полей для каждого аккаунта отдельно
30
- - Ограничение запросов (7 запросов в секунду) с использованием декораторов из библиотеки `ratelimit`
31
-
32
- ## Установка
33
-
34
- Установить библиотеку можно из PyPI (после публикации):
35
-
36
- pip install amochka
37
-
38
- ## Кэширование кастомных полей
39
-
40
- Для уменьшения количества запросов к API кастомные поля кэшируются персистентно. Если параметр cache_file не указан, имя файла кэша генерируется автоматически на основе домена amoCRM-аккаунта. Вы можете обновлять кэш принудительно, передавая параметр force_update=True в метод get_custom_fields_mapping() или настроить время жизни кэша (по умолчанию — 24 часа).
@@ -1,7 +0,0 @@
1
- amochka/__init__.py,sha256=RdQyNTzygG3l4X52op5afzvHjEjvYJB_yZz-jVd8R54,620
2
- amochka/client.py,sha256=hRO7e0kGmvyw1RZR9hMXBQnwrH7n-7m-izCqjag_QS4,50047
3
- amochka/etl.py,sha256=N8rXNFbtmlKfsYpgr7HDcP4enoj63XQPWuTDxGuMhw4,8901
4
- amochka-0.1.7.dist-info/METADATA,sha256=RovMukJ-TfsjV4UHCb3_P098JkbyeHs_ETfZdwDlFi0,2218
5
- amochka-0.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
- amochka-0.1.7.dist-info/top_level.txt,sha256=y5qXFXJUECmDwO6hyupsuYcTpZKZyByeE9e-1sa2U24,8
7
- amochka-0.1.7.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- amochka