# YaTrackerApi
> Асинхронный Python клиент для работы с Yandex Tracker API с модульной архитектурой
[](https://badge.fury.io/py/YaTrackerApi)
[](https://pypi.org/project/YaTrackerApi/)
[](https://opensource.org/licenses/MIT)
## 📋 Содержание
- [Особенности](#-особенности)
- [Установка](#-установка)
- [Быстрый старт](#-быстрый-старт)
- [Основные возможности](#-основные-возможности)
- [Работа с задачами](#работа-с-задачами)
- [Связи между задачами](#связи-между-задачами)
- [Управление статусами](#управление-статусами)
- [Чеклисты](#чеклисты)
- [Комментарии](#комментарии)
- [Работа с полями](#работа-с-полями)
- [Работа с сущностями](#работа-с-сущностями)
- [Работа с пользователями](#работа-с-пользователями)
- [Работа с очередями](#работа-с-очередями)
- [API Модули](#-api-модули)
- [Особенности и подводные камни](#-особенности-и-подводные-камни)
- [Требования](#-требования)
- [Лицензия](#-лицензия)
- [Контакты](#-контакты)
## ✨ Особенности
- **Асинхронность** - полная поддержка `async/await` на базе `aiohttp`
- **Модульная архитектура** - логическое разделение функциональности по модулям
- **Lazy Loading** - модули загружаются только при первом обращении
- **Типизация** - полная поддержка типов для всех методов
- **Гибкость** - множественные форматы для одного поля (строки, числа, объекты)
- **Валидация** - проверка данных на стороне клиента перед отправкой
- **Логирование** - система логов на русском языке с настраиваемым уровнем
- **Кастомные поля** - поддержка пользовательских полей любых типов
- **Безопасность** - настроенный SSL/TLS контекст для всех соединений
## 📦 Установка
```bash
pip install YaTrackerApi
```
Или через `uv`:
```bash
uv add YaTrackerApi
```
## 🚀 Быстрый старт
```python
import asyncio
from client import YandexTrackerClient
async def main():
async with YandexTrackerClient(
oauth_token="your_oauth_token",
org_id="your_org_id",
log_level="INFO"
) as client:
# Создание задачи
issue = await client.issues.create(
summary="Новая задача",
queue="TESTBOT",
description="Подробное описание задачи"
)
print(f"Создана задача: {issue['key']}")
# Получение задачи
issue = await client.issues.get('TESTBOT-1')
print(f"Задача: {issue['summary']}")
if __name__ == "__main__":
asyncio.run(main())
```
### Настройка переменных окружения
Создайте файл `.env` в корне проекта:
```env
TRACKER_API_KEY=your_oauth_token
TRACKER_ORG_ID=your_org_id
```
И загрузите их:
```python
from dotenv import load_dotenv
import os
load_dotenv()
async with YandexTrackerClient(
oauth_token=os.getenv("TRACKER_API_KEY"),
org_id=os.getenv("TRACKER_ORG_ID")
) as client:
# ваш код
```
## 💡 Основные возможности
### Работа с задачами
#### Создание задачи
```python
# Простое создание
issue = await client.issues.create(
summary="Новая задача",
queue="TESTBOT"
)
# С полными параметрами
issue = await client.issues.create(
summary="Задача с полными параметрами",
queue="TESTBOT",
description="Подробное описание",
assignee="username",
priority={"id": "2", "key": "minor"},
type="bug",
tags=["backend", "urgent"]
)
# С кастомными полями
issue = await client.issues.create(
summary="Задача с кастомными полями",
queue="TESTBOT",
localfields={
"numberOfEmployees": 10,
"contact_cl": "ООО Рога и Копыта",
"customPriority": "Очень высокий"
}
)
```
#### Получение задачи
```python
# Базовое получение
issue = await client.issues.get('TESTBOT-1')
# С расширенными полями
issue = await client.issues.get(
'TESTBOT-1',
expand=['transitions', 'attachments']
)
```
#### Обновление задачи
```python
# Простое обновление
await client.issues.update(
issue_id='TESTBOT-1',
summary="Обновленное название"
)
# Полное обновление
await client.issues.update(
issue_id='TESTBOT-1',
summary="Новое название",
description="Новое описание",
priority={"id": "1", "key": "critical"},
assignee="new_assignee",
tags={"add": ["new_tag"], "remove": ["old_tag"]}
)
```
#### Перенос задачи
```python
# Перенос в другую очередь
issue = await client.issues.move(
issue_id='TESTBOT-1',
queue='NEWQUEUE',
move_all_fields=True,
initial_status=True
)
```
#### Поиск задач
```python
# Поиск с фильтром
issues = await client.issues.search(
filter={"queue": "TESTBOT", "assignee": "me()"},
order="+status",
expand=["transitions", "attachments"],
per_page=50
)
# Поиск по текстовому запросу
issues = await client.issues.search(
query="Queue: TESTBOT AND Status: Open"
)
```
#### Подсчет задач
```python
count = await client.issues.count(
query="Queue: TESTBOT AND Status: New"
)
print(f"Найдено задач: {count}")
```
#### Получение приоритетов
```python
# Все приоритеты
priorities = await client.issues.priorities()
# Локализованные приоритеты
priorities = await client.issues.priorities(localized=True)
```
#### История изменений
```python
changelog = await client.issues.changelog('TESTBOT-1')
for change in changelog:
print(f"{change['updatedAt']}: {change['updatedBy']['display']}")
```
### Связи между задачами
```python
# Получение связей
links = await client.issues.links.get('TESTBOT-1')
# Создание связи
await client.issues.links.create(
issue_id='TESTBOT-1',
relationship='relates',
issue='TESTBOT-2'
)
# Удаление связи
await client.issues.links.delete(
issue_id='TESTBOT-1',
link_id='12345'
)
```
### Управление статусами
```python
# Получение доступных переходов
transitions = await client.issues.transitions.get('TESTBOT-1')
# Выполнение перехода
await client.issues.transitions.update(
issue_id='TESTBOT-1',
transition_id='resolve'
)
```
### Чеклисты
```python
# Получение чеклистов задачи
checklists = await client.issues.checklists.get('TESTBOT-1')
# Создание пункта чеклиста
await client.issues.checklists.create(
issue_id='TESTBOT-1',
text='Выполнить задачу',
checked=False
)
# Создание с дедлайном
await client.issues.checklists.create(
issue_id='TESTBOT-1',
text='Подготовить релиз',
checked=False,
deadline={
'date': '2025-12-31T23:59:59.000+0000',
'deadlineType': 'date'
}
)
# Обновление пункта чеклиста
await client.issues.checklists.item.update(
issue_id='TESTBOT-1',
checklist_item_id='item123',
text='Обновленный текст',
checked=True
)
# Удаление пункта
await client.issues.checklists.item.delete(
issue_id='TESTBOT-1',
checklist_item_id='item123'
)
```
### Комментарии
```python
# Получение комментариев
comments = await client.issues.comments.get('TESTBOT-1')
# Создание комментария
await client.issues.comments.create(
issue_id='TESTBOT-1',
text='Работа завершена',
summonees=['user1', 'user2']
)
# Обновление комментария
await client.issues.comments.update(
issue_id='TESTBOT-1',
comment_id='123',
text='Обновленный комментарий'
)
# Удаление комментария
await client.issues.comments.delete(
issue_id='TESTBOT-1',
comment_id='123'
)
```
### Работа с полями
```python
# Получение всех полей
fields = await client.issues.fields.get()
# Получение конкретного поля
field = await client.issues.fields.get(field_id='customField1')
# Создание кастомного поля
await client.fields.create(
name="Приоритет проекта",
key="projectPriority",
category="0000000000000001",
type="ru.yandex.startrek.core.fields.StringFieldType"
)
# Обновление поля
await client.fields.update(
field_id='customField1',
name="Новое название поля"
)
# Работа с локальными полями очереди
local_fields = await client.fields.local.get(queue_id='TESTBOT')
# Создание локального поля
await client.fields.local.create(
queue_id='TESTBOT',
name="Локальное поле",
key="localField1"
)
```
### Работа с сущностями
Сущности в Yandex Tracker - это проекты, портфели и цели.
```python
# Создание проекта
project = await client.entities.create(
entity_type="project",
summary="Новый проект",
lead="username"
)
# Получение сущности
entity = await client.entities.get(
entity_id="project123",
expand=["attachments"]
)
# Обновление сущности
await client.entities.update(
entity_id="project123",
summary="Обновленное название проекта"
)
# Поиск сущностей
entities = await client.entities.search(
entity_type="project",
filter={"lead": "username"},
order="+createdAt"
)
# Удаление сущности
await client.entities.delete(
entity_id="project123",
withBoard=True # Удалить вместе с доской
)
# Массовое обновление
await client.entities.bulk.update(
entity_ids=["project1", "project2"],
status="active"
)
# Работа со связями сущностей
await client.entities.links.create(
entity_id="project1",
relationship="relates",
entity="project2"
)
```
### Работа с пользователями
```python
# Получение списка всех пользователей
users = await client.users.get()
# Анализ пользователей
for user in users:
print(f"{user['display']} ({user['login']})")
# Фильтрация активных
active_users = [u for u in users if not u.get('dismissed', False)]
# Получение конкретного пользователя по логину
user = await client.users.get('username')
# Получение пользователя по ID
user = await client.users.get(123456)
```
### Работа с очередями
```python
# Получение списка всех очередей
queues = await client.queues.get()
# Получение конкретной очереди
queue = await client.queues.get('TREK', expand='all')
# Создание очереди
new_queue = await client.queues.create(
key='DESIGN',
name='Design Team',
lead='username',
default_type='task',
default_priority='normal',
issue_types_config=[
{
'issueType': 'task',
'workflow': 'oicn',
'resolutions': ['wontFix', 'fixed']
}
],
description='Очередь команды дизайна'
)
# Удаление очереди
await client.queues.delete('OLDQUEUE')
# Восстановление удаленной очереди
restored_queue = await client.queues.restore('OLDQUEUE')
# Работа с версиями очереди
versions = await client.queues.versions.get('TREK')
# Создание версии
version = await client.queues.versions.create(
queue='TREK',
name='v2.0.0',
description='Релиз версии 2.0',
start_date='2025-01-01',
due_date='2025-12-31'
)
# Получение полей очереди
fields = await client.queues.fields.get('TREK')
# Получение тегов очереди
tags = await client.queues.tags.get('TREK')
# Удаление тега
await client.queues.tags.delete('TREK', 'deprecated')
# Массовое управление доступом к очереди
await client.queues.bulk.update(
queue_id='TREK',
create={'add': ['user1', 'user2']},
write={'remove': ['user3']}
)
# Получение прав доступа пользователя к очереди
user_access = await client.queues.user.get('TREK', 'username')
# Получение прав доступа группы к очереди
group_access = await client.queues.group.get('TREK', 'group_id')
```
## 🔧 API Модули
Клиент состоит из следующих модулей:
### IssuesAPI (`client.issues`)
Работа с задачами:
- `get()` - получение задачи
- `create()` - создание задачи
- `update()` - обновление задачи
- `move()` - перенос задачи между очередями
- `search()` - поиск задач
- `count()` - подсчет задач
- `priorities()` - получение приоритетов
- `changelog()` - история изменений
**Подмодули:**
- `links` - связи между задачами
- `transitions` - переходы статусов
- `checklists` - чеклисты задач
- `comments` - комментарии
- `fields` - поля задач
### FieldsAPI (`client.fields`)
Управление полями:
- `get()` - получение списка полей
- `create()` - создание кастомного поля
- `update()` - обновление поля
- `create_category()` - создание категории полей
**Подмодуль:**
- `local` - локальные поля очередей
### EntitiesAPI (`client.entities`)
Работа с сущностями (проекты, портфели, цели):
- `get()` - получение сущности
- `create()` - создание сущности
- `update()` - обновление сущности
- `delete()` - удаление сущности
- `search()` - поиск сущностей
- `changelog()` - история изменений
**Подмодули:**
- `checklists` - чеклисты сущностей
- `bulk` - массовые операции
- `links` - связи между сущностями
### UsersAPI (`client.users`)
Работа с пользователями:
- `get()` - получение списка всех пользователей или конкретного пользователя
### QueuesAPI (`client.queues`)
Управление очередями:
- `get()` - получение очереди или списка очередей
- `create()` - создание новой очереди
- `delete()` - удаление очереди
- `restore()` - восстановление удаленной очереди
**Подмодули:**
- `versions` - версии очередей (create, get)
- `fields` - поля очередей (get)
- `tags` - теги очередей (get, delete)
- `bulk` - массовое управление доступом (update)
- `user` - права доступа пользователя (get)
- `group` - права доступа группы (get)
## ⚠️ Особенности и подводные камни
При работе с Yandex Tracker API есть ряд важных нюансов, которые необходимо учитывать для корректной работы.
### 1. Пагинация при поиске сущностей
**Проблема:** `entities.search()` возвращает пагинированный ответ в виде словаря, а не списка.
**Структура ответа:**
```python
{
"hits": 125, # Общее количество найденных сущностей
"pages": 3, # Количество страниц
"values": [...] # Массив сущностей на текущей странице (по умолчанию 50)
}
```
**Решение:**
```python
# Получаем первую страницу
projects_raw = await client.entities.search(
entity_type="project",
fields="summary,id"
)
# Проверяем тип ответа и загружаем все страницы
if isinstance(projects_raw, dict):
pages = projects_raw.get("pages", 1)
if isinstance(pages, int) and pages > 1:
per_page = pages * 50
projects_raw = await client.entities.search(
entity_type="project",
fields="summary,id",
per_page=per_page
)
projects = projects_raw.get("values", [])
else:
projects = projects_raw
```
### 2. Параметр fields при поиске
**Проблема:** Без явного указания полей в параметре `fields`, API может не возвращать важные данные.
**Неправильно:**
```python
projects = await client.entities.search(entity_type="project")
# summary может отсутствовать в ответе
```
**Правильно:**
```python
projects = await client.entities.search(
entity_type="project",
fields="summary,id,description"
)
# Извлекаем данные
for proj in projects["values"]:
summary = proj.get("fields", {}).get("summary", "")
if not summary:
summary = proj.get("summary", "")
```
### 3. Формат поля project при создании задачи
**Проблема:** API ожидает `shortId` проекта (число), а не полный `id` (строка).
**Неправильно:**
```python
project = {
"id": "68e5764ffa5085239cef5e94" # ❌ Строковый ID
}
```
**Правильно:**
```python
# После создания проекта
new_project = await client.entities.create(
entity_type="project",
summary="Новый проект"
)
# Используйте shortId
project_short_id = new_project.get("shortId")
# Создаем задачу
issue = await client.issues.create(
summary="Задача проекта",
queue="TESTBOT",
project={"primary": project_short_id} # ✅ shortId как число
)
```
### 4. Формат полей type и priority
**Проблема:** API выдает ошибку 400, если передать полные объекты вместо ключей.
**Неправильно:**
```python
issue = await client.issues.get("TESTBOT-1")
new_issue = await client.issues.create(
summary="Копия",
queue="TESTBOT",
type=issue["type"], # ❌ Полный объект
priority=issue["priority"] # ❌ Полный объект
)
```
**Правильно:**
```python
def extract_field_value(field_data):
"""Извлечь значение поля для API запроса"""
if isinstance(field_data, dict):
return field_data.get("key") or field_data.get("id")
return field_data
new_issue = await client.issues.create(
summary="Копия",
queue="TESTBOT",
type=extract_field_value(issue.get("type")), # ✅ "milestone"
priority=extract_field_value(issue.get("priority")) # ✅ "normal"
)
```
### 5. Получение сущностей с полным набором полей
**Проблема:** Без параметра `fields` API возвращает только базовые поля.
**Неправильно:**
```python
project = await client.entities.get(
entity_id=project_id,
entity_type="project"
)
# НЕТ: description, lead, teamUsers, parentEntity
```
**Правильно:**
```python
project = await client.entities.get(
entity_id=project_id,
entity_type="project",
fields="summary,description,lead,teamUsers,teamAccess,parentEntity,clients,followers,start,end,tags"
)
# ✅ Все поля присутствуют
```
### 6. Ограничения на изменение системных полей
**Нельзя изменить:**
- ❌ `createdBy` - автор (устанавливается автоматически)
- ❌ `createdAt` - дата создания
- ❌ `author` - автор (алиас createdBy)
**Можно изменить:**
- ✅ `lead` - руководитель проекта
- ✅ `assignee` - исполнитель задачи
- ✅ `teamUsers` - участники
- ✅ `followers` - наблюдатели
```python
# Правильное обновление
await client.entities.update(
entity_id=project_id,
lead="user_login",
teamUsers=["user1", "user2"]
)
```
### Дополнительные замечания
**Автоконвертация параметров:** Библиотека автоматически конвертирует snake_case в camelCase:
```python
per_page=50 # → perPage=50 в API
```
**Обработка ошибок:** Используйте debug логирование для диагностики:
```python
import logging
logging.getLogger("YaTrackerApi").setLevel(logging.DEBUG)
```
Полная документация: [context/gotchas.md](context/gotchas.md)
## 📋 Требования
- Python 3.9+
- aiohttp >= 3.12.15
- python-dotenv >= 1.1.1
## 📄 Лицензия
Проект распространяется под лицензией [MIT](LICENSE).
## 👤 Контакты
**Даниил**
- GitHub: [@imdeniil](https://github.com/imdeniil)
- Email: keemor821@gmail.com
## 🔗 Ссылки
- [PyPI](https://pypi.org/project/YaTrackerApi/)
- [GitHub Repository](https://github.com/imdeniil/YaTrackerApi)
- [Issues](https://github.com/imdeniil/YaTrackerApi/issues)
- [Официальная документация Yandex Tracker API](https://cloud.yandex.ru/docs/tracker/about-api)
---
**Создано с ❤️ для упрощения работы с Yandex Tracker API**
Raw data
{
"_id": null,
"home_page": null,
"name": "YaTrackerApi",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": "\u0414\u0430\u043d\u0438\u0438\u043b <keemor821@gmail.com>",
"keywords": "aiohttp, api, async, project-management, task-tracker, tracker, yandex",
"author": null,
"author_email": "\u0414\u0430\u043d\u0438\u0438\u043b <keemor821@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/5c/3d/b6e3ab9a327e29b89733e915bc32f9d48f40c0112f8697838208b69ce4a4/yatrackerapi-2.1.2.tar.gz",
"platform": null,
"description": "# YaTrackerApi\n\n> \u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 Python \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Yandex Tracker API \u0441 \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043e\u0439\n\n[](https://badge.fury.io/py/YaTrackerApi)\n[](https://pypi.org/project/YaTrackerApi/)\n[](https://opensource.org/licenses/MIT)\n\n## \ud83d\udccb \u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435\n\n- [\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438](#-\u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438)\n- [\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430](#-\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430)\n- [\u0411\u044b\u0441\u0442\u0440\u044b\u0439 \u0441\u0442\u0430\u0440\u0442](#-\u0431\u044b\u0441\u0442\u0440\u044b\u0439-\u0441\u0442\u0430\u0440\u0442)\n- [\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438](#-\u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435-\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438)\n - [\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438](#\u0440\u0430\u0431\u043e\u0442\u0430-\u0441-\u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438)\n - [\u0421\u0432\u044f\u0437\u0438 \u043c\u0435\u0436\u0434\u0443 \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438](#\u0441\u0432\u044f\u0437\u0438-\u043c\u0435\u0436\u0434\u0443-\u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438)\n - [\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u0443\u0441\u0430\u043c\u0438](#\u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435-\u0441\u0442\u0430\u0442\u0443\u0441\u0430\u043c\u0438)\n - [\u0427\u0435\u043a\u043b\u0438\u0441\u0442\u044b](#\u0447\u0435\u043a\u043b\u0438\u0441\u0442\u044b)\n - [\u041a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438](#\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438)\n - [\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043f\u043e\u043b\u044f\u043c\u0438](#\u0440\u0430\u0431\u043e\u0442\u0430-\u0441-\u043f\u043e\u043b\u044f\u043c\u0438)\n - [\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044f\u043c\u0438](#\u0440\u0430\u0431\u043e\u0442\u0430-\u0441-\u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044f\u043c\u0438)\n - [\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438](#\u0440\u0430\u0431\u043e\u0442\u0430-\u0441-\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438)\n - [\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043e\u0447\u0435\u0440\u0435\u0434\u044f\u043c\u0438](#\u0440\u0430\u0431\u043e\u0442\u0430-\u0441-\u043e\u0447\u0435\u0440\u0435\u0434\u044f\u043c\u0438)\n- [API \u041c\u043e\u0434\u0443\u043b\u0438](#-api-\u043c\u043e\u0434\u0443\u043b\u0438)\n- [\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0438 \u043f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u0435 \u043a\u0430\u043c\u043d\u0438](#-\u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438-\u0438-\u043f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u0435-\u043a\u0430\u043c\u043d\u0438)\n- [\u0422\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f](#-\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f)\n- [\u041b\u0438\u0446\u0435\u043d\u0437\u0438\u044f](#-\u043b\u0438\u0446\u0435\u043d\u0437\u0438\u044f)\n- [\u041a\u043e\u043d\u0442\u0430\u043a\u0442\u044b](#-\u043a\u043e\u043d\u0442\u0430\u043a\u0442\u044b)\n\n## \u2728 \u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438\n\n- **\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0441\u0442\u044c** - \u043f\u043e\u043b\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 `async/await` \u043d\u0430 \u0431\u0430\u0437\u0435 `aiohttp`\n- **\u041c\u043e\u0434\u0443\u043b\u044c\u043d\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430** - \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u043f\u043e \u043c\u043e\u0434\u0443\u043b\u044f\u043c\n- **Lazy Loading** - \u043c\u043e\u0434\u0443\u043b\u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438 \u043f\u0435\u0440\u0432\u043e\u043c \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0438\n- **\u0422\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u044f** - \u043f\u043e\u043b\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0442\u0438\u043f\u043e\u0432 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432\n- **\u0413\u0438\u0431\u043a\u043e\u0441\u0442\u044c** - \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u044b \u0434\u043b\u044f \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044f (\u0441\u0442\u0440\u043e\u043a\u0438, \u0447\u0438\u0441\u043b\u0430, \u043e\u0431\u044a\u0435\u043a\u0442\u044b)\n- **\u0412\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f** - \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043f\u0435\u0440\u0435\u0434 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439\n- **\u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435** - \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043b\u043e\u0433\u043e\u0432 \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u043e\u043c \u044f\u0437\u044b\u043a\u0435 \u0441 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u043c \u0443\u0440\u043e\u0432\u043d\u0435\u043c\n- **\u041a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f** - \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u043f\u043e\u043b\u0435\u0439 \u043b\u044e\u0431\u044b\u0445 \u0442\u0438\u043f\u043e\u0432\n- **\u0411\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u044c** - \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 SSL/TLS \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439\n\n## \ud83d\udce6 \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430\n\n```bash\npip install YaTrackerApi\n```\n\n\u0418\u043b\u0438 \u0447\u0435\u0440\u0435\u0437 `uv`:\n\n```bash\nuv add YaTrackerApi\n```\n\n## \ud83d\ude80 \u0411\u044b\u0441\u0442\u0440\u044b\u0439 \u0441\u0442\u0430\u0440\u0442\n\n```python\nimport asyncio\nfrom client import YandexTrackerClient\n\nasync def main():\n async with YandexTrackerClient(\n oauth_token=\"your_oauth_token\",\n org_id=\"your_org_id\",\n log_level=\"INFO\"\n ) as client:\n # \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438\n issue = await client.issues.create(\n summary=\"\u041d\u043e\u0432\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430\",\n queue=\"TESTBOT\",\n description=\"\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438\"\n )\n print(f\"\u0421\u043e\u0437\u0434\u0430\u043d\u0430 \u0437\u0430\u0434\u0430\u0447\u0430: {issue['key']}\")\n\n # \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438\n issue = await client.issues.get('TESTBOT-1')\n print(f\"\u0417\u0430\u0434\u0430\u0447\u0430: {issue['summary']}\")\n\nif __name__ == \"__main__\":\n asyncio.run(main())\n```\n\n### \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f\n\n\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0444\u0430\u0439\u043b `.env` \u0432 \u043a\u043e\u0440\u043d\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:\n\n```env\nTRACKER_API_KEY=your_oauth_token\nTRACKER_ORG_ID=your_org_id\n```\n\n\u0418 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 \u0438\u0445:\n\n```python\nfrom dotenv import load_dotenv\nimport os\n\nload_dotenv()\n\nasync with YandexTrackerClient(\n oauth_token=os.getenv(\"TRACKER_API_KEY\"),\n org_id=os.getenv(\"TRACKER_ORG_ID\")\n) as client:\n # \u0432\u0430\u0448 \u043a\u043e\u0434\n```\n\n## \ud83d\udca1 \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438\n\n### \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438\n\n#### \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438\n\n```python\n# \u041f\u0440\u043e\u0441\u0442\u043e\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\nissue = await client.issues.create(\n summary=\"\u041d\u043e\u0432\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430\",\n queue=\"TESTBOT\"\n)\n\n# \u0421 \u043f\u043e\u043b\u043d\u044b\u043c\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438\nissue = await client.issues.create(\n summary=\"\u0417\u0430\u0434\u0430\u0447\u0430 \u0441 \u043f\u043e\u043b\u043d\u044b\u043c\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438\",\n queue=\"TESTBOT\",\n description=\"\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\",\n assignee=\"username\",\n priority={\"id\": \"2\", \"key\": \"minor\"},\n type=\"bug\",\n tags=[\"backend\", \"urgent\"]\n)\n\n# \u0421 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u043c\u0438 \u043f\u043e\u043b\u044f\u043c\u0438\nissue = await client.issues.create(\n summary=\"\u0417\u0430\u0434\u0430\u0447\u0430 \u0441 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u043c\u0438 \u043f\u043e\u043b\u044f\u043c\u0438\",\n queue=\"TESTBOT\",\n localfields={\n \"numberOfEmployees\": 10,\n \"contact_cl\": \"\u041e\u041e\u041e \u0420\u043e\u0433\u0430 \u0438 \u041a\u043e\u043f\u044b\u0442\u0430\",\n \"customPriority\": \"\u041e\u0447\u0435\u043d\u044c \u0432\u044b\u0441\u043e\u043a\u0438\u0439\"\n }\n)\n```\n\n#### \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438\n\n```python\n# \u0411\u0430\u0437\u043e\u0432\u043e\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435\nissue = await client.issues.get('TESTBOT-1')\n\n# \u0421 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u043c\u0438 \u043f\u043e\u043b\u044f\u043c\u0438\nissue = await client.issues.get(\n 'TESTBOT-1',\n expand=['transitions', 'attachments']\n)\n```\n\n#### \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438\n\n```python\n# \u041f\u0440\u043e\u0441\u0442\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\nawait client.issues.update(\n issue_id='TESTBOT-1',\n summary=\"\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u043e\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\"\n)\n\n# \u041f\u043e\u043b\u043d\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\nawait client.issues.update(\n issue_id='TESTBOT-1',\n summary=\"\u041d\u043e\u0432\u043e\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\",\n description=\"\u041d\u043e\u0432\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\",\n priority={\"id\": \"1\", \"key\": \"critical\"},\n assignee=\"new_assignee\",\n tags={\"add\": [\"new_tag\"], \"remove\": [\"old_tag\"]}\n)\n```\n\n#### \u041f\u0435\u0440\u0435\u043d\u043e\u0441 \u0437\u0430\u0434\u0430\u0447\u0438\n\n```python\n# \u041f\u0435\u0440\u0435\u043d\u043e\u0441 \u0432 \u0434\u0440\u0443\u0433\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c\nissue = await client.issues.move(\n issue_id='TESTBOT-1',\n queue='NEWQUEUE',\n move_all_fields=True,\n initial_status=True\n)\n```\n\n#### \u041f\u043e\u0438\u0441\u043a \u0437\u0430\u0434\u0430\u0447\n\n```python\n# \u041f\u043e\u0438\u0441\u043a \u0441 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u043c\nissues = await client.issues.search(\n filter={\"queue\": \"TESTBOT\", \"assignee\": \"me()\"},\n order=\"+status\",\n expand=[\"transitions\", \"attachments\"],\n per_page=50\n)\n\n# \u041f\u043e\u0438\u0441\u043a \u043f\u043e \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u043c\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0443\nissues = await client.issues.search(\n query=\"Queue: TESTBOT AND Status: Open\"\n)\n```\n\n#### \u041f\u043e\u0434\u0441\u0447\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\n\n```python\ncount = await client.issues.count(\n query=\"Queue: TESTBOT AND Status: New\"\n)\nprint(f\"\u041d\u0430\u0439\u0434\u0435\u043d\u043e \u0437\u0430\u0434\u0430\u0447: {count}\")\n```\n\n#### \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u043e\u0432\n\n```python\n# \u0412\u0441\u0435 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u044b\npriorities = await client.issues.priorities()\n\n# \u041b\u043e\u043a\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u044b\npriorities = await client.issues.priorities(localized=True)\n```\n\n#### \u0418\u0441\u0442\u043e\u0440\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439\n\n```python\nchangelog = await client.issues.changelog('TESTBOT-1')\nfor change in changelog:\n print(f\"{change['updatedAt']}: {change['updatedBy']['display']}\")\n```\n\n### \u0421\u0432\u044f\u0437\u0438 \u043c\u0435\u0436\u0434\u0443 \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438\n\n```python\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u0432\u044f\u0437\u0435\u0439\nlinks = await client.issues.links.get('TESTBOT-1')\n\n# \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u0432\u044f\u0437\u0438\nawait client.issues.links.create(\n issue_id='TESTBOT-1',\n relationship='relates',\n issue='TESTBOT-2'\n)\n\n# \u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0441\u0432\u044f\u0437\u0438\nawait client.issues.links.delete(\n issue_id='TESTBOT-1',\n link_id='12345'\n)\n```\n\n### \u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u0443\u0441\u0430\u043c\u0438\n\n```python\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u043e\u0432\ntransitions = await client.issues.transitions.get('TESTBOT-1')\n\n# \u0412\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430\nawait client.issues.transitions.update(\n issue_id='TESTBOT-1',\n transition_id='resolve'\n)\n```\n\n### \u0427\u0435\u043a\u043b\u0438\u0441\u0442\u044b\n\n```python\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0447\u0435\u043a\u043b\u0438\u0441\u0442\u043e\u0432 \u0437\u0430\u0434\u0430\u0447\u0438\nchecklists = await client.issues.checklists.get('TESTBOT-1')\n\n# \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0443\u043d\u043a\u0442\u0430 \u0447\u0435\u043a\u043b\u0438\u0441\u0442\u0430\nawait client.issues.checklists.create(\n issue_id='TESTBOT-1',\n text='\u0412\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0443',\n checked=False\n)\n\n# \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441 \u0434\u0435\u0434\u043b\u0430\u0439\u043d\u043e\u043c\nawait client.issues.checklists.create(\n issue_id='TESTBOT-1',\n text='\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u044c \u0440\u0435\u043b\u0438\u0437',\n checked=False,\n deadline={\n 'date': '2025-12-31T23:59:59.000+0000',\n 'deadlineType': 'date'\n }\n)\n\n# \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0443\u043d\u043a\u0442\u0430 \u0447\u0435\u043a\u043b\u0438\u0441\u0442\u0430\nawait client.issues.checklists.item.update(\n issue_id='TESTBOT-1',\n checklist_item_id='item123',\n text='\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0442\u0435\u043a\u0441\u0442',\n checked=True\n)\n\n# \u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043f\u0443\u043d\u043a\u0442\u0430\nawait client.issues.checklists.item.delete(\n issue_id='TESTBOT-1',\n checklist_item_id='item123'\n)\n```\n\n### \u041a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438\n\n```python\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u0432\ncomments = await client.issues.comments.get('TESTBOT-1')\n\n# \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\nawait client.issues.comments.create(\n issue_id='TESTBOT-1',\n text='\u0420\u0430\u0431\u043e\u0442\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430',\n summonees=['user1', 'user2']\n)\n\n# \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\nawait client.issues.comments.update(\n issue_id='TESTBOT-1',\n comment_id='123',\n text='\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0439'\n)\n\n# \u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\nawait client.issues.comments.delete(\n issue_id='TESTBOT-1',\n comment_id='123'\n)\n```\n\n### \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043f\u043e\u043b\u044f\u043c\u0438\n\n```python\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u043f\u043e\u043b\u0435\u0439\nfields = await client.issues.fields.get()\n\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044f\nfield = await client.issues.fields.get(field_id='customField1')\n\n# \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044f\nawait client.fields.create(\n name=\"\u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\",\n key=\"projectPriority\",\n category=\"0000000000000001\",\n type=\"ru.yandex.startrek.core.fields.StringFieldType\"\n)\n\n# \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044f\nawait client.fields.update(\n field_id='customField1',\n name=\"\u041d\u043e\u0432\u043e\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044f\"\n)\n\n# \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u043f\u043e\u043b\u044f\u043c\u0438 \u043e\u0447\u0435\u0440\u0435\u0434\u0438\nlocal_fields = await client.fields.local.get(queue_id='TESTBOT')\n\n# \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044f\nawait client.fields.local.create(\n queue_id='TESTBOT',\n name=\"\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435\",\n key=\"localField1\"\n)\n```\n\n### \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044f\u043c\u0438\n\n\u0421\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0432 Yandex Tracker - \u044d\u0442\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u044b, \u043f\u043e\u0440\u0442\u0444\u0435\u043b\u0438 \u0438 \u0446\u0435\u043b\u0438.\n\n```python\n# \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\nproject = await client.entities.create(\n entity_type=\"project\",\n summary=\"\u041d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\",\n lead=\"username\"\n)\n\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438\nentity = await client.entities.get(\n entity_id=\"project123\",\n expand=[\"attachments\"]\n)\n\n# \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438\nawait client.entities.update(\n entity_id=\"project123\",\n summary=\"\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u043e\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\"\n)\n\n# \u041f\u043e\u0438\u0441\u043a \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439\nentities = await client.entities.search(\n entity_type=\"project\",\n filter={\"lead\": \"username\"},\n order=\"+createdAt\"\n)\n\n# \u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438\nawait client.entities.delete(\n entity_id=\"project123\",\n withBoard=True # \u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u0434\u043e\u0441\u043a\u043e\u0439\n)\n\n# \u041c\u0430\u0441\u0441\u043e\u0432\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\nawait client.entities.bulk.update(\n entity_ids=[\"project1\", \"project2\"],\n status=\"active\"\n)\n\n# \u0420\u0430\u0431\u043e\u0442\u0430 \u0441\u043e \u0441\u0432\u044f\u0437\u044f\u043c\u0438 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439\nawait client.entities.links.create(\n entity_id=\"project1\",\n relationship=\"relates\",\n entity=\"project2\"\n)\n```\n\n### \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438\n\n```python\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u0430 \u0432\u0441\u0435\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439\nusers = await client.users.get()\n\n# \u0410\u043d\u0430\u043b\u0438\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439\nfor user in users:\n print(f\"{user['display']} ({user['login']})\")\n\n# \u0424\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044f \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445\nactive_users = [u for u in users if not u.get('dismissed', False)]\n\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e \u043b\u043e\u0433\u0438\u043d\u0443\nuser = await client.users.get('username')\n\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e ID\nuser = await client.users.get(123456)\n```\n\n### \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043e\u0447\u0435\u0440\u0435\u0434\u044f\u043c\u0438\n\n```python\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u0430 \u0432\u0441\u0435\u0445 \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439\nqueues = await client.queues.get()\n\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0439 \u043e\u0447\u0435\u0440\u0435\u0434\u0438\nqueue = await client.queues.get('TREK', expand='all')\n\n# \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043e\u0447\u0435\u0440\u0435\u0434\u0438\nnew_queue = await client.queues.create(\n key='DESIGN',\n name='Design Team',\n lead='username',\n default_type='task',\n default_priority='normal',\n issue_types_config=[\n {\n 'issueType': 'task',\n 'workflow': 'oicn',\n 'resolutions': ['wontFix', 'fixed']\n }\n ],\n description='\u041e\u0447\u0435\u0440\u0435\u0434\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0434\u0438\u0437\u0430\u0439\u043d\u0430'\n)\n\n# \u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043e\u0447\u0435\u0440\u0435\u0434\u0438\nawait client.queues.delete('OLDQUEUE')\n\n# \u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0439 \u043e\u0447\u0435\u0440\u0435\u0434\u0438\nrestored_queue = await client.queues.restore('OLDQUEUE')\n\n# \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u0432\u0435\u0440\u0441\u0438\u044f\u043c\u0438 \u043e\u0447\u0435\u0440\u0435\u0434\u0438\nversions = await client.queues.versions.get('TREK')\n\n# \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0432\u0435\u0440\u0441\u0438\u0438\nversion = await client.queues.versions.create(\n queue='TREK',\n name='v2.0.0',\n description='\u0420\u0435\u043b\u0438\u0437 \u0432\u0435\u0440\u0441\u0438\u0438 2.0',\n start_date='2025-01-01',\n due_date='2025-12-31'\n)\n\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u0435\u0439 \u043e\u0447\u0435\u0440\u0435\u0434\u0438\nfields = await client.queues.fields.get('TREK')\n\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0442\u0435\u0433\u043e\u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u0438\ntags = await client.queues.tags.get('TREK')\n\n# \u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0442\u0435\u0433\u0430\nawait client.queues.tags.delete('TREK', 'deprecated')\n\n# \u041c\u0430\u0441\u0441\u043e\u0432\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u043e\u043c \u043a \u043e\u0447\u0435\u0440\u0435\u0434\u0438\nawait client.queues.bulk.update(\n queue_id='TREK',\n create={'add': ['user1', 'user2']},\n write={'remove': ['user3']}\n)\n\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u0430\u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043a \u043e\u0447\u0435\u0440\u0435\u0434\u0438\nuser_access = await client.queues.user.get('TREK', 'username')\n\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u0430\u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0433\u0440\u0443\u043f\u043f\u044b \u043a \u043e\u0447\u0435\u0440\u0435\u0434\u0438\ngroup_access = await client.queues.group.get('TREK', 'group_id')\n```\n\n## \ud83d\udd27 API \u041c\u043e\u0434\u0443\u043b\u0438\n\n\u041a\u043b\u0438\u0435\u043d\u0442 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439:\n\n### IssuesAPI (`client.issues`)\n\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438:\n- `get()` - \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438\n- `create()` - \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438\n- `update()` - \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438\n- `move()` - \u043f\u0435\u0440\u0435\u043d\u043e\u0441 \u0437\u0430\u0434\u0430\u0447\u0438 \u043c\u0435\u0436\u0434\u0443 \u043e\u0447\u0435\u0440\u0435\u0434\u044f\u043c\u0438\n- `search()` - \u043f\u043e\u0438\u0441\u043a \u0437\u0430\u0434\u0430\u0447\n- `count()` - \u043f\u043e\u0434\u0441\u0447\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\n- `priorities()` - \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u043e\u0432\n- `changelog()` - \u0438\u0441\u0442\u043e\u0440\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439\n\n**\u041f\u043e\u0434\u043c\u043e\u0434\u0443\u043b\u0438:**\n- `links` - \u0441\u0432\u044f\u0437\u0438 \u043c\u0435\u0436\u0434\u0443 \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438\n- `transitions` - \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u044b \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u0432\n- `checklists` - \u0447\u0435\u043a\u043b\u0438\u0441\u0442\u044b \u0437\u0430\u0434\u0430\u0447\n- `comments` - \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438\n- `fields` - \u043f\u043e\u043b\u044f \u0437\u0430\u0434\u0430\u0447\n\n### FieldsAPI (`client.fields`)\n\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044f\u043c\u0438:\n- `get()` - \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u0430 \u043f\u043e\u043b\u0435\u0439\n- `create()` - \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044f\n- `update()` - \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044f\n- `create_category()` - \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 \u043f\u043e\u043b\u0435\u0439\n\n**\u041f\u043e\u0434\u043c\u043e\u0434\u0443\u043b\u044c:**\n- `local` - \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439\n\n### EntitiesAPI (`client.entities`)\n\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044f\u043c\u0438 (\u043f\u0440\u043e\u0435\u043a\u0442\u044b, \u043f\u043e\u0440\u0442\u0444\u0435\u043b\u0438, \u0446\u0435\u043b\u0438):\n- `get()` - \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438\n- `create()` - \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438\n- `update()` - \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438\n- `delete()` - \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438\n- `search()` - \u043f\u043e\u0438\u0441\u043a \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439\n- `changelog()` - \u0438\u0441\u0442\u043e\u0440\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439\n\n**\u041f\u043e\u0434\u043c\u043e\u0434\u0443\u043b\u0438:**\n- `checklists` - \u0447\u0435\u043a\u043b\u0438\u0441\u0442\u044b \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439\n- `bulk` - \u043c\u0430\u0441\u0441\u043e\u0432\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438\n- `links` - \u0441\u0432\u044f\u0437\u0438 \u043c\u0435\u0436\u0434\u0443 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044f\u043c\u0438\n\n### UsersAPI (`client.users`)\n\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438:\n- `get()` - \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u0430 \u0432\u0441\u0435\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438\u043b\u0438 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\n\n### QueuesAPI (`client.queues`)\n\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043e\u0447\u0435\u0440\u0435\u0434\u044f\u043c\u0438:\n- `get()` - \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u0438\u043b\u0438 \u0441\u043f\u0438\u0441\u043a\u0430 \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439\n- `create()` - \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0439 \u043e\u0447\u0435\u0440\u0435\u0434\u0438\n- `delete()` - \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043e\u0447\u0435\u0440\u0435\u0434\u0438\n- `restore()` - \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0439 \u043e\u0447\u0435\u0440\u0435\u0434\u0438\n\n**\u041f\u043e\u0434\u043c\u043e\u0434\u0443\u043b\u0438:**\n- `versions` - \u0432\u0435\u0440\u0441\u0438\u0438 \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439 (create, get)\n- `fields` - \u043f\u043e\u043b\u044f \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439 (get)\n- `tags` - \u0442\u0435\u0433\u0438 \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439 (get, delete)\n- `bulk` - \u043c\u0430\u0441\u0441\u043e\u0432\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u043e\u043c (update)\n- `user` - \u043f\u0440\u0430\u0432\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f (get)\n- `group` - \u043f\u0440\u0430\u0432\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0433\u0440\u0443\u043f\u043f\u044b (get)\n\n## \u26a0\ufe0f \u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0438 \u043f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u0435 \u043a\u0430\u043c\u043d\u0438\n\n\u041f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 Yandex Tracker API \u0435\u0441\u0442\u044c \u0440\u044f\u0434 \u0432\u0430\u0436\u043d\u044b\u0445 \u043d\u044e\u0430\u043d\u0441\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b.\n\n### 1. \u041f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u044f \u043f\u0440\u0438 \u043f\u043e\u0438\u0441\u043a\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439\n\n**\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430:** `entities.search()` \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u0430\u0433\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 \u0432 \u0432\u0438\u0434\u0435 \u0441\u043b\u043e\u0432\u0430\u0440\u044f, \u0430 \u043d\u0435 \u0441\u043f\u0438\u0441\u043a\u0430.\n\n**\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043e\u0442\u0432\u0435\u0442\u0430:**\n```python\n{\n \"hits\": 125, # \u041e\u0431\u0449\u0435\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0445 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439\n \"pages\": 3, # \u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\n \"values\": [...] # \u041c\u0430\u0441\u0441\u0438\u0432 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439 \u043d\u0430 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e 50)\n}\n```\n\n**\u0420\u0435\u0448\u0435\u043d\u0438\u0435:**\n```python\n# \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u0435\u0440\u0432\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443\nprojects_raw = await client.entities.search(\n entity_type=\"project\",\n fields=\"summary,id\"\n)\n\n# \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0442\u0438\u043f \u043e\u0442\u0432\u0435\u0442\u0430 \u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b\nif isinstance(projects_raw, dict):\n pages = projects_raw.get(\"pages\", 1)\n\n if isinstance(pages, int) and pages > 1:\n per_page = pages * 50\n projects_raw = await client.entities.search(\n entity_type=\"project\",\n fields=\"summary,id\",\n per_page=per_page\n )\n\n projects = projects_raw.get(\"values\", [])\nelse:\n projects = projects_raw\n```\n\n### 2. \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 fields \u043f\u0440\u0438 \u043f\u043e\u0438\u0441\u043a\u0435\n\n**\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430:** \u0411\u0435\u0437 \u044f\u0432\u043d\u043e\u0433\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u0435\u0439 \u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435 `fields`, API \u043c\u043e\u0436\u0435\u0442 \u043d\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0432\u0430\u0436\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435.\n\n**\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e:**\n```python\nprojects = await client.entities.search(entity_type=\"project\")\n# summary \u043c\u043e\u0436\u0435\u0442 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0432 \u043e\u0442\u0432\u0435\u0442\u0435\n```\n\n**\u041f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e:**\n```python\nprojects = await client.entities.search(\n entity_type=\"project\",\n fields=\"summary,id,description\"\n)\n\n# \u0418\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435\nfor proj in projects[\"values\"]:\n summary = proj.get(\"fields\", {}).get(\"summary\", \"\")\n if not summary:\n summary = proj.get(\"summary\", \"\")\n```\n\n### 3. \u0424\u043e\u0440\u043c\u0430\u0442 \u043f\u043e\u043b\u044f project \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0437\u0430\u0434\u0430\u0447\u0438\n\n**\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430:** API \u043e\u0436\u0438\u0434\u0430\u0435\u0442 `shortId` \u043f\u0440\u043e\u0435\u043a\u0442\u0430 (\u0447\u0438\u0441\u043b\u043e), \u0430 \u043d\u0435 \u043f\u043e\u043b\u043d\u044b\u0439 `id` (\u0441\u0442\u0440\u043e\u043a\u0430).\n\n**\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e:**\n```python\nproject = {\n \"id\": \"68e5764ffa5085239cef5e94\" # \u274c \u0421\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u0439 ID\n}\n```\n\n**\u041f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e:**\n```python\n# \u041f\u043e\u0441\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430\nnew_project = await client.entities.create(\n entity_type=\"project\",\n summary=\"\u041d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\"\n)\n\n# \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 shortId\nproject_short_id = new_project.get(\"shortId\")\n\n# \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0437\u0430\u0434\u0430\u0447\u0443\nissue = await client.issues.create(\n summary=\"\u0417\u0430\u0434\u0430\u0447\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\",\n queue=\"TESTBOT\",\n project={\"primary\": project_short_id} # \u2705 shortId \u043a\u0430\u043a \u0447\u0438\u0441\u043b\u043e\n)\n```\n\n### 4. \u0424\u043e\u0440\u043c\u0430\u0442 \u043f\u043e\u043b\u0435\u0439 type \u0438 priority\n\n**\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430:** API \u0432\u044b\u0434\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 400, \u0435\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0432\u043c\u0435\u0441\u0442\u043e \u043a\u043b\u044e\u0447\u0435\u0439.\n\n**\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e:**\n```python\nissue = await client.issues.get(\"TESTBOT-1\")\n\nnew_issue = await client.issues.create(\n summary=\"\u041a\u043e\u043f\u0438\u044f\",\n queue=\"TESTBOT\",\n type=issue[\"type\"], # \u274c \u041f\u043e\u043b\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442\n priority=issue[\"priority\"] # \u274c \u041f\u043e\u043b\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442\n)\n```\n\n**\u041f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e:**\n```python\ndef extract_field_value(field_data):\n \"\"\"\u0418\u0437\u0432\u043b\u0435\u0447\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044f \u0434\u043b\u044f API \u0437\u0430\u043f\u0440\u043e\u0441\u0430\"\"\"\n if isinstance(field_data, dict):\n return field_data.get(\"key\") or field_data.get(\"id\")\n return field_data\n\nnew_issue = await client.issues.create(\n summary=\"\u041a\u043e\u043f\u0438\u044f\",\n queue=\"TESTBOT\",\n type=extract_field_value(issue.get(\"type\")), # \u2705 \"milestone\"\n priority=extract_field_value(issue.get(\"priority\")) # \u2705 \"normal\"\n)\n```\n\n### 5. \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439 \u0441 \u043f\u043e\u043b\u043d\u044b\u043c \u043d\u0430\u0431\u043e\u0440\u043e\u043c \u043f\u043e\u043b\u0435\u0439\n\n**\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430:** \u0411\u0435\u0437 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 `fields` API \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043f\u043e\u043b\u044f.\n\n**\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e:**\n```python\nproject = await client.entities.get(\n entity_id=project_id,\n entity_type=\"project\"\n)\n# \u041d\u0415\u0422: description, lead, teamUsers, parentEntity\n```\n\n**\u041f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e:**\n```python\nproject = await client.entities.get(\n entity_id=project_id,\n entity_type=\"project\",\n fields=\"summary,description,lead,teamUsers,teamAccess,parentEntity,clients,followers,start,end,tags\"\n)\n# \u2705 \u0412\u0441\u0435 \u043f\u043e\u043b\u044f \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442\n```\n\n### 6. \u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f \u043d\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0445 \u043f\u043e\u043b\u0435\u0439\n\n**\u041d\u0435\u043b\u044c\u0437\u044f \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c:**\n- \u274c `createdBy` - \u0430\u0432\u0442\u043e\u0440 (\u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438)\n- \u274c `createdAt` - \u0434\u0430\u0442\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f\n- \u274c `author` - \u0430\u0432\u0442\u043e\u0440 (\u0430\u043b\u0438\u0430\u0441 createdBy)\n\n**\u041c\u043e\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c:**\n- \u2705 `lead` - \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c \u043f\u0440\u043e\u0435\u043a\u0442\u0430\n- \u2705 `assignee` - \u0438\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c \u0437\u0430\u0434\u0430\u0447\u0438\n- \u2705 `teamUsers` - \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438\n- \u2705 `followers` - \u043d\u0430\u0431\u043b\u044e\u0434\u0430\u0442\u0435\u043b\u0438\n\n```python\n# \u041f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\nawait client.entities.update(\n entity_id=project_id,\n lead=\"user_login\",\n teamUsers=[\"user1\", \"user2\"]\n)\n```\n\n### \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0437\u0430\u043c\u0435\u0447\u0430\u043d\u0438\u044f\n\n**\u0410\u0432\u0442\u043e\u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432:** \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u0443\u0435\u0442 snake_case \u0432 camelCase:\n```python\nper_page=50 # \u2192 perPage=50 \u0432 API\n```\n\n**\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a:** \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 debug \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0434\u043b\u044f \u0434\u0438\u0430\u0433\u043d\u043e\u0441\u0442\u0438\u043a\u0438:\n```python\nimport logging\nlogging.getLogger(\"YaTrackerApi\").setLevel(logging.DEBUG)\n```\n\n\u041f\u043e\u043b\u043d\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f: [context/gotchas.md](context/gotchas.md)\n\n## \ud83d\udccb \u0422\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f\n\n- Python 3.9+\n- aiohttp >= 3.12.15\n- python-dotenv >= 1.1.1\n\n## \ud83d\udcc4 \u041b\u0438\u0446\u0435\u043d\u0437\u0438\u044f\n\n\u041f\u0440\u043e\u0435\u043a\u0442 \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0434 \u043b\u0438\u0446\u0435\u043d\u0437\u0438\u0435\u0439 [MIT](LICENSE).\n\n## \ud83d\udc64 \u041a\u043e\u043d\u0442\u0430\u043a\u0442\u044b\n\n**\u0414\u0430\u043d\u0438\u0438\u043b**\n\n- GitHub: [@imdeniil](https://github.com/imdeniil)\n- Email: keemor821@gmail.com\n\n## \ud83d\udd17 \u0421\u0441\u044b\u043b\u043a\u0438\n\n- [PyPI](https://pypi.org/project/YaTrackerApi/)\n- [GitHub Repository](https://github.com/imdeniil/YaTrackerApi)\n- [Issues](https://github.com/imdeniil/YaTrackerApi/issues)\n- [\u041e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f Yandex Tracker API](https://cloud.yandex.ru/docs/tracker/about-api)\n\n---\n\n**\u0421\u043e\u0437\u0434\u0430\u043d\u043e \u0441 \u2764\ufe0f \u0434\u043b\u044f \u0443\u043f\u0440\u043e\u0449\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Yandex Tracker API**\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 Python \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Yandex Tracker API \u0441 \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043e\u0439",
"version": "2.1.2",
"project_urls": {
"Documentation": "https://github.com/imdeniil/YaTrackerApi#readme",
"Homepage": "https://github.com/imdeniil/YaTrackerApi",
"Issues": "https://github.com/imdeniil/YaTrackerApi/issues",
"Repository": "https://github.com/imdeniil/YaTrackerApi"
},
"split_keywords": [
"aiohttp",
" api",
" async",
" project-management",
" task-tracker",
" tracker",
" yandex"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "33589c12362479b7c65d93b5193303741c0127e9b0883601b1af933191c784ad",
"md5": "88e932a439f344e2bf41ff3e5729762a",
"sha256": "de498c89f716bc3a5415e3e3d7781b5c0badcdc7bdfe2d587c1017ea950818f7"
},
"downloads": -1,
"filename": "yatrackerapi-2.1.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "88e932a439f344e2bf41ff3e5729762a",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 86004,
"upload_time": "2025-10-07T22:15:08",
"upload_time_iso_8601": "2025-10-07T22:15:08.856732Z",
"url": "https://files.pythonhosted.org/packages/33/58/9c12362479b7c65d93b5193303741c0127e9b0883601b1af933191c784ad/yatrackerapi-2.1.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "5c3db6e3ab9a327e29b89733e915bc32f9d48f40c0112f8697838208b69ce4a4",
"md5": "76094a8e77b6f21efac0c0766457d60a",
"sha256": "2381ddded70440b42c0719dd4f3aaee8198b85986357e738b0411e5ae3097989"
},
"downloads": -1,
"filename": "yatrackerapi-2.1.2.tar.gz",
"has_sig": false,
"md5_digest": "76094a8e77b6f21efac0c0766457d60a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 110583,
"upload_time": "2025-10-07T22:15:10",
"upload_time_iso_8601": "2025-10-07T22:15:10.927944Z",
"url": "https://files.pythonhosted.org/packages/5c/3d/b6e3ab9a327e29b89733e915bc32f9d48f40c0112f8697838208b69ce4a4/yatrackerapi-2.1.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-07 22:15:10",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "imdeniil",
"github_project": "YaTrackerApi#readme",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "yatrackerapi"
}