> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aigenda.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Полный пример

> Скрипт на Python: вход, проверка минут, загрузка, обработка, протокол в файл.

Скрипт загружает файл в личное пространство, ждёт обработку и сохраняет протокол рядом с файлом. Нужен Python 3.9+ и пакет `requests`.

```bash theme={null}
pip install requests
export AIGENDA_EMAIL="you@example.com"
export AIGENDA_PASSWORD="ваш-пароль"
python aigenda_upload.py "Планёрка отдела продаж.mp3" audio/mpeg
```

```python aigenda_upload.py theme={null}
"""Загрузка записи в AiGenda и получение протокола.

Использование: python aigenda_upload.py <файл> <content_type>
"""
import math
import os
import sys
import time

import requests

API = "https://api.aigenda.tech"
MIN_RESERVE_SECONDS = 5 * 60  # запас минут сверх длительности записи


def login(email: str, password: str) -> str:
    for attempt in range(3):
        r = requests.post(f"{API}/api/token/", json={"username": email, "password": password}, timeout=30)
        if r.status_code == 401:
            raise SystemExit("Неверная почта или пароль")
        if r.ok and r.headers.get("Content-Type", "").startswith("application/json"):
            return r.json()["access"]
        time.sleep(1.5)
    r.raise_for_status()
    raise RuntimeError("Не удалось войти")


def main_space(headers: dict) -> dict:
    r = requests.get(f"{API}/api/spaces/", params={"limit": 50, "offset": 0}, headers=headers, timeout=30)
    r.raise_for_status()
    return next(item["space"] for item in r.json()["results"] if item["is_main_space"])


def available_seconds(headers: dict, space_id: int) -> int:
    r = requests.get(f"{API}/api/spaces/{space_id}/", headers=headers, timeout=30)
    r.raise_for_status()
    return r.json()["subscribe"]["available_usage_count"]


def upload_file(headers: dict, space_id: int, path: str, content_type: str) -> int:
    size = os.path.getsize(path)
    r = requests.post(
        f"{API}/api/spaces/{space_id}/audio/",
        headers=headers,
        json={"title": os.path.basename(path), "size": size, "content_type": content_type},
        timeout=30,
    )
    r.raise_for_status()
    audio = r.json()

    part_size = math.ceil(size / len(audio["url"]))
    parts = []
    with open(path, "rb") as f:
        for number, url in enumerate(audio["url"], start=1):
            put = requests.put(url, data=f.read(part_size), timeout=900)
            put.raise_for_status()
            parts.append({"PartNumber": number, "ETag": put.headers["ETag"]})

    r = requests.post(
        f"{API}/api/spaces/{space_id}/audio/{audio['id']}/complete_upload/",
        headers=headers,
        json={"parts": parts},
        timeout=30,
    )
    r.raise_for_status()

    for _ in range(60):
        info = requests.get(f"{API}/api/spaces/{space_id}/audio/{audio['id']}/info/", headers=headers, timeout=30)
        if info.ok and info.json().get("is_uploaded"):
            return audio["id"]
        time.sleep(2)
    raise TimeoutError("AiGenda не подтвердила файл за 2 минуты")


def start_processing(headers: dict, audio_id: int) -> None:
    r = requests.post(f"{API}/api/audio/classify/", headers=headers, json={"audio_id": audio_id}, timeout=30)
    r.raise_for_status()


def wait_for_result(headers: dict, space_id: int, audio_id: int, timeout: int = 3600) -> dict:
    url = f"{API}/api/spaces/{space_id}/audio/{audio_id}/result/"
    started = time.monotonic()
    not_found_since = failing_since = None
    while time.monotonic() - started < timeout:
        r = requests.get(url, headers=headers, timeout=30)
        now = time.monotonic()
        if r.status_code == 200:
            not_found_since = failing_since = None
            body = r.json()
            if body.get("classified") and body.get("result"):
                return body
        elif r.status_code == 404:
            not_found_since = not_found_since or now
            if now - not_found_since > 60:
                raise RuntimeError("Обработка не запущена")
        elif r.status_code >= 500:
            failing_since = failing_since or now
            if now - failing_since > 180:
                raise RuntimeError("Обработка упала, минуты вернутся автоматически")
        time.sleep(15)
    raise TimeoutError("Результат не готов за час")


def main() -> None:
    path, content_type = sys.argv[1], sys.argv[2]
    token = login(os.environ["AIGENDA_EMAIL"], os.environ["AIGENDA_PASSWORD"])
    headers = {"Authorization": f"Bearer {token}"}

    space = main_space(headers)
    left = available_seconds(headers, space["id"])
    print(f"Пространство «{space['name']}», осталось {left // 60} мин")
    if left <= MIN_RESERVE_SECONDS:
        raise SystemExit("Минут на тарифе почти не осталось")

    audio_id = upload_file(headers, space["id"], path, content_type)
    print(f"Файл загружен, audio_id={audio_id}")

    start_processing(headers, audio_id)
    print("Обработка запущена, ждём результат…")

    result = wait_for_result(headers, space["id"], audio_id)
    out = os.path.splitext(path)[0] + ".protocol.md"
    with open(out, "w", encoding="utf-8") as f:
        f.write(result["result"]["protocol"])
    print(f"Протокол сохранён: {out}")


if __name__ == "__main__":
    main()
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.