Metadata-Version: 2.4
Name: sentexts
Version: 0.4.1
Summary: Local text storage, reads and explicit uploads with Yandex Object Storage
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: boto3<2,>=1.40.65
Requires-Dist: python-dotenv<2,>=1.0

# sentexts

Python-библиотека для работы с текстами судебных решений в Yandex Object Storage.
Позволяет скачивать и читать `.txt`, отправлять свои тексты в хранилище и удалять
локальные копии.

## Установка

Требуется Python 3.10 или новее. Из корня проекта:

```bash
python -m venv .venv
# Linux/macOS:
source .venv/bin/activate
# Windows PowerShell:
# .venv\Scripts\Activate.ps1
python -m pip install .
```

В PyCharm выберите Python из созданной `.venv`.

## Настройка

```python
from sentexts import Sentexts

texts = Sentexts()
print(texts.settings.config_path)
```

При первом импорте создаются отсутствующие `config.json` и `.env`. Команда выше
покажет расположение конфигурации; по умолчанию `.env` находится рядом с ней.
Существующие файлы сохраняются.

Для скачивания текстов укажите ключ с правом чтения в `.env`:

```dotenv
AWS_ACCESS_KEY_ID=YOUR_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY=YOUR_SECRET_ACCESS_KEY
```

Ввести или изменить ключи можно и через API:

```python
texts.update_env()
```

Метод последовательно запросит Access Key и Secret Access Key. Символы при вводе
в терминале не отображаются. Оба значения обязательны и сразу сохраняются
в выбранный `.env`; вызывать `save_settings()` для них не нужно.

Для отправки текстов нужен ключ с правом записи. Для шаблона есть `.env.example`.
Не включайте файл с ключом в Git и не сохраняйте ключ в JSON.

Папку локальных копий и адрес хранилища можно задать из Python:

```python
from pathlib import Path

Path("my_texts").mkdir(exist_ok=True)
texts.update_settings(
    storage_dir="my_texts",
    cloud_folder="s3://sentexts-corpus-73fa/Судебные решения",
)
texts.save_settings()
```

- `storage_dir` — существующая папка, предназначенная только для текстов.
  Допустимы строка и `Path`, включая Windows-пути. Относительный путь считается
  от текущего рабочего каталога и фиксируется при выборе папки.
- `cloud_folder` — адрес вида `s3://бакет/папка`. Для корня хранилища используйте
  `s3://sentexts-corpus-73fa/`. Пробелы, Unicode и `%` передавайте как есть.

Если настройки ещё не заполнены, используются `./texts` и
`s3://sentexts-corpus-73fa/texts/`, с предупреждением. Папка `./texts` создаётся
при необходимости. Для ручного редактирования JSON есть `config.example.json`.

`update_settings()` проверяет весь набор параметров до изменения настроек.
Сохранение в JSON выполняется отдельно через `save_settings()`. Смена локальной
папки не перемещает и не удаляет существующие файлы.

Чтобы выбрать другое расположение конфигурации и ключа, задайте пути до импорта:

```python
import os

os.environ["CONFIG_PATH"] = "settings/config.json"
os.environ["ENV_PATH"] = "settings/.env"

from sentexts import Sentexts

texts = Sentexts()
```

## Чтение текстов

```python
one = texts.read("решение")
# {"решение.txt": "текст решения"}

many = texts.read(["решение", "другое решение.TXT"])
present = texts.exists("решение.txt")
names = texts.list_local_files()
```

`read()` возвращает словарь `имя файла → текст`. Если локальная копия уже есть,
она читается без обращения к хранилищу. Иначе файл скачивается в `storage_dir`.
В `read()` можно передать имя без расширения — после проверки к нему добавится
`.txt`. Полные имена с `.txt`/`.TXT` сохраняются как переданы; другие расширения
не поддерживаются. Ключи результата всегда содержат полные имена файлов.
Поддерживаются Unicode и пробелы. `list_local_files()` возвращает
отсортированный список имён.

По умолчанию используется UTF-8. Другую кодировку можно указать явно:

```python
texts.read("решение.txt", encoding="cp1251")
```

Если файла нет в хранилище, выдаётся `CloudFileNotFoundWarning`: имя пропускается,
а чтение остальных файлов продолжается. При запросе единственного отсутствующего
файла результат — `{}`. Если самого хранилища нет, возникает `CloudFolderError`.
Отсутствие файлов по выбранному префиксу также приводит к предупреждению о файле.

При другой ошибке чтение останавливается. Уже скачанные полные копии остаются;
неполные файлы не становятся локальными копиями. Существующие копии автоматически
не обновляются из облака.

## Отправка текстов

```python
from pathlib import Path

uploaded = texts.upload(Path("to_upload/решение.txt"))
# {"решение.txt": "s3://sentexts-corpus-73fa/решение.txt"}

texts.upload([
    Path("to_upload/первое.txt"),
    Path("to_upload/второе.TXT"),
])
```

`upload()` принимает один путь или набор путей и возвращает словарь
`имя файла → адрес в хранилище`. Файлы отправляются в выбранный `cloud_folder`
под своими именами. Локальные подпапки в адрес не включаются.

Поддерживаются обычные `.txt`/`.TXT` размером до 5 ГиБ. Весь список проверяется
до начала отправки. Файлы из разных папок с одинаковым именем не допускаются;
повтор одного и того же пути отправляется один раз.

По умолчанию существующий файл не заменяется: возникает `CloudFileExistsError`.
Чтобы разрешить замену, укажите:

```python
texts.upload(Path("to_upload/решение.txt"), overwrite=True)
```

При ошибке отправка останавливается, а уже отправленные файлы остаются
в хранилище. Исходные файлы и локальные копии не меняются.

После замены облачного файла локальная копия продолжит использоваться при
чтении. Чтобы скачать обновлённый текст:

```python
texts.cleanup(["решение.txt"])
texts.read("решение.txt")
```

## Удаление локальных копий

```python
deleted = texts.cleanup(["решение.txt", "другое решение.TXT"])
# список pathlib.Path удалённых файлов

texts.cleanup([])  # ничего не удаляет
texts.cleanup()    # удалить все локальные .txt/.TXT
```

Очистка выполняется только по вызову пользователя и затрагивает выбранную локальную
папку. Файлы в облаке не удаляются. Подкаталоги и символические ссылки
пропускаются.

Все имена проверяются до удаления. Повторы и отсутствующие файлы пропускаются.
Если файлов несколько, то передавать их нужно списком. При ошибке удаления операция
останавливается; уже удалённые копии не восстанавливаются.

## Ошибки

Ошибки облачного доступа доступны в `sentexts.cloud`:

| Ошибка | Значение |
| --- | --- |
| `MissingCredentialsError` | В выбранном `.env` не указан ключ. |
| `CloudAuthError` | Ключ не прошёл проверку. |
| `CloudPermissionError` | У ключа нет нужных прав. |
| `CloudFolderError` | Ошибочный или отсутствующий бакет. |
| `CloudConnectionError` | Не удалось обратиться к хранилищу. |
| `CloudFileExistsError` | Файл уже существует, а замена не разрешена. |
| `CloudError` | Другая ошибка облачного доступа. |

`CloudFileNotFoundWarning` — предупреждение о пропущенном файле; им можно
управлять стандартным модулем `warnings`.

Неверные настройки вызывают `sentexts.config.ConfigError`, небезопасные имена —
`sentexts.storage.UnsafePathError`. Ошибки локальной файловой системы и
декодирования текста передаются вызывающему коду.
