onco-cola-utils


Nameonco-cola-utils JSON
Version 0.4.3 PyPI version JSON
download
home_pagehttps://t.me/hitomamoru
SummaryМои общие утилиты для работы с Excel
upload_time2025-09-04 07:56:02
maintainerNone
docs_urlNone
authorVolodin Alexandr Sergeevich
requires_python<4.0,>=3.9
licenseMIT
keywords utils excel pandas tools openpyxl key-param key-value
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # ExcelDataProcessor

Мощный и гибкий инструмент для обработки, анализа и модификации Excel-файлов с поддержкой структурированного логирования, валидации данных и удобного отображения сложных структур. Основной компонент — `ReaderController` — предназначен для автоматизации работы с XLS(X) файлами, содержащими табличные данные, особенно в контексте обработки данных с помощью ИИ-моделей.

---

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

- ✅ **Полная поддержка Excel (xls/xlsx)**: чтение, запись, переименование, удаление листов
- 🔍 **Автоматический поиск нужного листа** по ключевым полям (`source_name`, `url`)
- 🧹 **Очистка и фильтрация данных**: удаление пустых строк, работа с TTL-значениями
- 🔢 **Локальная идентификация строк** через `local_id` (устойчивая к перезаписям)
- 🔄 **Обновление данных по ID**: безопасное слияние результатов ИИ-обработки с исходным датафреймом
- 🛡️ **Потокобезопасность и проверка блокировок**: контроль доступа к файлам
- 📄 **Экспорт в CSV**: гибкое сохранение в альтернативных форматах
- 🧾 **Красивый вывод структур**: встроенная поддержка pretty-print для словарей, списков и dataclass
- 📋 **Цветное логирование**: интеграция с `loguru`, удобные уровни логов (DEBUG, INFO, SUCCESS, ERROR)

---

## Установка

Этот пакет предполагает использование как внутреннего модуля (например, в составе larger ETL- или AI-проекта). Для установки зависимостей:

```bash
pip install pandas openpyxl loguru deprecated
```

> ⚠️ Убедитесь, что файлы **не открыты в Excel**, иначе будет ошибка записи.

---

## Быстрый старт

```python
from onco_cola_utils.reader_controller.core import ReaderController
from pathlib import Path

# Пути к файлам
input_file = Path("data/input.xlsx")
output_file = Path("data/output.xlsx")

# Создаём контроллер
reader = ReaderController(file_path=input_file, file_output=output_file)

# Автоматически находит нужный лист и читает данные
reader.read_data()

# Получаем данные без пустых значений
clean_data = reader.perfect_data(reader.get_data())

# Обновляем поле из ИИ-ответа
ai_response = [
    {"ID": "1", "category_asis": "Electronics", "remark": "1"},
    {"ID": "2", "category_asis": "Books", "remark": None},
]

# Применяем изменения
reader.update_dataframe_from_updated_dataframe(
    updated_dataframe={1: ai_response[0], 2: ai_response[1]},
    updated_fields=["category_asis", "remark"]
)

# Сохраняем результат
reader.update_file()
```

---

## API-документация

### `ReaderController(file_path, file_output, is_new=False, skip_rows=0)`

Основной класс для управления Excel-файлами.

**Параметры:**
- `file_path` (`Path`): путь к входному файлу.
- `file_output` (`Path`): путь к выходному файлу (может совпадать).
- `is_new` (`bool`): флаг, указывающий, что файл новый (не требует проверки на существование).
- `skip_rows` (`int`): количество строк для пропуска при чтении (не реализовано напрямую, но может быть расширено).

**Пример:**
```python
rc = ReaderController(Path("in.xlsx"), Path("out.xlsx"))
```

---

### `read_data(sheet_name=None) -> None`

Читает данные из Excel-листа в формате списка словарей. Все значения преобразуются в строки (с обрезанием `.0` у целых чисел).

**Параметры:**
- `sheet_name` (`str | None`): имя листа. Если `None` — читается первый активный лист.

**Пример:**
```python
reader.read_data(sheet_name="Sheet1")
```

---

### `get_data(sheet_name=None) -> list[dict]`

Ленивое получение данных: если данные ещё не загружены — вызывает `read_data()`.

**Возвращает:**
- `list[dict]`: список строк в виде словарей (ключ — имя столбца).

**Пример:**
```python
data = reader.get_data()
```

---

### `perfect_data(data_list) -> dict`

Фильтрует данные, удаляя строки, где значение в поле `DATA_ENTITY_TOBE` является "нулевым" (согласно `System.NULLED`).

**Параметры:**
- `data_list` (`list[dict]`): входной список данных.

**Возвращает:**
- `dict`: словарь, индексированный `local_id`.

**Пример:**
```python
filtered = reader.perfect_data(reader.get_data())
```

---

### `local_idfy(data_list) -> dict`

Присваивает идентификаторы на основе существующего `local_id` из данных.

**Параметры:**
- `data_list` (`list[dict]`): список данных с полем `local_id`.

**Возвращает:**
- `dict`: словарь вида `{local_id: row_data}`.

**Пример:**
```python
idfy_data = reader.local_idfy(data_list)
```

---

### `cycle_right_sheet() -> None`

Циклически удаляет первые листы Excel-файла, пока не найдёт лист, содержащий поля `source_name` и `url`.

**Использование:**
- Полезно при наличии служебных листов в начале файла.
- Автоматически пересохраняет файл без ненужных листов.

**Пример:**
```python
reader.cycle_right_sheet()  # Вызывается автоматически в check_local_id
```

---

### `check_local_id(find_it=True) -> bool`

Проверяет, содержит ли первый лист поле `local_id`. Перед этим запускает `cycle_right_sheet`.

**Параметры:**
- `find_it` (`bool`): если `True`, проверяет наличие `local_id`; иначе просто проверяет корректность листа.

**Возвращает:**
- `bool`: `True`, если `local_id` найден.

**Пример:**
```python
if not reader.check_local_id():
    reader.process_local_idfying()
```

---

### `process_local_idfying(field=ColumnStrings.DATA_LOCAL_ID) -> None`

Добавляет `local_id` (1, 2, 3...) к каждой строке и сохраняет файл.

**Пример:**
```python
reader.process_local_idfying()
```

---

### `update_dataframe_from_updated_dataframe(updated_dataframe, updated_fields, field_id="ID") -> bool | None`

Обновляет основной датафрейм на основе словаря обновлённых данных (например, ответа ИИ).

**Параметры:**
- `updated_dataframe` (`dict[int, dict]`): словарь `{ID: {field: value}}`.
- `updated_fields` (`list[str]`): список полей для обновления.
- `field_id` (`str`): ключ, по которому идентифицируются строки (по умолчанию `"ID"`).

**Возвращает:**
- `True` при успехе, `None` при ошибках.

**Пример:**
```python
reader.update_dataframe_from_updated_dataframe(
    updated_dataframe={1: {"cat_asis": "NewCat"}},
    updated_fields=["cat_asis"]
)
```

---

### `update_file(same_file=True) -> bool`

Сохраняет текущий датафрейм обратно в Excel.

**Параметры:**
- `same_file` (`bool`): если `True`, перезаписывает `file_path`, иначе — `file_output`.

**Пример:**
```python
reader.update_file(same_file=False)  # Сохранить в output
```

---

### `save_to_csv(same_file=True) -> bool`

Сохраняет данные в формате CSV.

**Параметры:**
- `same_file` (`bool`): использовать `file_path` или `file_output`.

**Пример:**
```python
reader.save_to_csv()
```

---

### `rename(new_name, same_file=True) -> bool`

Переименовывает файл.

**Параметры:**
- `new_name` (`str`): новое имя без расширения.
- `same_file` (`bool`): переименовать входной или выходной файл.

**Пример:**
```python
reader.rename("processed_data")
```

---

### `get_asis_fields() -> list[str]`

Возвращает список всех столбцов, содержащих `_asis`.

**Пример:**
```python
asis_cols = reader.get_asis_fields()
```

---

### `get_tobe_fields() -> list[str]`

Возвращает список всех столбцов, содержащих `_tobe`.

**Пример:**
```python
tobe_cols = reader.get_tobe_fields()
```

---

### `idfy_to_dataframe(idfy_data) -> list[dict]`

Преобразует `idfy`-словарь обратно в список словарей (формат датафрейма).

**Параметры:**
- `idfy_data` (`dict`): словарь вида `{ID: data}`.

**Возвращает:**
- `list[dict]`: готовый для `DataFrame` список.

---

## pretty_print(obj, title='PRETTY_PRINT', m2d=False, outputter=log)

Форматированный вывод сложных объектов (словари, списки, dataclass).

**Параметры:**
- `obj`: объект для вывода.
- `title` (`str`): заголовок лога.
- `m2d` (`bool`): преобразовать объект в словарь (через `model_to_dict`) перед выводом.
- `outputter` (`callable`): функция вывода (по умолчанию `log`).

**Пример:**
```python
from your_package.pretty_print.core import pretty_print

pretty_print({"a": 1, "b": {"c": 2}}, title="DATA")
# Вывод:
# PRETTY_PRINT
# dict(
#     a='1',
#     b=dict(
#         c='2',
#     ),
# )
```

---

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

Используется `loguru` с цветным выводом и уровнями:

| Уровень | Функция | Цвет |
|--------|--------|------|
| DEBUG | `log("msg")` | Белый |
| INFO | `loginf("msg")` | Синий |
| SUCCESS | `logsuc("msg")` | Зелёный |
| ERROR | `logerr("msg")` | Красный |

**Пример:**
```python
logsuc("Файл успешно обработан")
logerr("Ошибка валидации данных")
```

---

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

### Системные константы (в `configs/system.py`)

- `System.NULLED`: значения, считаемые "пустыми" (например, `["", "0", "none"]`).
- `System.FULL_SKIP`: значения, при которых строка полностью пропускается.
- `System.ID`: имя поля по умолчанию для ID (`"ID"`).

### Строки столбцов (в `configs/column_strings.py`)

- `ColumnStrings.DATA_LOCAL_ID`: имя столбца `local_id`.
- `ColumnStrings.DATA_SOURCE_NAME`: имя столбца `source_name`.
- `ColumnStrings.DATA_URL`: имя столбца `url`.
- `ColumnStrings.RMK`: имя столбца для заметок (`remark`).
- `ColumnStrings.DATA_ENTITY_TOBE`: поле для проверки на "нулевое" значение.

---

## Зависимости

```txt
pandas>=1.3.0
openpyxl>=3.0.0
loguru>=0.7.0
deprecated>=1.2.0
```

> Убедитесь, что Excel-файлы **не открыты в Excel** во избежание `PermissionError`.

---

## Лицензия

UNLICENSED (внутренний проект). Для использования в других проектах — требуется согласование.

---

## Дополнительно

### Обработка ошибок

- `WrongSheetListError`: ни один лист не содержит обязательных полей.
- `ContentLengthError`: данные отсутствуют или пусты.
- `PermissionError`: файл заблокирован (открыт в Excel).
- `ValueError`: ошибка при перезаписи (несоответствие размеров).

### Рекомендации

- Всегда вызывайте `cycle_right_sheet()` перед работой с данными.
- Используйте `local_idfy` вместо устаревшего `idfy_data`.
- Для отладки используйте `show_local_idfy_dataframe()`.
```

---

Теперь вы можете просто скопировать этот текст и вставить его в файл `README.md` вашего проекта. Всё отформатировано, валидно и готово к использованию.
            

Raw data

            {
    "_id": null,
    "home_page": "https://t.me/hitomamoru",
    "name": "onco-cola-utils",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.9",
    "maintainer_email": null,
    "keywords": "utils, excel, pandas, tools, openpyxl, key-param, key-value",
    "author": "Volodin Alexandr Sergeevich",
    "author_email": "endocrinologist@ya.ru",
    "download_url": "https://files.pythonhosted.org/packages/86/21/84b6d61d09d916690890a47bf20da4dbf20819b9cc79d56f14ec71cb2c02/onco_cola_utils-0.4.3.tar.gz",
    "platform": null,
    "description": "# ExcelDataProcessor\n\n\u041c\u043e\u0449\u043d\u044b\u0439 \u0438 \u0433\u0438\u0431\u043a\u0438\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u0438 \u043c\u043e\u0434\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 Excel-\u0444\u0430\u0439\u043b\u043e\u0432 \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0443\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440. \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u2014 `ReaderController` \u2014 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 XLS(X) \u0444\u0430\u0439\u043b\u0430\u043c\u0438, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u043c\u0438 \u0442\u0430\u0431\u043b\u0438\u0447\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0418\u0418-\u043c\u043e\u0434\u0435\u043b\u0435\u0439.\n\n---\n\n## \u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438\n\n- \u2705 **\u041f\u043e\u043b\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 Excel (xls/xlsx)**: \u0447\u0442\u0435\u043d\u0438\u0435, \u0437\u0430\u043f\u0438\u0441\u044c, \u043f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435, \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043b\u0438\u0441\u0442\u043e\u0432\n- \ud83d\udd0d **\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u043e\u0438\u0441\u043a \u043d\u0443\u0436\u043d\u043e\u0433\u043e \u043b\u0438\u0441\u0442\u0430** \u043f\u043e \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u043c \u043f\u043e\u043b\u044f\u043c (`source_name`, `url`)\n- \ud83e\uddf9 **\u041e\u0447\u0438\u0441\u0442\u043a\u0430 \u0438 \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445**: \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043f\u0443\u0441\u0442\u044b\u0445 \u0441\u0442\u0440\u043e\u043a, \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 TTL-\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438\n- \ud83d\udd22 **\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u0430\u044f \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0441\u0442\u0440\u043e\u043a** \u0447\u0435\u0440\u0435\u0437 `local_id` (\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u0430\u044f \u043a \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u044f\u043c)\n- \ud83d\udd04 **\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e ID**: \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0435 \u0441\u043b\u0438\u044f\u043d\u0438\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u0418\u0418-\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u043c \u0434\u0430\u0442\u0430\u0444\u0440\u0435\u0439\u043c\u043e\u043c\n- \ud83d\udee1\ufe0f **\u041f\u043e\u0442\u043e\u043a\u043e\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u044c \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043e\u043a**: \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0444\u0430\u0439\u043b\u0430\u043c\n- \ud83d\udcc4 **\u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u0432 CSV**: \u0433\u0438\u0431\u043a\u043e\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0432 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0445 \u0444\u043e\u0440\u043c\u0430\u0442\u0430\u0445\n- \ud83e\uddfe **\u041a\u0440\u0430\u0441\u0438\u0432\u044b\u0439 \u0432\u044b\u0432\u043e\u0434 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440**: \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 pretty-print \u0434\u043b\u044f \u0441\u043b\u043e\u0432\u0430\u0440\u0435\u0439, \u0441\u043f\u0438\u0441\u043a\u043e\u0432 \u0438 dataclass\n- \ud83d\udccb **\u0426\u0432\u0435\u0442\u043d\u043e\u0435 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435**: \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441 `loguru`, \u0443\u0434\u043e\u0431\u043d\u044b\u0435 \u0443\u0440\u043e\u0432\u043d\u0438 \u043b\u043e\u0433\u043e\u0432 (DEBUG, INFO, SUCCESS, ERROR)\n\n---\n\n## \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430\n\n\u042d\u0442\u043e\u0442 \u043f\u0430\u043a\u0435\u0442 \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u0430\u043a \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u0433\u043e \u043c\u043e\u0434\u0443\u043b\u044f (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 \u0441\u043e\u0441\u0442\u0430\u0432\u0435 larger ETL- \u0438\u043b\u0438 AI-\u043f\u0440\u043e\u0435\u043a\u0442\u0430). \u0414\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439:\n\n```bash\npip install pandas openpyxl loguru deprecated\n```\n\n> \u26a0\ufe0f \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0444\u0430\u0439\u043b\u044b **\u043d\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u044b \u0432 Excel**, \u0438\u043d\u0430\u0447\u0435 \u0431\u0443\u0434\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0430 \u0437\u0430\u043f\u0438\u0441\u0438.\n\n---\n\n## \u0411\u044b\u0441\u0442\u0440\u044b\u0439 \u0441\u0442\u0430\u0440\u0442\n\n```python\nfrom onco_cola_utils.reader_controller.core import ReaderController\nfrom pathlib import Path\n\n# \u041f\u0443\u0442\u0438 \u043a \u0444\u0430\u0439\u043b\u0430\u043c\ninput_file = Path(\"data/input.xlsx\")\noutput_file = Path(\"data/output.xlsx\")\n\n# \u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\nreader = ReaderController(file_path=input_file, file_output=output_file)\n\n# \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0430\u0445\u043e\u0434\u0438\u0442 \u043d\u0443\u0436\u043d\u044b\u0439 \u043b\u0438\u0441\u0442 \u0438 \u0447\u0438\u0442\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435\nreader.read_data()\n\n# \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0435\u0437 \u043f\u0443\u0441\u0442\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\nclean_data = reader.perfect_data(reader.get_data())\n\n# \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043f\u043e\u043b\u0435 \u0438\u0437 \u0418\u0418-\u043e\u0442\u0432\u0435\u0442\u0430\nai_response = [\n    {\"ID\": \"1\", \"category_asis\": \"Electronics\", \"remark\": \"1\"},\n    {\"ID\": \"2\", \"category_asis\": \"Books\", \"remark\": None},\n]\n\n# \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\nreader.update_dataframe_from_updated_dataframe(\n    updated_dataframe={1: ai_response[0], 2: ai_response[1]},\n    updated_fields=[\"category_asis\", \"remark\"]\n)\n\n# \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\nreader.update_file()\n```\n\n---\n\n## API-\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\n\n### `ReaderController(file_path, file_output, is_new=False, skip_rows=0)`\n\n\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f Excel-\u0444\u0430\u0439\u043b\u0430\u043c\u0438.\n\n**\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:**\n- `file_path` (`Path`): \u043f\u0443\u0442\u044c \u043a \u0432\u0445\u043e\u0434\u043d\u043e\u043c\u0443 \u0444\u0430\u0439\u043b\u0443.\n- `file_output` (`Path`): \u043f\u0443\u0442\u044c \u043a \u0432\u044b\u0445\u043e\u0434\u043d\u043e\u043c\u0443 \u0444\u0430\u0439\u043b\u0443 (\u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0442\u044c).\n- `is_new` (`bool`): \u0444\u043b\u0430\u0433, \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0449\u0438\u0439, \u0447\u0442\u043e \u0444\u0430\u0439\u043b \u043d\u043e\u0432\u044b\u0439 (\u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043d\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u0435).\n- `skip_rows` (`int`): \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0441\u0442\u0440\u043e\u043a \u0434\u043b\u044f \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430 \u043f\u0440\u0438 \u0447\u0442\u0435\u043d\u0438\u0438 (\u043d\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e, \u043d\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043e).\n\n**\u041f\u0440\u0438\u043c\u0435\u0440:**\n```python\nrc = ReaderController(Path(\"in.xlsx\"), Path(\"out.xlsx\"))\n```\n\n---\n\n### `read_data(sheet_name=None) -> None`\n\n\u0427\u0438\u0442\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 Excel-\u043b\u0438\u0441\u0442\u0430 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u0441\u043f\u0438\u0441\u043a\u0430 \u0441\u043b\u043e\u0432\u0430\u0440\u0435\u0439. \u0412\u0441\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u044e\u0442\u0441\u044f \u0432 \u0441\u0442\u0440\u043e\u043a\u0438 (\u0441 \u043e\u0431\u0440\u0435\u0437\u0430\u043d\u0438\u0435\u043c `.0` \u0443 \u0446\u0435\u043b\u044b\u0445 \u0447\u0438\u0441\u0435\u043b).\n\n**\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:**\n- `sheet_name` (`str | None`): \u0438\u043c\u044f \u043b\u0438\u0441\u0442\u0430. \u0415\u0441\u043b\u0438 `None` \u2014 \u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0432\u044b\u0439 \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0439 \u043b\u0438\u0441\u0442.\n\n**\u041f\u0440\u0438\u043c\u0435\u0440:**\n```python\nreader.read_data(sheet_name=\"Sheet1\")\n```\n\n---\n\n### `get_data(sheet_name=None) -> list[dict]`\n\n\u041b\u0435\u043d\u0438\u0432\u043e\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445: \u0435\u0441\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0435\u0449\u0451 \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u044b \u2014 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 `read_data()`.\n\n**\u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442:**\n- `list[dict]`: \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u0442\u0440\u043e\u043a \u0432 \u0432\u0438\u0434\u0435 \u0441\u043b\u043e\u0432\u0430\u0440\u0435\u0439 (\u043a\u043b\u044e\u0447 \u2014 \u0438\u043c\u044f \u0441\u0442\u043e\u043b\u0431\u0446\u0430).\n\n**\u041f\u0440\u0438\u043c\u0435\u0440:**\n```python\ndata = reader.get_data()\n```\n\n---\n\n### `perfect_data(data_list) -> dict`\n\n\u0424\u0438\u043b\u044c\u0442\u0440\u0443\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435, \u0443\u0434\u0430\u043b\u044f\u044f \u0441\u0442\u0440\u043e\u043a\u0438, \u0433\u0434\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 \u043f\u043e\u043b\u0435 `DATA_ENTITY_TOBE` \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \"\u043d\u0443\u043b\u0435\u0432\u044b\u043c\" (\u0441\u043e\u0433\u043b\u0430\u0441\u043d\u043e `System.NULLED`).\n\n**\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:**\n- `data_list` (`list[dict]`): \u0432\u0445\u043e\u0434\u043d\u043e\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0434\u0430\u043d\u043d\u044b\u0445.\n\n**\u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442:**\n- `dict`: \u0441\u043b\u043e\u0432\u0430\u0440\u044c, \u0438\u043d\u0434\u0435\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 `local_id`.\n\n**\u041f\u0440\u0438\u043c\u0435\u0440:**\n```python\nfiltered = reader.perfect_data(reader.get_data())\n```\n\n---\n\n### `local_idfy(data_list) -> dict`\n\n\u041f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u0435\u0442 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u044b \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e `local_id` \u0438\u0437 \u0434\u0430\u043d\u043d\u044b\u0445.\n\n**\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:**\n- `data_list` (`list[dict]`): \u0441\u043f\u0438\u0441\u043e\u043a \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u043f\u043e\u043b\u0435\u043c `local_id`.\n\n**\u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442:**\n- `dict`: \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0432\u0438\u0434\u0430 `{local_id: row_data}`.\n\n**\u041f\u0440\u0438\u043c\u0435\u0440:**\n```python\nidfy_data = reader.local_idfy(data_list)\n```\n\n---\n\n### `cycle_right_sheet() -> None`\n\n\u0426\u0438\u043a\u043b\u0438\u0447\u0435\u0441\u043a\u0438 \u0443\u0434\u0430\u043b\u044f\u0435\u0442 \u043f\u0435\u0440\u0432\u044b\u0435 \u043b\u0438\u0441\u0442\u044b Excel-\u0444\u0430\u0439\u043b\u0430, \u043f\u043e\u043a\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0451\u0442 \u043b\u0438\u0441\u0442, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u043f\u043e\u043b\u044f `source_name` \u0438 `url`.\n\n**\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435:**\n- \u041f\u043e\u043b\u0435\u0437\u043d\u043e \u043f\u0440\u0438 \u043d\u0430\u043b\u0438\u0447\u0438\u0438 \u0441\u043b\u0443\u0436\u0435\u0431\u043d\u044b\u0445 \u043b\u0438\u0441\u0442\u043e\u0432 \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u0444\u0430\u0439\u043b\u0430.\n- \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0435\u0440\u0435\u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u0444\u0430\u0439\u043b \u0431\u0435\u0437 \u043d\u0435\u043d\u0443\u0436\u043d\u044b\u0445 \u043b\u0438\u0441\u0442\u043e\u0432.\n\n**\u041f\u0440\u0438\u043c\u0435\u0440:**\n```python\nreader.cycle_right_sheet()  # \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432 check_local_id\n```\n\n---\n\n### `check_local_id(find_it=True) -> bool`\n\n\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043b\u0438 \u043f\u0435\u0440\u0432\u044b\u0439 \u043b\u0438\u0441\u0442 \u043f\u043e\u043b\u0435 `local_id`. \u041f\u0435\u0440\u0435\u0434 \u044d\u0442\u0438\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 `cycle_right_sheet`.\n\n**\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:**\n- `find_it` (`bool`): \u0435\u0441\u043b\u0438 `True`, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043d\u0430\u043b\u0438\u0447\u0438\u0435 `local_id`; \u0438\u043d\u0430\u0447\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0441\u0442\u044c \u043b\u0438\u0441\u0442\u0430.\n\n**\u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442:**\n- `bool`: `True`, \u0435\u0441\u043b\u0438 `local_id` \u043d\u0430\u0439\u0434\u0435\u043d.\n\n**\u041f\u0440\u0438\u043c\u0435\u0440:**\n```python\nif not reader.check_local_id():\n    reader.process_local_idfying()\n```\n\n---\n\n### `process_local_idfying(field=ColumnStrings.DATA_LOCAL_ID) -> None`\n\n\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 `local_id` (1, 2, 3...) \u043a \u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u0444\u0430\u0439\u043b.\n\n**\u041f\u0440\u0438\u043c\u0435\u0440:**\n```python\nreader.process_local_idfying()\n```\n\n---\n\n### `update_dataframe_from_updated_dataframe(updated_dataframe, updated_fields, field_id=\"ID\") -> bool | None`\n\n\u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0434\u0430\u0442\u0430\u0444\u0440\u0435\u0439\u043c \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u043b\u043e\u0432\u0430\u0440\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0451\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0442\u0432\u0435\u0442\u0430 \u0418\u0418).\n\n**\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:**\n- `updated_dataframe` (`dict[int, dict]`): \u0441\u043b\u043e\u0432\u0430\u0440\u044c `{ID: {field: value}}`.\n- `updated_fields` (`list[str]`): \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u043b\u0435\u0439 \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f.\n- `field_id` (`str`): \u043a\u043b\u044e\u0447, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0441\u0442\u0440\u043e\u043a\u0438 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e `\"ID\"`).\n\n**\u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442:**\n- `True` \u043f\u0440\u0438 \u0443\u0441\u043f\u0435\u0445\u0435, `None` \u043f\u0440\u0438 \u043e\u0448\u0438\u0431\u043a\u0430\u0445.\n\n**\u041f\u0440\u0438\u043c\u0435\u0440:**\n```python\nreader.update_dataframe_from_updated_dataframe(\n    updated_dataframe={1: {\"cat_asis\": \"NewCat\"}},\n    updated_fields=[\"cat_asis\"]\n)\n```\n\n---\n\n### `update_file(same_file=True) -> bool`\n\n\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0434\u0430\u0442\u0430\u0444\u0440\u0435\u0439\u043c \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432 Excel.\n\n**\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:**\n- `same_file` (`bool`): \u0435\u0441\u043b\u0438 `True`, \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 `file_path`, \u0438\u043d\u0430\u0447\u0435 \u2014 `file_output`.\n\n**\u041f\u0440\u0438\u043c\u0435\u0440:**\n```python\nreader.update_file(same_file=False)  # \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 output\n```\n\n---\n\n### `save_to_csv(same_file=True) -> bool`\n\n\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 CSV.\n\n**\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:**\n- `same_file` (`bool`): \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c `file_path` \u0438\u043b\u0438 `file_output`.\n\n**\u041f\u0440\u0438\u043c\u0435\u0440:**\n```python\nreader.save_to_csv()\n```\n\n---\n\n### `rename(new_name, same_file=True) -> bool`\n\n\u041f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u043e\u0432\u044b\u0432\u0430\u0435\u0442 \u0444\u0430\u0439\u043b.\n\n**\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:**\n- `new_name` (`str`): \u043d\u043e\u0432\u043e\u0435 \u0438\u043c\u044f \u0431\u0435\u0437 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f.\n- `same_file` (`bool`): \u043f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u0442\u044c \u0432\u0445\u043e\u0434\u043d\u043e\u0439 \u0438\u043b\u0438 \u0432\u044b\u0445\u043e\u0434\u043d\u043e\u0439 \u0444\u0430\u0439\u043b.\n\n**\u041f\u0440\u0438\u043c\u0435\u0440:**\n```python\nreader.rename(\"processed_data\")\n```\n\n---\n\n### `get_asis_fields() -> list[str]`\n\n\u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0441\u0435\u0445 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0445 `_asis`.\n\n**\u041f\u0440\u0438\u043c\u0435\u0440:**\n```python\nasis_cols = reader.get_asis_fields()\n```\n\n---\n\n### `get_tobe_fields() -> list[str]`\n\n\u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0441\u0435\u0445 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0445 `_tobe`.\n\n**\u041f\u0440\u0438\u043c\u0435\u0440:**\n```python\ntobe_cols = reader.get_tobe_fields()\n```\n\n---\n\n### `idfy_to_dataframe(idfy_data) -> list[dict]`\n\n\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442 `idfy`-\u0441\u043b\u043e\u0432\u0430\u0440\u044c \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432 \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u043b\u043e\u0432\u0430\u0440\u0435\u0439 (\u0444\u043e\u0440\u043c\u0430\u0442 \u0434\u0430\u0442\u0430\u0444\u0440\u0435\u0439\u043c\u0430).\n\n**\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:**\n- `idfy_data` (`dict`): \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0432\u0438\u0434\u0430 `{ID: data}`.\n\n**\u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442:**\n- `list[dict]`: \u0433\u043e\u0442\u043e\u0432\u044b\u0439 \u0434\u043b\u044f `DataFrame` \u0441\u043f\u0438\u0441\u043e\u043a.\n\n---\n\n## pretty_print(obj, title='PRETTY_PRINT', m2d=False, outputter=log)\n\n\u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434 \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 (\u0441\u043b\u043e\u0432\u0430\u0440\u0438, \u0441\u043f\u0438\u0441\u043a\u0438, dataclass).\n\n**\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:**\n- `obj`: \u043e\u0431\u044a\u0435\u043a\u0442 \u0434\u043b\u044f \u0432\u044b\u0432\u043e\u0434\u0430.\n- `title` (`str`): \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u043b\u043e\u0433\u0430.\n- `m2d` (`bool`): \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442 \u0432 \u0441\u043b\u043e\u0432\u0430\u0440\u044c (\u0447\u0435\u0440\u0435\u0437 `model_to_dict`) \u043f\u0435\u0440\u0435\u0434 \u0432\u044b\u0432\u043e\u0434\u043e\u043c.\n- `outputter` (`callable`): \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432\u044b\u0432\u043e\u0434\u0430 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e `log`).\n\n**\u041f\u0440\u0438\u043c\u0435\u0440:**\n```python\nfrom your_package.pretty_print.core import pretty_print\n\npretty_print({\"a\": 1, \"b\": {\"c\": 2}}, title=\"DATA\")\n# \u0412\u044b\u0432\u043e\u0434:\n# PRETTY_PRINT\n# dict(\n#     a='1',\n#     b=dict(\n#         c='2',\n#     ),\n# )\n```\n\n---\n\n## \u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\n\n\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f `loguru` \u0441 \u0446\u0432\u0435\u0442\u043d\u044b\u043c \u0432\u044b\u0432\u043e\u0434\u043e\u043c \u0438 \u0443\u0440\u043e\u0432\u043d\u044f\u043c\u0438:\n\n| \u0423\u0440\u043e\u0432\u0435\u043d\u044c | \u0424\u0443\u043d\u043a\u0446\u0438\u044f | \u0426\u0432\u0435\u0442 |\n|--------|--------|------|\n| DEBUG | `log(\"msg\")` | \u0411\u0435\u043b\u044b\u0439 |\n| INFO | `loginf(\"msg\")` | \u0421\u0438\u043d\u0438\u0439 |\n| SUCCESS | `logsuc(\"msg\")` | \u0417\u0435\u043b\u0451\u043d\u044b\u0439 |\n| ERROR | `logerr(\"msg\")` | \u041a\u0440\u0430\u0441\u043d\u044b\u0439 |\n\n**\u041f\u0440\u0438\u043c\u0435\u0440:**\n```python\nlogsuc(\"\u0424\u0430\u0439\u043b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\")\nlogerr(\"\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445\")\n```\n\n---\n\n## \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\n\n### \u0421\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0435 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b (\u0432 `configs/system.py`)\n\n- `System.NULLED`: \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u0441\u0447\u0438\u0442\u0430\u0435\u043c\u044b\u0435 \"\u043f\u0443\u0441\u0442\u044b\u043c\u0438\" (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, `[\"\", \"0\", \"none\"]`).\n- `System.FULL_SKIP`: \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u043f\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u0442\u0440\u043e\u043a\u0430 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f.\n- `System.ID`: \u0438\u043c\u044f \u043f\u043e\u043b\u044f \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0434\u043b\u044f ID (`\"ID\"`).\n\n### \u0421\u0442\u0440\u043e\u043a\u0438 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432 (\u0432 `configs/column_strings.py`)\n\n- `ColumnStrings.DATA_LOCAL_ID`: \u0438\u043c\u044f \u0441\u0442\u043e\u043b\u0431\u0446\u0430 `local_id`.\n- `ColumnStrings.DATA_SOURCE_NAME`: \u0438\u043c\u044f \u0441\u0442\u043e\u043b\u0431\u0446\u0430 `source_name`.\n- `ColumnStrings.DATA_URL`: \u0438\u043c\u044f \u0441\u0442\u043e\u043b\u0431\u0446\u0430 `url`.\n- `ColumnStrings.RMK`: \u0438\u043c\u044f \u0441\u0442\u043e\u043b\u0431\u0446\u0430 \u0434\u043b\u044f \u0437\u0430\u043c\u0435\u0442\u043e\u043a (`remark`).\n- `ColumnStrings.DATA_ENTITY_TOBE`: \u043f\u043e\u043b\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043d\u0430 \"\u043d\u0443\u043b\u0435\u0432\u043e\u0435\" \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.\n\n---\n\n## \u0417\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438\n\n```txt\npandas>=1.3.0\nopenpyxl>=3.0.0\nloguru>=0.7.0\ndeprecated>=1.2.0\n```\n\n> \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e Excel-\u0444\u0430\u0439\u043b\u044b **\u043d\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u044b \u0432 Excel** \u0432\u043e \u0438\u0437\u0431\u0435\u0436\u0430\u043d\u0438\u0435 `PermissionError`.\n\n---\n\n## \u041b\u0438\u0446\u0435\u043d\u0437\u0438\u044f\n\nUNLICENSED (\u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442). \u0414\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u2014 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0441\u043e\u0433\u043b\u0430\u0441\u043e\u0432\u0430\u043d\u0438\u0435.\n\n---\n\n## \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\n\n### \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a\n\n- `WrongSheetListError`: \u043d\u0438 \u043e\u0434\u0438\u043d \u043b\u0438\u0441\u0442 \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u043e\u043b\u0435\u0439.\n- `ContentLengthError`: \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u0438\u043b\u0438 \u043f\u0443\u0441\u0442\u044b.\n- `PermissionError`: \u0444\u0430\u0439\u043b \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d (\u043e\u0442\u043a\u0440\u044b\u0442 \u0432 Excel).\n- `ValueError`: \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u0438 (\u043d\u0435\u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u0432).\n\n### \u0420\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0430\u0446\u0438\u0438\n\n- \u0412\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0439\u0442\u0435 `cycle_right_sheet()` \u043f\u0435\u0440\u0435\u0434 \u0440\u0430\u0431\u043e\u0442\u043e\u0439 \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438.\n- \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 `local_idfy` \u0432\u043c\u0435\u0441\u0442\u043e \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0435\u0433\u043e `idfy_data`.\n- \u0414\u043b\u044f \u043e\u0442\u043b\u0430\u0434\u043a\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 `show_local_idfy_dataframe()`.\n```\n\n---\n\n\u0422\u0435\u043f\u0435\u0440\u044c \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u0442\u0435\u043a\u0441\u0442 \u0438 \u0432\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0432 \u0444\u0430\u0439\u043b `README.md` \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u0412\u0441\u0451 \u043e\u0442\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043e, \u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0438 \u0433\u043e\u0442\u043e\u0432\u043e \u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044e.",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "\u041c\u043e\u0438 \u043e\u0431\u0449\u0438\u0435 \u0443\u0442\u0438\u043b\u0438\u0442\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Excel",
    "version": "0.4.3",
    "project_urls": {
        "Homepage": "https://t.me/hitomamoru",
        "Repository": "https://github.com/VolodinAS/onco-cola-tools"
    },
    "split_keywords": [
        "utils",
        " excel",
        " pandas",
        " tools",
        " openpyxl",
        " key-param",
        " key-value"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "a391b452c5af87a56b61ff8e1ff74ce9c003595566d63bb34eb1f927aa9ea566",
                "md5": "c6ffd5a3acb92353dd5cbac4c321b288",
                "sha256": "f49703267d66628f87d2c682e269d1de2899c424d91963bc3650cc083616f454"
            },
            "downloads": -1,
            "filename": "onco_cola_utils-0.4.3-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "c6ffd5a3acb92353dd5cbac4c321b288",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.9",
            "size": 35295,
            "upload_time": "2025-09-04T07:56:01",
            "upload_time_iso_8601": "2025-09-04T07:56:01.267939Z",
            "url": "https://files.pythonhosted.org/packages/a3/91/b452c5af87a56b61ff8e1ff74ce9c003595566d63bb34eb1f927aa9ea566/onco_cola_utils-0.4.3-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "862184b6d61d09d916690890a47bf20da4dbf20819b9cc79d56f14ec71cb2c02",
                "md5": "0d06720bb5c9ede3b424258c98a6f18b",
                "sha256": "b4bf19f2a7de4e6a312c72dc3505d2f7c2bb4485aea270d791054c10de146b63"
            },
            "downloads": -1,
            "filename": "onco_cola_utils-0.4.3.tar.gz",
            "has_sig": false,
            "md5_digest": "0d06720bb5c9ede3b424258c98a6f18b",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.9",
            "size": 33771,
            "upload_time": "2025-09-04T07:56:02",
            "upload_time_iso_8601": "2025-09-04T07:56:02.578775Z",
            "url": "https://files.pythonhosted.org/packages/86/21/84b6d61d09d916690890a47bf20da4dbf20819b9cc79d56f14ec71cb2c02/onco_cola_utils-0.4.3.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-09-04 07:56:02",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "VolodinAS",
    "github_project": "onco-cola-tools",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "onco-cola-utils"
}
        
Elapsed time: 0.64916s