SensoryDataClient — это изолированный, портируемый Python-модуль, предоставляющий единый асинхронный интерфейс для работы с объектным хранилищем MinIO и реляционной базой данных PostgreSQL. Он спроектирован как надежный фундамент для сервисов, которым требуется атомарно управлять не только файлами и их метаданными, но и их структурированным содержимым в виде строк.
Предоставить разработчикам production-ready инструмент, который инкапсулирует всю сложность взаимодействия с хранилищами данных, предлагая простой, типобезопасный и асинхронный API для управления как целыми документами, так и их составными частями.
- Асинхронность "из коробки": Построен на
asyncio,SQLAlchemy 2.0 (async),asyncpgи асинхронных обертках над SDK MinIO. - Единый фасад (
DataClient): Один класс для управления файлами, их метаданными и структурированными строками. Больше не нужно жонглировать несколькими клиентами в бизнес-логике. - Управление строками документа (
Document Lines): Возможность сохранять, обновлять и запрашивать разобранное содержимое документа (текст, код, ссылки на изображения) с указанием типа и точной позиции (float), что идеально подходит для реализации diff-обновлений и RAG-пайплайнов. - Атомарные операции: Метод
upload_fileгарантирует, что метаданные не появятся в БД без соответствующего файла в MinIO (с автоматическим откатом). Операции со строками также транзакционны. - Типобезопасность: Активное использование
Pydanticдля моделей данных и 100% покрытие кода тайп-хинтами. - Готовность к развертыванию: Настройка через переменные окружения, структурированное JSON-логирование и удобный CLI-интерфейс на базе
Typer. - Простота для разработчика: Готовое Docker Compose окружение для локального тестирования и CLI для быстрой инициализации.
Клиент реализует паттерн Фасад, скрывая детали работы с конкретными репозиториями. В отличие от предыдущей версии, работа с PostgreSQL теперь разделена на два репозитория для лучшего разделения ответственности (SRP).
┌────────────────────────────────┐
│ Ваш Сервис (Business Logic) │
└───────────────┬────────────────┘
│
┌───────────────▼────────────────┐
│ DataClient │ <- Единая точка входа
└───────────────┬────────────────┘
┌─────────┴─────────┬────────────────┐
│ │ │
┌─────▼─────┐ ┌─────▼──────┐ ┌─────▼──────┐
│ MinIO │ │ MetaData │ │ Line │
│ Repository│ │ Repository │ │ Repository │
└───────────┘ └────────────┘ └────────────┘
(MinIO SDK) (SQLAlchemy) (SQLAlchemy)
Для использования клиента в других проектах предполагается его установка из приватного репозитория или локально.
Шаг 1: Клонируйте репозиторий
git clone https://github.com/sensoryfox/datafileclient.git
cd datafileclientШаг 2: Установите зависимости Эта команда установит все необходимые библиотеки для работы клиента и для его разработки (тесты, линтеры).
pip install -e ".[dev]"┌───────────────────┐ ┌───────────────────┐
│ StoredFileORM │──────▶│ DocumentORM │
│ (stored_files) │ 1..1 │ (documents) │
└───────────────────┘ └─────────┬─────────┘
│ 1..N
▼
┌───────────────────┐
│ RawLineORM │ <- Главная "лента" контента
│ (raw_lines) │
└─────────┬─────────┘
┌──────────────────────────┼──────────────────────────┐
│ 1..0/1 │ 1..0/1 │ 1..0/1
▼ ▼ ▼
┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐
│ DocumentLineORM │ │ ImageLineORM │ │ AudioLineORM │
│ (lines_document) │ │ (lines_image) │ │ (lines_audio) │
└───────────────────┘ └───────────────────┘ └───────────────────┘
(геометрия, страница) (путь в S3, alt-text) (таймкоды, спикер)
Для разработки и тестирования клиента вам понадобятся запущенные экземпляры PostgreSQL и MinIO. Мы предоставляем готовую конфигурацию docker-compose.
Шаг 1: Создайте файл с переменными окружения Скопируйте пример. Для локального запуска менять ничего не нужно.
cp .env.example .envВажно: Файл
.envдолжен содержатьPOSTGRES_HOST=localhostиMINIO_ENDPOINT=localhost:9000для подключения с вашего компьютера к контейнерам.
Шаг 2: Запустите окружение Эта команда поднимет контейнеры с PostgreSQL и MinIO.
docker-compose up -dПосле запуска:
- Веб-консоль MinIO будет доступна по адресу
http://localhost:9001. - PostgreSQL будет доступен по адресу
localhost:5432.
Шаг 3: Инициализируйте окружение
Эта команда создаст таблицы в PostgreSQL (documents, document_lines) и бакет в MinIO.
python -m sensory_data_client initШаг 4: Проверьте соединения Убедитесь, что ваш локальный Python-код может достучаться до сервисов в Docker.
python -m sensory_data_client checkВы должны увидеть:
✅ Connection Check
✔ PostgreSQL connection: OK
✔ MinIO connection: OK (bucket: 'test-bucket')import asyncio
from uuid import uuid4
from sensory_data_client import create_data_client, DocumentCreate, DocumentMetadata, Line
# --- 1. Инициализация клиента через фабрику ---
client = create_data_client()
# --- 2. Модель метаданных ---
doc_meta = DocumentCreate(
user_document_id="ext-id-456",
name="Project Proposal.md",
owner="user-02",
access_group="engineering",
extension="md"
)
# --- 3. Контент файла ---
file_content = b"# Project Sensory\n\nThis is the main proposal document."
# --- 4. Структурированные строки из файла ---
document_lines = [
Line(block_id="block-1", position=1.0, type="header", content="# Project Sensory"),
Line(block_id="block-2", position=2.0, type="paragraph", content="This is the main proposal document."),
]
async def main():
"""Основной сценарий использования клиента."""
new_doc_id = None
try:
# --- 5. Загрузка файла и метаданных ---
print("Uploading file...")
new_doc = await client.upload_file(
file_name="proposal.md",
content=file_content,
meta=doc_meta
)
new_doc_id = new_doc.id
print(f"Document created with ID: {new_doc_id}")
# --- 6. Сохранение разобранных строк документа ---
print("\nSaving document lines...")
await client.save_document_lines(new_doc_id, document_lines)
print(f"Saved {len(document_lines)} lines.")
# --- 7. Получение строк документа из БД ---
print("\nFetching document lines...")
retrieved_lines = await client.list_doclines(new_doc_id)
assert len(retrieved_lines) == len(document_lines)
assert retrieved_lines[0].content == "# Project Sensory"
print("Lines match!")
# --- 8. Генерация временной ссылки на скачивание ---
print("\nGenerating presigned URL for the original file...")
download_url = await client.generate_download_url(new_doc_id, expires_in=60)
print(f"URL (expires in 60s): {download_url}")
except Exception as e:
print(f"An error occurred: {e}")
finally:
# --- 9. Удаление документа для очистки ---
if new_doc_id:
print(f"\nDeleting document {new_doc_id}...")
# Удаление метаданных и файла. Строки будут удалены каскадно или их нужно удалять отдельно.
await client.delete_file(new_doc_id)
print("Document deleted successfully.")
if __name__ == "__main__":
asyncio.run(main())Клиент настраивается через переменные окружения. Значения по умолчанию можно найти в файле .env.example.
| Переменная | Описание | Значение по умолчанию |
|---|---|---|
POSTGRES_HOST |
Хост PostgreSQL | localhost |
POSTGRES_PORT |
Порт PostgreSQL | 5432 |
POSTGRES_DB |
Имя базы данных | documents |
POSTGRES_USER |
Пользователь PostgreSQL | postgres |
POSTGRES_PASSWORD |
Пароль пользователя PostgreSQL | postgres |
MINIO_ENDPOINT |
Адрес MinIO (хост:порт) | localhost:9000 |
MINIO_ACCESS_KEY |
Ключ доступа MinIO | minioadmin |
MINIO_SECRET_KEY |
Секретный ключ MinIO | minioadmin |
MINIO_BUCKET |
Имя бакета для хранения файлов | documents |
MINIO_SECURE |
Использовать TLS для MinIO (True/False) |
False |
LOG_LEVEL |
Уровень логирования (INFO, DEBUG, ...) |
INFO |
Для удобства разработки в клиент встроен CLI на базе Typer. Вызывается как модуль.
- Инициализировать сервисы: Создает таблицы в БД и бакет в MinIO.
python -m sensory_data_client init- Проверить соединения: Проверяет доступность PostgreSQL и MinIO с текущими настройками.
python -m sensory_data_client checkПроект использует testcontainers для запуска интеграционных тестов в изолированном окружении с реальными версиями PostgreSQL и MinIO.
Для запуска всех тестов используйте pytest:
pytestТесты автоматически поднимут необходимые Docker-контейнеры, выполнят проверки и остановят их после завершения.