Metadata-Version: 2.4
Name: simpleworkernet
Version: 0.0.1b17
Summary: Python клиент для API WorkerNet
Author-email: BusyBeaver <busybeaver.bb@gmail.com>
License: MIT
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE.txt
Requires-Dist: requests>=2.25.0
Dynamic: license-file

SimpleWorkerNet

Высокопроизводительный Python клиент для REST API системы WorkerNet с интеллектуальной системой трансформации и типизации сложных JSON структур


📋 Содержание

    🌟 Особенности

    📦 Установка

    🚀 Быстрый старт

    🔧 Конфигурация

    📚 Основные компоненты

        WorkerNetClient

        BaseModel и smart_model

        SmartData Framework

        Метаданные и пути

    📊 Логирование

        Настройка логов

        Сессионные логи

        Управление логами

    💾 Кэширование

        Настройка кэша

        Управление кэшем

    🎨 Примеры использования

        Базовые операции с API

        Фильтрация и поиск с SmartData

        Продвинутый поиск

        Работа с моделями

        Агрегация и статистика

        Сериализация

    🧹 Очистка и деинсталляция

    📖 Документация

    🤝 Вклад в разработку

    📄 Лицензия

    ✒️ Автор

🌟 Особенности
🚀 SmartData Framework

SmartData — это специализированный SDK-компонент для обработки API-ответов сервера WorkerNet. Он превращает сырые, динамически типизированные JSON-данные сервера в строго типизированные объекты Python с поддержкой глубокого поиска и автоматической трансформации коллекций.

    Автоматическое приведение типов согласно аннотациям Python

    Сохранение метаданных о пути извлечения данных (MetaData класс)

    Глубокий поиск по любым уровням вложенности

    Fluent-интерфейс для фильтрации, сортировки и агрегации

    Поддержка экспорта/импорта (JSON, Pickle, Gzip)

🔧 BaseModel Engine

BaseModel - мощная система рекурсивного кастинга типов:

    Автоматическое преобразование данных в типизированные объекты

    Поддержка Union, Optional, List, вложенных моделей

    Постобработка и схлопывание избыточных структур

    Сериализация/десериализация с сохранением типов

    Декоратор @smart_model для удобного создания моделей

🎯 Умный клиент API

    Автоматическое управление сессиями (поддержка with контекста)

    Интеллектуальный выбор метода (GET/POST) при превышении лимита URL

    Автоматические повторы при таймаутах

    Кэширование полей для оптимизации производительности

    Полное логирование всех операций

📊 Продвинутое логирование

    Сессионные логи - каждый запуск создает отдельный файл с временной меткой

    Автоматическая ротация - старые логи удаляются, новые не разрастаются

    Детальная информация - ID сессии, время создания, размер файла

    Кросс-платформенность - корректная работа на Windows, macOS и Linux

    Управление сессиями - создание новых сессий, просмотр истории

🗄️ Кэширование

    Двухуровневое кэширование (поля моделей и числовые ключи)

    Автоматическая очистка при достижении лимита

    Сохранение кэша на диск и загрузка при инициализации

    Возможность полного отключения кэширования

    Детальная статистика использования

📦 Установка
Базовая установка
```bash

pip install simpleworkernet
```

🚀 Быстрый старт
Минимальный пример
```python

from simpleworkernet import WorkerNetClient

# Создаем клиент
client = WorkerNetClient(
    host="my.workernet.ru",
    apikey="your-secret-api-key"
)

# Получаем данные
cables = client.Fiber.catalog_cables_get()
print(f"Найдено кабелей: {len(cables)}")
```

Использование с контекстным менеджером
```python

from simpleworkernet import WorkerNetClient

with WorkerNetClient("my.workernet.ru", "your-api-key") as client:
    customers = client.Customer.get_data()
    addresses = client.Address.get()
    
    print(f"Абонентов: {len(customers)}")
    print(f"Адресов: {len(addresses)}")
```

Умная фильтрация с SmartData
```python

from simpleworkernet import WorkerNetClient, Where, Operator

client = WorkerNetClient("my.workernet.ru", "your-api-key")

# Получаем данные
cables = client.Fiber.catalog_cables_get()

# Создаем условия поиска
conditions = [
    Where('cable_line_type_id', 5),
    Where('fiber_count', [4, 16], Operator.BETWEEN),
    Where('model', 'ОКА', Operator.LIKE)
]

# Фильтруем
filtered = cables.filter(*conditions, join='AND')
print(f"Найдено: {filtered.count()}")

# Или через where для простых условий
active_customers = customers.where('state', 'active')
```

🔧 Конфигурация
Базовая настройка
```python

from simpleworkernet import config_manager

# Просмотр текущей конфигурации
config_manager.show_config()

# Изменение настроек
config_manager.update(
    log_level="DEBUG",
    log_to_file=True,
    console_output=True,
    cache_enabled=True,
    cache_max_size=100000
)

# Сохранение в реестр/файл
config_manager.save()
```

Полная конфигурация через код
```python

from simpleworkernet import config_manager, WorkerNetConfig

# Создание своей конфигурации
custom_config = WorkerNetConfig(
    log_level="INFO",
    log_file="/custom/path/workernet.log",
    cache_enabled=True,
    cache_max_size=50000,
    default_timeout=60,
    max_retries=5,
    smartdata_max_depth=200,
    smartdata_debug=True
)

# Применение
config_manager.update(**custom_config.to_dict())
config_manager.save()
```

Переменные окружения
```bash

export SMARTDATA_LOG_LEVEL=DEBUG
export SMARTDATA_LOG_FILE=true
export SMARTDATA_LOG_PATH=/var/log/workernet.log
```

📚 Основные компоненты
WorkerNetClient

WorkerNetClient - основной класс для взаимодействия с API WorkerNet.
Параметры инициализации:

    host - хост API (обязательный)

    apikey - ключ API (обязательный)

    protocol - протокол ('http' или 'https', по умолчанию 'https')

    port - порт (по умолчанию 443)

    apiscr - имя скрипта API (по умолчанию 'api.php')

    timeout - таймаут запроса (из конфига)

    max_retries - количество повторов при ошибке

Доступные категории:

    Address - работа с адресами

    Customer - работа с абонентами

    Device - оборудование

    Employee - сотрудники

    Fiber - кабельные линии

    Map - карты покрытия

    Module - внешние запросы

    И многие другие...

BaseModel и smart_model

BaseModel - базовый класс для всех моделей данных с автоматическим кастингом типов.
```python

from simpleworkernet import smart_model, BaseModel, vStr, GeoPoint

@smart_model
class Address(BaseModel):
    """Модель адреса"""
    id: int
    city: vStr
    street: vStr
    house: str
    apartment: Optional[int]
    coordinates: GeoPoint

# Автоматическое создание из словаря
addr = Address({
    "id": 1,
    "city": "Москва",
    "street": "Ленина",
    "house": "10",
    "apartment": 42,
    "coordinates": [55.75, 37.62]
})

print(addr.city)  # "Москва"
print(addr.coordinates)  # "55.75,37.62"
```

SmartData Framework

SmartData - контейнер для интеллектуальной обработки JSON-структур.
Создание SmartData
```python

from simpleworkernet import SmartData

# Из списка словарей
data = [
    {"id": 1, "name": "Иван", "age": 30},
    {"id": 2, "name": "Петр", "age": 25},
    {"id": 3, "name": "Сидор", "age": 35}
]

sd = SmartData(data, target_type=Customer)
print(f"Всего: {sd.count()}")  # Всего: 3
```

Метаданные и пути

Каждый элемент в SmartData сохраняет информацию о своем местоположении в исходной структуре:
```python

# Получение метаданных
for item in sd:
    path = sd.get_item_path(item)
    print(f"Элемент {item} находится по пути: {path}")

# Фильтрация по пути
nested_items = sd.filter_by_path("field:address/*")
```

📊 Логирование
Настройка логов
```python

from simpleworkernet import log, config_manager

# Базовая настройка через конфиг
config_manager.update(
    log_level="DEBUG",
    log_to_file=True,
    console_output=True
)

# Прямая настройка логгера
log.configure(
    level="DEBUG",
    log_to_file=True,
    log_file="/custom/path/workernet.log",
    console_output=True,
    max_log_files=20  # хранить 20 последних сессий
)
```

Сессионные логи

Логгер автоматически создает отдельные файлы для каждого запуска:
```python

from simpleworkernet import log

# Получить информацию о текущей сессии
current_log = log.get_session_log_path()
session_id = log.get_session_id()

print(f"Текущая сессия: {session_id}")
print(f"Файл лога: {current_log}")
print(f"ID сессии: {session_id}")

# Пример вывода:
# Текущая сессия: 20250220_143022
# Файл лога: C:\Users\user\AppData\Roaming\simpleworkernet\logs\workernet_20250220_143022.log
# ID сессии: 20250220_143022
```

Управление логами
```python

from simpleworkernet import log
from datetime import datetime

# Список всех сессий (от новых к старым)
all_logs = log.list_session_logs(sort_by='newest')
print(f"Всего сессий: {len(all_logs)}")

# Детальная информация о каждой сессии
for log_file in all_logs[:5]:  # последние 5
    info = log.get_session_info(log_file)
    if 'created' in info:
        created = info['created'].strftime("%Y-%m-%d %H:%M:%S")
        size = info['size_kb']
        print(f"📄 {info['session_id']} - {created} - {size:.1f} KB")
        
# Начать новую сессию вручную
new_session = log.new_session("my_custom_session")
print(f"Новая сессия: {new_session}")

# Проверить структуру файлов
import os
log_dir = log.get_session_log_path().parent
print(f"\nДиректория логов: {log_dir}")
print("Файлы:")
for f in sorted(log_dir.glob("*.log")):
    stat = f.stat()
    size = stat.st_size / 1024
    modified = datetime.fromtimestamp(stat.st_mtime).strftime("%Y-%m-%d %H:%M")
    print(f"  {f.name} - {modified} - {size:.1f} KB")
```

Пример структуры файлов логов
```text

%APPDATA%\simpleworkernet\logs\
├── workernet_20250220_091233.log   # утренняя сессия (50 KB)
├── workernet_20250220_143022.log   # дневная сессия (120 KB)
└── workernet_20250220_163502.log   # текущая сессия (45 KB)
```

💾 Кэширование
Настройка кэша
```python

from simpleworkernet import SmartData, config_manager

# Настройка через конфиг
config_manager.update(
    cache_enabled=True,
    cache_max_size=100000,
    cache_auto_save=True,
    cache_dir="/custom/cache/path"
)

# Прямое управление
SmartData.set_cache_max_size(50000)
SmartData.setup_auto_save(True)
```

Управление кэшем
```python

from simpleworkernet import SmartData

# Сохранение кэша
SmartData.save_cache()
SmartData.save_cache(force=True)  # принудительно

# Загрузка кэша
SmartData.load_cache()

# Очистка
SmartData.clear_cache()

# Отключение/включение
SmartData.disable_cache()
SmartData.enable_cache()

# Статистика
stats = SmartData.get_cache_stats()
print(f"Всего обращений: {stats['total']}")
print(f"Попаданий: {stats['hits']} ({stats['hit_rate']:.1f}%)")
print(f"Размер кэша: {stats['field_cache_size']} полей")

# Детальная статистика
SmartData.print_detailed_stats()
```

🎨 Примеры использования
Базовые операции с API
```python

from simpleworkernet import WorkerNetClient, log

# Настройка логирования
log.configure(level="DEBUG")

with WorkerNetClient("my.workernet.ru", "your-api-key") as client:
    
    # Получение списка абонентов
    customers = client.Module.get_user_list()
    log.info(f"Получено абонентов: {len(customers)}")
    
    # Получение конкретного абонента по ID
    customer = client.Customer.get_data(customer_id=123)
    
    # Получение адресов
    addresses = client.Address.get(city_id=1)
    
    # Получение каталога кабелей
    cables = client.Fiber.catalog_cables_get()
    
    # Работа с модулями
    users = client.Module.get_house_list()
```

Фильтрация и поиск с SmartData
```python

from simpleworkernet import SmartData, Where, Operator

# Создаем SmartData из ответа API
data = [
    {"id": 1, "name": "Иванов Иван", "age": 30, "city": "Москва", "balance": 1500},
    {"id": 2, "name": "Петров Петр", "age": 25, "city": "СПб", "balance": 800},
    {"id": 3, "name": "Сидоров Сидор", "age": 35, "city": "Москва", "balance": 2200},
    {"id": 4, "name": "Смирнова Анна", "age": 28, "city": "Казань", "balance": 1200},
]

sd = SmartData(data)

# Простая фильтрация
adults = sd.where('age', 18, Operator.GTE)
moscow = sd.where('city', 'Москва')

# Составные условия
filtered = sd.filter(
    Where('age', 30, Operator.GTE),
    Where('balance', 1000, Operator.GT),
    join='AND'
)

# Частичное совпадение
ivanov = sd.where('name', 'Иванов', Operator.LIKE)

# Диапазон
middle_age = sd.where('age', [25, 35], Operator.BETWEEN)

# Проверка вхождения
cities = sd.where('city', ['Москва', 'СПб'], Operator.IN)
```

Продвинутый поиск
```python

# Глубокий поиск по всей структуре (включая вложенные объекты)
complex_data = [
    {
        "id": 1,
        "name": "Иван",
        "contacts": {
            "email": "ivan@example.com",
            "phone": "+7-999-123-45-67"
        },
        "orders": [
            {"id": 101, "amount": 1500},
            {"id": 102, "amount": 2300}
        ]
    },
    {
        "id": 2,
        "name": "Петр",
        "contacts": {
            "email": "petr@example.com",
            "phone": "+7-999-765-43-21"
        },
        "orders": [
            {"id": 103, "amount": 800}
        ]
    }
]

sd = SmartData(complex_data)

# Поиск по email (найдет в любом месте структуры)
results = sd.find_all('email', 'ivan@example.com')
print(f"Найдено объектов: {len(results)}")

# Проверка существования
if sd.exists('phone', '+7-999-123-45-67'):
    print("Телефон найден!")

# Поиск по частичному совпадению
results = sd.find_all('name', 'Иван', is_partial=True)

# Сложный поиск с несколькими критериями
found = sd.find_all(
    key=['name', 'amount'],
    value=['Иван', 1500],
    is_partial=False
)

# Работа с путями через метаданные
for item in sd:
    path = sd.get_item_path(item)
    if "contacts" in path:
        print(f"Контакт: {item} по пути {path}")

# Фильтрация по пути
emails = sd.filter_by_path("field:contacts/field:email")
```

Работа с моделями
```python

from simpleworkernet import smart_model, BaseModel, vStr, vPhoneNumber, vMoney
from typing import List, Optional

@smart_model
class Contact(BaseModel):
    email: Optional[str]
    phone: Optional[vPhoneNumber]
    telegram: Optional[str]

@smart_model
class Order(BaseModel):
    id: int
    amount: vMoney
    date: str
    status: str

@smart_model
class User(BaseModel):
    id: int
    name: vStr
    age: Optional[int]
    contacts: Contact
    orders: List[Order]
    balance: vMoney

# Создание из словаря
user_data = {
    "id": 1,
    "name": "Иван Петров",
    "age": 30,
    "contacts": {
        "email": "ivan@example.com",
        "phone": "+7-999-123-45-67"
    },
    "orders": [
        {"id": 101, "amount": 1500, "date": "2024-01-15", "status": "completed"},
        {"id": 102, "amount": 2300, "date": "2024-02-01", "status": "pending"}
    ],
    "balance": 5000
}

user = User(**user_data)
print(f"Пользователь: {user.name}")
print(f"Телефон: {user.contacts.phone.normalized}")
print(f"Всего заказов: {len(user.orders)}")
print(f"Сумма заказов: {sum(o.amount.amount for o in user.orders)}")
```

Агрегация и статистика
```python

from simpleworkernet import SmartData

data = [
    {"name": "Иван", "age": 30, "salary": 50000, "department": "IT"},
    {"name": "Петр", "age": 25, "salary": 45000, "department": "IT"},
    {"name": "Сидор", "age": 35, "salary": 60000, "department": "Sales"},
    {"name": "Анна", "age": 28, "salary": 55000, "department": "Sales"},
    {"name": "Мария", "age": 32, "salary": 52000, "department": "HR"},
]

sd = SmartData(data)

# Статистика
total = sd.count()  # 5
avg_age = sd.avg(lambda x: x['age'])  # 30.0
max_salary = sd.max(lambda x: x['salary'])  # 60000
min_salary = sd.min(lambda x: x['salary'])  # 45000
total_salary = sd.sum(lambda x: x['salary'])  # 262000

# Группировка по отделам
by_department = sd.group_by(lambda x: x['department'])
for dept, employees in by_department.items():
    print(f"{dept}: {employees.count()} сотрудников")
    print(f"  Средняя зарплата: {employees.avg(lambda x: x['salary']):.0f}")

# Уникальные значения
unique_depts = sd.unique(lambda x: x['department'])
print(f"Отделы: {list(unique_depts)}")

# Трансформация
names = sd.map(lambda x: x['name'].upper())
print(f"Имена: {names}")

# Сортировка
by_age = sd.sort(key=lambda x: x['age'])
by_salary_desc = sd.sort(key=lambda x: x['salary'], reverse=True)

# Лимиты и смещения
top_3 = sd.sort(key=lambda x: x['salary'], reverse=True).limit(3)
next_2 = sd.sort(key=lambda x: x['salary']).skip(3).limit(2)
```

Сериализация
```python

from simpleworkernet import SmartData

data = [{"id": 1, "name": "Test"}, {"id": 2, "name": "Test2"}]
sd = SmartData(data)

# Сохранение в JSON
sd.to_file("data.json")
sd.to_file("data.json", clear_meta=True)  # без метаданных

# Сохранение в pickle
sd.to_file("data.pkl", format="pkl")

# Сохранение в сжатый gzip
sd.to_file("data.gz", format="gz")

# Загрузка из файлов
loaded_json = SmartData.from_file("data.json")
loaded_pkl = SmartData.from_file("data.pkl")
loaded_gz = SmartData.from_file("data.gz")

# Преобразование в словарь с восстановлением структуры
original_dict = sd.to_dict()
print(original_dict)

# Получение плоского списка
flat_list = sd.to_list()
```

Работа с метаданными
```python

from simpleworkernet import SmartData

complex_data = {
    "users": [
        {
            "id": 1,
            "name": "Alice",
            "contacts": {
                "email": "alice@example.com",
                "phone": "+7-999-111-22-33"
            }
        },
        {
            "id": 2,
            "name": "Bob",
            "contacts": {
                "email": "bob@example.com",
                "phone": "+7-999-444-55-66"
            }
        }
    ],
    "settings": {"theme": "dark", "language": "ru"}
}

sd = SmartData(complex_data)

# Получение путей всех элементов
for item in sd:
    path = sd.get_item_path(item)
    if path:
        print(f"{path}: {item}")

# Поиск по конкретному пути
emails = sd.filter_by_path("field:users/*/field:contacts/field:email")
for email in emails:
    print(f"Email: {email}")

# Фильтрация по типу элементов
primitives = sd.get_items_by_type('primitive')
dicts = sd.get_items_by_type('dict')

# Получение метаданных конкретного элемента
first_user = sd[0]
meta = sd.get_metadata(first_user)
if meta:
    print(f"Путь: {meta.get_path_string()}")
    print(f"Родительский путь: {meta.get_parent_path()}")
    print(f"Последний сегмент: {meta.get_last_segment()}")
```

Примитивные типы
```python

from simpleworkernet import vStr, vFlag, GeoPoint, vPhoneNumber, vMoney, vPercent, vINN

# Декодирование строк
text = vStr("Hello%20World&amp;Co")  # "Hello World&Co"

# Битовые флаги
flag = vFlag.v1
if flag & vFlag.v1:
    print("Флаг установлен")
print(vFlag.from_bool(True))  # vFlag.v1

# Геокоординаты
point = GeoPoint(55.75, 37.62)
point2 = GeoPoint("55.76,37.63")
print(point)  # "55.75,37.62"
print(point.distance_to(point2))  # расстояние в км

# Телефонные номера
phone = vPhoneNumber("+7 (123) 456-78-90")
print(phone.normalized)  # "71234567890"
print(phone.formatted)   # "+7 (123) 456-78-90"
print(phone.international)  # "+71234567890"

# Денежные суммы
money = vMoney(100.50, "RUB")
money2 = money + 50.25
print(money2)  # "150.75 RUB"

# Проценты
p = vPercent(15.5)
print(p)  # "15.5%"
print(p.of(1000))  # 155.0
print(p.add_to(1000))  # 1155.0

# ИНН с валидацией
inn = vINN("1234567890")
print(inn.is_valid)  # True
print(inn.is_legal)  # True (10 цифр)
```

Пакетная обработка
```python

from simpleworkernet import SmartData

sd1 = SmartData([1, 2, 3])
sd2 = SmartData([4, 5, 6])

# Конкатенация
combined = sd1 + sd2
print(combined.count())  # 6

# Массовое обновление
data = [
    {"id": 1, "status": "new"},
    {"id": 2, "status": "new"}
]
sd = SmartData(data)

# Обновить все элементы
sd.update_all(status="processed", priority=1)

# Через __setattr__
sd.status = "archived"
sd.priority = 2

for item in sd:
    print(item)  # {"id": 1, "status": "archived", "priority": 2}, ...
```

🧹 Очистка и деинсталляция
Очистка данных (реестр, кэш, логи)
```python

from simpleworkernet import cleanup

# С подтверждением
cleanup()

# Без подтверждения (из кода)
from simpleworkernet.scripts.uninstall import cleanup_simpleworkernet
cleanup_simpleworkernet()
```

Консольная команда (доступна после установки)
```bash

# Запуск очистки
cleanup-simpleworkernet

# Будет запрошено подтверждение
# Вы уверены, что хотите удалить все данные SimpleWorkerNet? (y/N):
```

Полное удаление пакета
```bash

# 1. Сначала очищаем данные
cleanup-simpleworkernet

# 2. Затем удаляем пакет
pip uninstall simpleworkernet
```

Что удаляется при очистке:

    Windows: ключ реестра HKEY_CURRENT_USER\SOFTWARE\SimpleWorkerNet

    Конфигурационные файлы: ~/.config/simpleworkernet/ или %APPDATA%\simpleworkernet\

    Кэш: ~/.cache/simpleworkernet/ или %LOCALAPPDATA%\simpleworkernet\cache\

    Логи: ~/.local/share/simpleworkernet/logs/ или %APPDATA%\simpleworkernet\logs\


✒️ Автор

 - [Андрей Литвинов](https://t.me/busy4beaver)

