py4writers 0.8.0__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.
@@ -0,0 +1,516 @@
1
+ Metadata-Version: 2.3
2
+ Name: py4writers
3
+ Version: 0.8.0
4
+ Summary: Python library for 4writers.net automation
5
+ Author: socalmy
6
+ Author-email: sergeitoropov2003@gmail.com
7
+ Requires-Python: >=3.12,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Requires-Dist: aiohttp (>=3.11.10,<4.0.0)
12
+ Requires-Dist: bs4 (>=0.0.2,<0.0.3)
13
+ Requires-Dist: build (>=1.2.2.post1,<2.0.0)
14
+ Requires-Dist: certifi (>=2024.8.30,<2025.0.0)
15
+ Requires-Dist: envparse (>=0.2.0,<0.3.0)
16
+ Requires-Dist: lxml (>=5.3.0,<6.0.0)
17
+ Requires-Dist: setuptools (>=75.7.0,<76.0.0)
18
+ Description-Content-Type: text/markdown
19
+
20
+ # py4writers
21
+
22
+ [![PyPI version](https://badge.fury.io/py/py4writers.svg)](https://badge.fury.io/py/py4writers)
23
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
24
+
25
+ Python библиотека для автоматизации работы с [4writers.net](https://4writers.net) - асинхронный API для получения заказов, управления файлами и взаимодействия с платформой.
26
+
27
+ ## ✨ Основные возможности
28
+
29
+ - 🔐 **Авторизация** - автоматическая авторизация и управление сессией
30
+ - 📋 **Получение заказов** - доступные заказы (free orders) и выполненные (completed orders)
31
+ - 📄 **Работа с файлами** - получение списка файлов и скачивание
32
+ - 🔄 **Retry механизм** - автоматические повторные попытки при ошибках сети
33
+ - ⚡ **Rate limiting** - контроль параллельных запросов (до 10 одновременно)
34
+ - 🎯 **Context manager** - автоматическое управление ресурсами через `async with`
35
+ - 🛡️ **Type hints** - полная типизация для IDE autocomplete
36
+ - 📊 **Детальное логирование** - отслеживание всех операций
37
+
38
+ ## 📦 Установка
39
+
40
+ ```bash
41
+ pip install py4writers
42
+ ```
43
+
44
+ Или через poetry:
45
+
46
+ ```bash
47
+ poetry add py4writers
48
+ ```
49
+
50
+ ## 🚀 Быстрый старт
51
+
52
+ ### Базовый пример
53
+
54
+ ```python
55
+ import asyncio
56
+ from py4writers import API
57
+
58
+ async def main():
59
+ # Используем context manager для автоматического закрытия ресурсов
60
+ async with API(login="your_login", password="your_password") as api:
61
+ # Авторизация
62
+ await api.login()
63
+
64
+ # Получаем доступные заказы
65
+ orders = await api.get_orders(page=1, page_size=50)
66
+
67
+ for order in orders:
68
+ print(f"📦 {order.order_id}: {order.title}")
69
+ print(f"💰 ${order.total:.2f} | ⏰ {order.deadline}")
70
+ if order.description:
71
+ print(f"📝 {order.description[:100]}...")
72
+
73
+ if __name__ == "__main__":
74
+ asyncio.run(main())
75
+ ```
76
+
77
+ ### Использование с .env файлом
78
+
79
+ ```python
80
+ import asyncio
81
+ from py4writers import API
82
+ from envparse import env
83
+
84
+ # Загружаем переменные окружения
85
+ env.read_envfile(".env")
86
+ LOGIN = env.str("LOGIN")
87
+ PASSWORD = env.str("PASSWORD")
88
+
89
+ async def main():
90
+ async with API(login=LOGIN, password=PASSWORD) as api:
91
+ await api.login()
92
+ orders = await api.get_orders()
93
+ print(f"Найдено заказов: {len(orders)}")
94
+
95
+ asyncio.run(main())
96
+ ```
97
+
98
+ ## 📚 Примеры использования
99
+
100
+ ### 1. Получение доступных заказов
101
+
102
+ #### Вариант A: Получить все заказы сразу (в память)
103
+
104
+ ```python
105
+ async with API(login=LOGIN, password=PASSWORD) as api:
106
+ await api.login()
107
+
108
+ # Получить заказы с пагинацией
109
+ orders = await api.get_orders(
110
+ page=1,
111
+ page_size=50,
112
+ category="essay"
113
+ )
114
+
115
+ for order in orders:
116
+ print(f"Order #{order.order_id}")
117
+ print(f" Название: {order.title}")
118
+ print(f" Всего: ${order.total:.2f}")
119
+ ```
120
+
121
+ #### Вариант B: Streaming через async generator (рекомендуется)
122
+
123
+ ```python
124
+ async with API(login=LOGIN, password=PASSWORD) as api:
125
+ await api.login()
126
+
127
+ # Получаем заказы по одному (не загружает все в память)
128
+ async for order in api.iter_orders(page=1, page_size=50, max_pages=3):
129
+ print(f"📦 Order #{order.order_id}")
130
+ print(f" Название: {order.title}")
131
+ print(f" Тема: {order.subject}")
132
+ print(f" Тип: {order.order_type}")
133
+ print(f" Уровень: {order.academic_level}")
134
+ print(f" Страниц: {order.pages}")
135
+ print(f" Источников: {order.sources}")
136
+ print(f" Стиль: {order.style}")
137
+ print(f" Язык: {order.language}")
138
+ print(f" Дедлайн: {order.deadline}")
139
+ print(f" Осталось времени: {order.remaining}")
140
+ print(f" Зарплата: ${order.salary:.2f}")
141
+ print(f" Бонус: ${order.bonus:.2f}")
142
+ print(f" Всего: ${order.total:.2f}")
143
+ print(f" Описание: {order.description}")
144
+
145
+ # Можно прервать в любой момент
146
+ if order.total > 50.0:
147
+ print("Нашли дорогой заказ, останавливаемся!")
148
+ break
149
+ ```
150
+
151
+ ### 2. Получение выполненных заказов
152
+
153
+ #### Вариант A: Получить все заказы сразу
154
+
155
+ ```python
156
+ async with API(login=LOGIN, password=PASSWORD) as api:
157
+ await api.login()
158
+
159
+ # Получаем выполненные заказы
160
+ completed = await api.get_completed_orders(page=1)
161
+
162
+ for order in completed:
163
+ print(f"✅ Order #{order.order_id}")
164
+ print(f" Название: {order.title}")
165
+ print(f" Ваша выплата: ${order.your_payment:.2f}")
166
+ print(f" Работа редактора: ${order.editor_work:.2f}")
167
+ print(f" Страниц: {order.pages}")
168
+ print(f" Описание: {order.description}")
169
+ print(f" Файлов: {len(order.files) if order.files else 0}")
170
+ ```
171
+
172
+ #### Вариант B: Streaming (рекомендуется)
173
+
174
+ ```python
175
+ async with API(login=LOGIN, password=PASSWORD) as api:
176
+ await api.login()
177
+
178
+ # Получаем заказы по одному
179
+ async for order in api.iter_completed_orders():
180
+ print(f"✅ Order #{order.order_id}: {order.title}")
181
+ print(f" 💰 Ваша выплата: ${order.your_payment:.2f}")
182
+ print(f" 📝 Описание: {order.description[:100]}...")
183
+
184
+ # Обрабатываем файлы сразу
185
+ if order.files:
186
+ for file in order.files:
187
+ print(f" 📁 {file.name}")
188
+ ```
189
+
190
+ ### 3. Работа с файлами
191
+
192
+ #### Вариант A: Получить все файлы сразу
193
+
194
+ ```python
195
+ async with API(login=LOGIN, password=PASSWORD) as api:
196
+ await api.login()
197
+
198
+ # Получаем список файлов заказа
199
+ files = await api.get_order_files(order_index=2569038)
200
+
201
+ for file in files:
202
+ print(f"📁 {file.name}")
203
+ print(f" ID: {file.id}")
204
+ print(f" Автор: {file.author}")
205
+ print(f" Дата: {file.date}")
206
+
207
+ # Скачиваем файл
208
+ file_bytes = await api.download_file(file.id)
209
+
210
+ if file_bytes:
211
+ # Сохраняем на диск
212
+ with open(f"downloaded_{file.name}", "wb") as f:
213
+ f.write(file_bytes)
214
+ print(f" ✅ Скачан: {len(file_bytes):,} байт")
215
+ ```
216
+
217
+ #### Вариант B: Streaming файлов (рекомендуется)
218
+
219
+ ```python
220
+ async with API(login=LOGIN, password=PASSWORD) as api:
221
+ await api.login()
222
+
223
+ # Скачиваем файлы по одному (экономит память)
224
+ async for file in api.iter_order_files(order_index=2569038):
225
+ print(f"📁 Скачиваем: {file.name}")
226
+
227
+ # Скачиваем и сразу сохраняем
228
+ file_bytes = await api.download_file(file.id)
229
+ if file_bytes:
230
+ with open(f"downloads/{file.name}", "wb") as f:
231
+ f.write(file_bytes)
232
+ print(f" ✅ Сохранен: {len(file_bytes):,} байт")
233
+ ```
234
+
235
+ ### 4. Получение деталей заказа
236
+
237
+ ```python
238
+ async with API(login=LOGIN, password=PASSWORD) as api:
239
+ await api.login()
240
+
241
+ # Для обычного заказа
242
+ description = await api.fetch_order_details(
243
+ order_index=2569038,
244
+ is_completed=False
245
+ )
246
+
247
+ # Для выполненного заказа
248
+ description = await api.fetch_order_details(
249
+ order_index=2569038,
250
+ is_completed=True
251
+ )
252
+
253
+ print(f"Описание: {description}")
254
+ ```
255
+
256
+ ### 5. Взятие заказа в работу
257
+
258
+ ```python
259
+ async with API(login=LOGIN, password=PASSWORD) as api:
260
+ await api.login()
261
+
262
+ # Берем заказ по индексу
263
+ success = await api.take_order(order_index=2569038)
264
+
265
+ if success:
266
+ print("✅ Заказ успешно взят!")
267
+ else:
268
+ print("❌ Не удалось взять заказ")
269
+ ```
270
+
271
+ ## 📊 Модели данных
272
+
273
+ ### Order
274
+
275
+ ```python
276
+ @dataclass
277
+ class Order:
278
+ order_id: str # ID заказа
279
+ order_index: int # Индекс заказа (для запросов)
280
+ title: str # Название
281
+ subject: str # Тема
282
+ deadline: str # Дедлайн
283
+ remaining: str # Оставшееся время
284
+ order_type: str # Тип работы (Essay, Research Paper, etc.)
285
+ academic_level: str # Академический уровень
286
+ style: str # Стиль оформления (APA, MLA, etc.)
287
+ language: str # Язык
288
+ pages: Optional[int] # Количество страниц
289
+ sources: Optional[int] # Количество источников
290
+ salary: Optional[float] # Зарплата
291
+ bonus: Optional[float] # Бонус
292
+ total: Optional[float] # Итого
293
+ description: Optional[str] # Описание заказа
294
+ files: Optional[List[File]] # Файлы заказа
295
+ editor_work: Optional[float] # Работа редактора (completed)
296
+ your_payment: Optional[float] # Ваша выплата (completed)
297
+ ```
298
+
299
+ ### File
300
+
301
+ ```python
302
+ @dataclass
303
+ class File:
304
+ id: int # ID файла
305
+ name: str # Название файла
306
+ author: str # Автор загрузки
307
+ date: str # Дата загрузки
308
+ ```
309
+
310
+ ## ⚙️ Конфигурация
311
+
312
+ ### Создание API клиента
313
+
314
+ ```python
315
+ from py4writers import API
316
+
317
+ # С credentials
318
+ api = API(login="user", password="pass")
319
+
320
+ # С кастомным HTTP клиентом
321
+ from py4writers.client.aiohttp import AiohttpClient
322
+
323
+ http_client = AiohttpClient(timeout=60)
324
+ api = API(
325
+ login="user",
326
+ password="pass",
327
+ http_client=http_client
328
+ )
329
+
330
+ # Context manager (рекомендуется)
331
+ async with API(login="user", password="pass") as api:
332
+ await api.login()
333
+ # ваш код
334
+ ```
335
+
336
+ ### Настройка логирования
337
+
338
+ ```python
339
+ import logging
340
+
341
+ # Базовая настройка
342
+ logging.basicConfig(
343
+ level=logging.INFO,
344
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
345
+ )
346
+
347
+ # Детальное логирование
348
+ logging.basicConfig(level=logging.DEBUG)
349
+ ```
350
+
351
+ ## 🛡️ Обработка ошибок
352
+
353
+ ```python
354
+ from py4writers import API
355
+ from py4writers.exceptions import (
356
+ AuthenticationError,
357
+ SessionExpiredError,
358
+ NetworkError,
359
+ ParsingError
360
+ )
361
+
362
+ async with API(login=LOGIN, password=PASSWORD) as api:
363
+ try:
364
+ await api.login()
365
+ orders = await api.get_orders()
366
+
367
+ except AuthenticationError as e:
368
+ print(f"❌ Ошибка авторизации: {e}")
369
+
370
+ except SessionExpiredError as e:
371
+ print(f"⏰ Сессия истекла: {e}")
372
+ # API автоматически попытается переавторизоваться
373
+
374
+ except NetworkError as e:
375
+ print(f"🌐 Сетевая ошибка: {e}")
376
+ # Retry декоратор автоматически повторит запрос
377
+
378
+ except ParsingError as e:
379
+ print(f"📄 Ошибка парсинга: {e}")
380
+ ```
381
+
382
+ ## 🔧 Продвинутые возможности
383
+
384
+ ### Async Generators (Streaming)
385
+
386
+ **Преимущества использования `iter_*` методов:**
387
+
388
+ 1. 🚀 **Меньше памяти** - заказы не загружаются все сразу
389
+ 2. ⚡ **Быстрее начало** - первый заказ доступен моментально
390
+ 3. 🔄 **Прерывание** - можно остановить в любой момент через `break`
391
+ 4. 📊 **Пагинация** - автоматически обходит все страницы
392
+
393
+ ```python
394
+ async with API(login=LOGIN, password=PASSWORD) as api:
395
+ await api.login()
396
+
397
+ # Обрабатываем первые 100 заказов из всех страниц
398
+ count = 0
399
+ async for order in api.iter_orders(max_pages=None): # None = все страницы
400
+ print(f"{order.order_id}: ${order.total}")
401
+
402
+ count += 1
403
+ if count >= 100:
404
+ break # Останавливаемся после 100 заказов
405
+
406
+ # Скачиваем все файлы выполненного заказа
407
+ async for order in api.iter_completed_orders():
408
+ print(f"\n📦 {order.title}")
409
+
410
+ async for file in api.iter_order_files(order.order_index):
411
+ print(f" 📁 {file.name}")
412
+ file_bytes = await api.download_file(file.id)
413
+ # Сразу обрабатываем без хранения в памяти
414
+ process_file(file.name, file_bytes)
415
+ ```
416
+
417
+ ### Rate Limiting
418
+
419
+ API автоматически ограничивает количество параллельных запросов (по умолчанию 10):
420
+
421
+ ```python
422
+ from py4writers.utils.rate_limiter import RateLimiter
423
+
424
+ # Кастомный лимит
425
+ rate_limiter = RateLimiter(max_concurrent=5)
426
+ api = API(
427
+ login=LOGIN,
428
+ password=PASSWORD,
429
+ rate_limiter=rate_limiter
430
+ )
431
+ ```
432
+
433
+ ### Retry механизм
434
+
435
+ Используйте декоратор для автоматических повторных попыток:
436
+
437
+ ```python
438
+ from py4writers.utils.retry import async_retry
439
+ from py4writers.exceptions import NetworkError
440
+
441
+ @async_retry(
442
+ max_attempts=3,
443
+ delay=1.0,
444
+ backoff=2.0,
445
+ exceptions=(NetworkError,)
446
+ )
447
+ async def my_function():
448
+ # ваш код
449
+ pass
450
+ ```
451
+
452
+ ## 📝 Требования
453
+
454
+ - Python 3.12+
455
+ - aiohttp
456
+ - beautifulsoup4
457
+ - lxml
458
+ - certifi
459
+
460
+ ## 🔄 Changelog
461
+
462
+ ### [0.8.0] - 2026-01-17
463
+
464
+ #### ✨ Added
465
+ - **Async Generators**: Новые методы для streaming
466
+ - `iter_orders()` - получение заказов по одному с автоматической пагинацией
467
+ - `iter_completed_orders()` - получение выполненных заказов по одному
468
+ - `iter_order_files()` - получение файлов по одному
469
+ - **Преимущества**: меньше памяти, быстрее начало, можно прервать через `break`
470
+
471
+ ### [0.7.0] - 2026-01-17
472
+
473
+ #### 🔄 Breaking Changes
474
+ - Переименован модуль `fourwritersapi` → `py4writers`
475
+
476
+ ### [0.6.1] - 2026-01-17
477
+
478
+ #### 🔧 Fixed
479
+ - Исправлен парсинг описаний для completed orders
480
+ - Добавлен отдельный endpoint для completed order details
481
+
482
+ ### [0.6.0] - 2026-01-17
483
+
484
+ #### ✨ Added
485
+ - Context Manager Support
486
+ - Custom Exceptions
487
+ - Retry Logic
488
+ - Rate Limiting
489
+ - OrderParser Class
490
+ - Pagination Support
491
+ - Better Logging
492
+
493
+ #### 🔧 Fixed
494
+ - Resource Leak в AiohttpClient
495
+ - Session Expiration handling
496
+ - BeautifulSoup warnings
497
+ - Code duplication (200+ строк)
498
+
499
+ ## 📄 Лицензия
500
+
501
+ MIT License
502
+
503
+ ## 🤝 Контрибьюция
504
+
505
+ Pull requests приветствуются! Для больших изменений сначала откройте issue для обсуждения.
506
+
507
+ ## 📧 Контакты
508
+
509
+ - Author: socalmy
510
+ - Email: sergeitoropov2003@gmail.com
511
+ - PyPI: https://pypi.org/project/py4writers/
512
+
513
+ ## ⭐ Поддержка
514
+
515
+ Если библиотека была полезна, поставьте звезду на GitHub!
516
+