megaplan-sdk 0.2.2__tar.gz → 0.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {megaplan_sdk-0.2.2/src/megaplan_sdk.egg-info → megaplan_sdk-0.2.3}/PKG-INFO +84 -2
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/README.md +83 -1
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/pyproject.toml +1 -1
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/__init__.py +4 -1
- megaplan_sdk-0.2.3/src/megaplan_sdk/constants.py +42 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/task.py +7 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/comments.py +61 -4
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/deals.py +7 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/full_details.py +15 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/projects.py +7 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/tasks.py +27 -1
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3/src/megaplan_sdk.egg-info}/PKG-INFO +84 -2
- megaplan_sdk-0.2.2/src/megaplan_sdk/constants.py +0 -17
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/LICENSE +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/setup.cfg +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/auth.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/cache.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/client.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/exceptions.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/filter_builder.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/helpers.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/http_client.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/logging_config.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/__init__.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/base.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/comment.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/common.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/contractor.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/deal.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/department.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/employee.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/filter.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/group.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/milestone.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/participant.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/project.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/__init__.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/auth.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/base.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/contractors.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/departments.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/employees.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/filters.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk/types.py +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk.egg-info/SOURCES.txt +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk.egg-info/dependency_links.txt +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk.egg-info/requires.txt +0 -0
- {megaplan_sdk-0.2.2 → megaplan_sdk-0.2.3}/src/megaplan_sdk.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: megaplan-sdk
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Professional Python SDK for Megaplan API v3
|
|
5
5
|
Author-email: Maxim Borzov <max@borzov.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -73,6 +73,7 @@ Dynamic: license-file
|
|
|
73
73
|
- [Глобальные дефолтные лимиты](#глобальные-дефолтные-лимиты)
|
|
74
74
|
- [Автоматическая подгрузка связанных сущностей](#автоматическая-подгрузка-связанных-сущностей)
|
|
75
75
|
- [Работа с фильтрами](#работа-с-фильтрами)
|
|
76
|
+
- [Работа с комментариями](#работа-с-комментариями)
|
|
76
77
|
- [Настройка HTTP-клиента](#настройка-http-клиента)
|
|
77
78
|
- [Работа через прокси](#работа-через-прокси)
|
|
78
79
|
- [Ручное управление токенами](#ручное-управление-токенами)
|
|
@@ -429,7 +430,7 @@ print(details.deal.name) # для сделок
|
|
|
429
430
|
# Связанные данные
|
|
430
431
|
if details.comments:
|
|
431
432
|
for comment in details.comments:
|
|
432
|
-
print(comment.
|
|
433
|
+
print(comment.content)
|
|
433
434
|
|
|
434
435
|
if details.history:
|
|
435
436
|
print(f"Записей в истории: {len(details.history)}")
|
|
@@ -519,6 +520,37 @@ tasks = await client.tasks.list(filter=filter_obj)
|
|
|
519
520
|
- `todos: list[BaseEntity]` - Подзадачи-чеклисты
|
|
520
521
|
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
521
522
|
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
523
|
+
- `activity: str | None` - Дата последней активности (API поле `activity`)
|
|
524
|
+
- `last_comment_time_created: str | None` - Время последнего комментария (API поле `lastCommentTimeCreated`)
|
|
525
|
+
- `status_change_time: str | None` - Время смены статуса (API поле `statusChangeTime`)
|
|
526
|
+
- `actual_start: str | None` - Фактическое время начала (API поле `actualStart`)
|
|
527
|
+
- `last_view: str | None` - Время последнего просмотра (API поле `lastView`)
|
|
528
|
+
|
|
529
|
+
### Фильтрация задач по временным полям
|
|
530
|
+
|
|
531
|
+
Временны́е поля (`activity`, `lastCommentTimeCreated` и др.) не возвращаются в `tasks.list()` по умолчанию.
|
|
532
|
+
Используйте константу `DEFAULT_TASK_LIST_FIELDS`, чтобы запросить их явно:
|
|
533
|
+
|
|
534
|
+
```python
|
|
535
|
+
from megaplan_sdk import DEFAULT_TASK_LIST_FIELDS
|
|
536
|
+
|
|
537
|
+
# Запросить задачи с временными полями (activity, lastCommentTimeCreated и др.)
|
|
538
|
+
tasks = await client.tasks.list(
|
|
539
|
+
limit=50,
|
|
540
|
+
fields=list(DEFAULT_TASK_LIST_FIELDS),
|
|
541
|
+
)
|
|
542
|
+
for task in tasks:
|
|
543
|
+
print(f"{task.name}: активность {task.activity}")
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
> **Примечание:** Сортировка по `"timeUpdated"` не поддерживается API — используйте `"activity"`:
|
|
547
|
+
>
|
|
548
|
+
> ```python
|
|
549
|
+
> tasks = await client.tasks.list(
|
|
550
|
+
> sort_by=[{"fieldName": "activity", "order": "desc"}],
|
|
551
|
+
> fields=list(DEFAULT_TASK_LIST_FIELDS),
|
|
552
|
+
> )
|
|
553
|
+
> ```
|
|
522
554
|
|
|
523
555
|
### Упрощенные методы создания
|
|
524
556
|
|
|
@@ -1502,6 +1534,56 @@ updated = await client.filters.update("task", filter_id=123, filter_config={...}
|
|
|
1502
1534
|
export_data = await client.filters.export("task", filter_id=123)
|
|
1503
1535
|
```
|
|
1504
1536
|
|
|
1537
|
+
### Работа с комментариями
|
|
1538
|
+
|
|
1539
|
+
Комментарии доступны для задач, проектов и сделок через `client.comments`.
|
|
1540
|
+
|
|
1541
|
+
#### Получение комментариев
|
|
1542
|
+
|
|
1543
|
+
```python
|
|
1544
|
+
# Комментарии задачи (entity_type по умолчанию "task")
|
|
1545
|
+
comments = await client.comments.list(entity_id=42)
|
|
1546
|
+
|
|
1547
|
+
# Комментарии проекта
|
|
1548
|
+
comments = await client.comments.list(entity_id=5, entity_type="project")
|
|
1549
|
+
|
|
1550
|
+
# Комментарии сделки
|
|
1551
|
+
comments = await client.comments.list(entity_id=200, entity_type="deal")
|
|
1552
|
+
|
|
1553
|
+
# Автоматическая пагинация
|
|
1554
|
+
async for comment in client.comments.iterate(entity_id=42):
|
|
1555
|
+
print(comment.content)
|
|
1556
|
+
```
|
|
1557
|
+
|
|
1558
|
+
#### Подгрузка авторов через expand
|
|
1559
|
+
|
|
1560
|
+
API Мегаплана не раскрывает поле `owner` (автор комментария) в списке комментариев.
|
|
1561
|
+
Используйте `expand=["owner"]`, чтобы SDK дозагрузил авторов отдельными запросами (с кэшированием):
|
|
1562
|
+
|
|
1563
|
+
```python
|
|
1564
|
+
# Комментарии задачи с именами авторов
|
|
1565
|
+
comments = await client.comments.list(entity_id=42, expand=["owner"])
|
|
1566
|
+
for comment in comments:
|
|
1567
|
+
author_name = comment.owner.name if comment.owner else "неизвестен"
|
|
1568
|
+
print(f"{author_name}: {comment.content}")
|
|
1569
|
+
```
|
|
1570
|
+
|
|
1571
|
+
#### Создание комментария
|
|
1572
|
+
|
|
1573
|
+
```python
|
|
1574
|
+
# Комментарий к задаче
|
|
1575
|
+
comment = await client.comments.create(entity_id=42, comment_data={"text": "Текст комментария"})
|
|
1576
|
+
|
|
1577
|
+
# Комментарий к проекту
|
|
1578
|
+
comment = await client.comments.create(entity_id=5, comment_data={"text": "Текст"}, entity_type="project")
|
|
1579
|
+
|
|
1580
|
+
# Комментарий к сделке
|
|
1581
|
+
comment = await client.comments.create(entity_id=200, comment_data={"text": "Текст"}, entity_type="deal")
|
|
1582
|
+
```
|
|
1583
|
+
|
|
1584
|
+
> **Ограничение:** Комментарии контрагентов не поддерживаются API (возвращает 500).
|
|
1585
|
+
> Используйте комментарии в связанных сделках или задачах.
|
|
1586
|
+
|
|
1505
1587
|
### Настройка HTTP-клиента
|
|
1506
1588
|
|
|
1507
1589
|
```python
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
- [Глобальные дефолтные лимиты](#глобальные-дефолтные-лимиты)
|
|
42
42
|
- [Автоматическая подгрузка связанных сущностей](#автоматическая-подгрузка-связанных-сущностей)
|
|
43
43
|
- [Работа с фильтрами](#работа-с-фильтрами)
|
|
44
|
+
- [Работа с комментариями](#работа-с-комментариями)
|
|
44
45
|
- [Настройка HTTP-клиента](#настройка-http-клиента)
|
|
45
46
|
- [Работа через прокси](#работа-через-прокси)
|
|
46
47
|
- [Ручное управление токенами](#ручное-управление-токенами)
|
|
@@ -397,7 +398,7 @@ print(details.deal.name) # для сделок
|
|
|
397
398
|
# Связанные данные
|
|
398
399
|
if details.comments:
|
|
399
400
|
for comment in details.comments:
|
|
400
|
-
print(comment.
|
|
401
|
+
print(comment.content)
|
|
401
402
|
|
|
402
403
|
if details.history:
|
|
403
404
|
print(f"Записей в истории: {len(details.history)}")
|
|
@@ -487,6 +488,37 @@ tasks = await client.tasks.list(filter=filter_obj)
|
|
|
487
488
|
- `todos: list[BaseEntity]` - Подзадачи-чеклисты
|
|
488
489
|
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
489
490
|
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
491
|
+
- `activity: str | None` - Дата последней активности (API поле `activity`)
|
|
492
|
+
- `last_comment_time_created: str | None` - Время последнего комментария (API поле `lastCommentTimeCreated`)
|
|
493
|
+
- `status_change_time: str | None` - Время смены статуса (API поле `statusChangeTime`)
|
|
494
|
+
- `actual_start: str | None` - Фактическое время начала (API поле `actualStart`)
|
|
495
|
+
- `last_view: str | None` - Время последнего просмотра (API поле `lastView`)
|
|
496
|
+
|
|
497
|
+
### Фильтрация задач по временным полям
|
|
498
|
+
|
|
499
|
+
Временны́е поля (`activity`, `lastCommentTimeCreated` и др.) не возвращаются в `tasks.list()` по умолчанию.
|
|
500
|
+
Используйте константу `DEFAULT_TASK_LIST_FIELDS`, чтобы запросить их явно:
|
|
501
|
+
|
|
502
|
+
```python
|
|
503
|
+
from megaplan_sdk import DEFAULT_TASK_LIST_FIELDS
|
|
504
|
+
|
|
505
|
+
# Запросить задачи с временными полями (activity, lastCommentTimeCreated и др.)
|
|
506
|
+
tasks = await client.tasks.list(
|
|
507
|
+
limit=50,
|
|
508
|
+
fields=list(DEFAULT_TASK_LIST_FIELDS),
|
|
509
|
+
)
|
|
510
|
+
for task in tasks:
|
|
511
|
+
print(f"{task.name}: активность {task.activity}")
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
> **Примечание:** Сортировка по `"timeUpdated"` не поддерживается API — используйте `"activity"`:
|
|
515
|
+
>
|
|
516
|
+
> ```python
|
|
517
|
+
> tasks = await client.tasks.list(
|
|
518
|
+
> sort_by=[{"fieldName": "activity", "order": "desc"}],
|
|
519
|
+
> fields=list(DEFAULT_TASK_LIST_FIELDS),
|
|
520
|
+
> )
|
|
521
|
+
> ```
|
|
490
522
|
|
|
491
523
|
### Упрощенные методы создания
|
|
492
524
|
|
|
@@ -1470,6 +1502,56 @@ updated = await client.filters.update("task", filter_id=123, filter_config={...}
|
|
|
1470
1502
|
export_data = await client.filters.export("task", filter_id=123)
|
|
1471
1503
|
```
|
|
1472
1504
|
|
|
1505
|
+
### Работа с комментариями
|
|
1506
|
+
|
|
1507
|
+
Комментарии доступны для задач, проектов и сделок через `client.comments`.
|
|
1508
|
+
|
|
1509
|
+
#### Получение комментариев
|
|
1510
|
+
|
|
1511
|
+
```python
|
|
1512
|
+
# Комментарии задачи (entity_type по умолчанию "task")
|
|
1513
|
+
comments = await client.comments.list(entity_id=42)
|
|
1514
|
+
|
|
1515
|
+
# Комментарии проекта
|
|
1516
|
+
comments = await client.comments.list(entity_id=5, entity_type="project")
|
|
1517
|
+
|
|
1518
|
+
# Комментарии сделки
|
|
1519
|
+
comments = await client.comments.list(entity_id=200, entity_type="deal")
|
|
1520
|
+
|
|
1521
|
+
# Автоматическая пагинация
|
|
1522
|
+
async for comment in client.comments.iterate(entity_id=42):
|
|
1523
|
+
print(comment.content)
|
|
1524
|
+
```
|
|
1525
|
+
|
|
1526
|
+
#### Подгрузка авторов через expand
|
|
1527
|
+
|
|
1528
|
+
API Мегаплана не раскрывает поле `owner` (автор комментария) в списке комментариев.
|
|
1529
|
+
Используйте `expand=["owner"]`, чтобы SDK дозагрузил авторов отдельными запросами (с кэшированием):
|
|
1530
|
+
|
|
1531
|
+
```python
|
|
1532
|
+
# Комментарии задачи с именами авторов
|
|
1533
|
+
comments = await client.comments.list(entity_id=42, expand=["owner"])
|
|
1534
|
+
for comment in comments:
|
|
1535
|
+
author_name = comment.owner.name if comment.owner else "неизвестен"
|
|
1536
|
+
print(f"{author_name}: {comment.content}")
|
|
1537
|
+
```
|
|
1538
|
+
|
|
1539
|
+
#### Создание комментария
|
|
1540
|
+
|
|
1541
|
+
```python
|
|
1542
|
+
# Комментарий к задаче
|
|
1543
|
+
comment = await client.comments.create(entity_id=42, comment_data={"text": "Текст комментария"})
|
|
1544
|
+
|
|
1545
|
+
# Комментарий к проекту
|
|
1546
|
+
comment = await client.comments.create(entity_id=5, comment_data={"text": "Текст"}, entity_type="project")
|
|
1547
|
+
|
|
1548
|
+
# Комментарий к сделке
|
|
1549
|
+
comment = await client.comments.create(entity_id=200, comment_data={"text": "Текст"}, entity_type="deal")
|
|
1550
|
+
```
|
|
1551
|
+
|
|
1552
|
+
> **Ограничение:** Комментарии контрагентов не поддерживаются API (возвращает 500).
|
|
1553
|
+
> Используйте комментарии в связанных сделках или задачах.
|
|
1554
|
+
|
|
1473
1555
|
### Настройка HTTP-клиента
|
|
1474
1556
|
|
|
1475
1557
|
```python
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Megaplan Python SDK - Professional SDK for Megaplan API v3."""
|
|
2
2
|
|
|
3
3
|
from megaplan_sdk.client import MegaplanClient
|
|
4
|
+
from megaplan_sdk.constants import DEFAULT_TASK_LIST_FIELDS
|
|
4
5
|
from megaplan_sdk.exceptions import (
|
|
5
6
|
AuthenticationError,
|
|
6
7
|
AuthorizationError,
|
|
@@ -101,6 +102,8 @@ __all__ = [
|
|
|
101
102
|
"TaskFilterBuilder",
|
|
102
103
|
"TradeFilterBuilder",
|
|
103
104
|
"ProjectFilterBuilder",
|
|
105
|
+
# Constants
|
|
106
|
+
"DEFAULT_TASK_LIST_FIELDS",
|
|
104
107
|
]
|
|
105
108
|
|
|
106
|
-
__version__ = "0.2.
|
|
109
|
+
__version__ = "0.2.3"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Constants for Megaplan SDK."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ContentType:
|
|
5
|
+
"""Content type constants for Megaplan API entities."""
|
|
6
|
+
|
|
7
|
+
TASK = "Task"
|
|
8
|
+
PROJECT = "Project"
|
|
9
|
+
DEAL = "Deal"
|
|
10
|
+
EMPLOYEE = "Employee"
|
|
11
|
+
CONTRACTOR = "Contractor"
|
|
12
|
+
CONTRACTOR_COMPANY = "ContractorCompany"
|
|
13
|
+
CONTRACTOR_HUMAN = "ContractorHuman"
|
|
14
|
+
CONTRACTOR_CATEGORY = "ContractorCategory"
|
|
15
|
+
DEPARTMENT = "Department"
|
|
16
|
+
COMMENT = "Comment"
|
|
17
|
+
GROUP = "Group"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Task fields users commonly try to sort by that the Megaplan API rejects (422).
|
|
21
|
+
# Maps the unsupported field name to the supported replacement to suggest.
|
|
22
|
+
UNSUPPORTED_TASK_SORT_FIELDS: dict[str, str] = {
|
|
23
|
+
"timeUpdated": "activity",
|
|
24
|
+
"updatedAt": "activity",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Recommended `fields` set for tasks.list() so that date fields are populated.
|
|
28
|
+
# Megaplan list endpoints omit these unless explicitly requested, which makes
|
|
29
|
+
# client-side time-window filtering silently return nothing (#8).
|
|
30
|
+
# Only fields confirmed to exist on Task are included (no "timeUpdated" — see #7).
|
|
31
|
+
DEFAULT_TASK_LIST_FIELDS: tuple[str, ...] = (
|
|
32
|
+
"name",
|
|
33
|
+
"status",
|
|
34
|
+
"timeCreated",
|
|
35
|
+
"activity",
|
|
36
|
+
"lastCommentTimeCreated",
|
|
37
|
+
"statusChangeTime",
|
|
38
|
+
"actualStart",
|
|
39
|
+
"owner",
|
|
40
|
+
"responsible",
|
|
41
|
+
"commentsCount",
|
|
42
|
+
)
|
|
@@ -44,6 +44,13 @@ class Task(TimestampMixin):
|
|
|
44
44
|
tags: list[BaseEntity] | None = None
|
|
45
45
|
attaches: list[BaseEntity] | None = None
|
|
46
46
|
todos: list[BaseEntity] | None = None
|
|
47
|
+
activity: str | DateTime | None = None
|
|
48
|
+
last_comment_time_created: str | DateTime | None = Field(
|
|
49
|
+
alias="lastCommentTimeCreated", default=None
|
|
50
|
+
)
|
|
51
|
+
status_change_time: str | DateTime | None = Field(alias="statusChangeTime", default=None)
|
|
52
|
+
actual_start: str | DateTime | None = Field(alias="actualStart", default=None)
|
|
53
|
+
last_view: str | DateTime | None = Field(alias="lastView", default=None)
|
|
47
54
|
|
|
48
55
|
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
49
56
|
|
|
@@ -12,7 +12,12 @@ from megaplan_sdk.resources.base import BaseResource
|
|
|
12
12
|
class CommentsResource(BaseResource):
|
|
13
13
|
"""Resource for working with comments."""
|
|
14
14
|
|
|
15
|
-
async def create(
|
|
15
|
+
async def create(
|
|
16
|
+
self,
|
|
17
|
+
entity_id: int,
|
|
18
|
+
comment_data: dict[str, Any],
|
|
19
|
+
entity_type: str = "task",
|
|
20
|
+
) -> Comment:
|
|
16
21
|
"""Create a new comment for an entity.
|
|
17
22
|
|
|
18
23
|
Args:
|
|
@@ -20,6 +25,9 @@ class CommentsResource(BaseResource):
|
|
|
20
25
|
comment_data: Comment data dictionary.
|
|
21
26
|
Required: text
|
|
22
27
|
Optional: work (hours), attaches (file IDs)
|
|
28
|
+
entity_type: Entity type segment for the API path.
|
|
29
|
+
Allowed values: ``"task"`` | ``"project"`` | ``"deal"``.
|
|
30
|
+
Defaults to ``"task"``.
|
|
23
31
|
|
|
24
32
|
Returns:
|
|
25
33
|
Created comment.
|
|
@@ -30,14 +38,21 @@ class CommentsResource(BaseResource):
|
|
|
30
38
|
... entity_id=123,
|
|
31
39
|
... comment_data={"text": "Comment text", "work": 2.5}
|
|
32
40
|
... )
|
|
41
|
+
>>> # Create comment for project #55
|
|
42
|
+
>>> comment = await client.comments.create(
|
|
43
|
+
... entity_id=55,
|
|
44
|
+
... comment_data={"text": "Project note"},
|
|
45
|
+
... entity_type="project",
|
|
46
|
+
... )
|
|
33
47
|
"""
|
|
34
|
-
path = self._build_path("api", "v3",
|
|
48
|
+
path = self._build_path("api", "v3", entity_type, str(entity_id), "comments")
|
|
35
49
|
response = await self._http.post(path, json_data=comment_data)
|
|
36
50
|
return Comment(**response["data"])
|
|
37
51
|
|
|
38
52
|
async def list(
|
|
39
53
|
self,
|
|
40
54
|
entity_id: int,
|
|
55
|
+
entity_type: str = "task",
|
|
41
56
|
limit: int | None = None,
|
|
42
57
|
page_after: dict[str, Any] | None = None,
|
|
43
58
|
page_before: dict[str, Any] | None = None,
|
|
@@ -45,11 +60,15 @@ class CommentsResource(BaseResource):
|
|
|
45
60
|
fields: Any | None = None,
|
|
46
61
|
sort_by: list[dict[str, str]] | None = None,
|
|
47
62
|
only_requested_fields: bool | None = None,
|
|
63
|
+
expand: list[str] | None = None,
|
|
48
64
|
) -> list[Comment]:
|
|
49
65
|
"""Get list of comments for an entity.
|
|
50
66
|
|
|
51
67
|
Args:
|
|
52
68
|
entity_id: Parent entity ID (task, project, deal, etc.).
|
|
69
|
+
entity_type: Entity type segment for the API path.
|
|
70
|
+
Allowed values: ``"task"`` | ``"project"`` | ``"deal"``.
|
|
71
|
+
Defaults to ``"task"``.
|
|
53
72
|
limit: Number of items per page.
|
|
54
73
|
page_after: Load page starting from this entity.
|
|
55
74
|
page_before: Load page strictly before this entity.
|
|
@@ -57,6 +76,11 @@ class CommentsResource(BaseResource):
|
|
|
57
76
|
fields: Additional fields to include.
|
|
58
77
|
sort_by: Sort fields.
|
|
59
78
|
only_requested_fields: Return only requested fields.
|
|
79
|
+
expand: Fields to expand. Supported values: ``"owner"``.
|
|
80
|
+
When ``"owner"`` is included, resolves Employee comment authors
|
|
81
|
+
to full Employee objects via batch parallel requests (cached).
|
|
82
|
+
The API never returns populated owners, so this is the only
|
|
83
|
+
resolution path.
|
|
60
84
|
|
|
61
85
|
Returns:
|
|
62
86
|
List of comments.
|
|
@@ -64,8 +88,13 @@ class CommentsResource(BaseResource):
|
|
|
64
88
|
Examples:
|
|
65
89
|
>>> # Get all comments for task #123
|
|
66
90
|
>>> comments = await client.comments.list(entity_id=123)
|
|
91
|
+
>>> # Get all comments for project #55
|
|
92
|
+
>>> comments = await client.comments.list(entity_id=55, entity_type="project")
|
|
93
|
+
>>> # Resolve comment authors to full Employee objects
|
|
94
|
+
>>> comments = await client.comments.list(entity_id=123, expand=["owner"])
|
|
95
|
+
>>> print(comments[0].owner.name) # "Иван Петров"
|
|
67
96
|
"""
|
|
68
|
-
path = self._build_path("api", "v3",
|
|
97
|
+
path = self._build_path("api", "v3", entity_type, str(entity_id), "comments")
|
|
69
98
|
|
|
70
99
|
# Use base method to build params (DRY)
|
|
71
100
|
params = self._build_list_params(
|
|
@@ -78,7 +107,27 @@ class CommentsResource(BaseResource):
|
|
|
78
107
|
only_requested_fields=only_requested_fields,
|
|
79
108
|
)
|
|
80
109
|
|
|
81
|
-
|
|
110
|
+
comments = await self._get_list(path, Comment, params)
|
|
111
|
+
|
|
112
|
+
if not expand or "owner" not in expand or not comments:
|
|
113
|
+
return comments
|
|
114
|
+
|
|
115
|
+
from megaplan_sdk.models.employee import Employee
|
|
116
|
+
|
|
117
|
+
# Only Employee owners are resolvable via the employee endpoint.
|
|
118
|
+
employee_owners = [
|
|
119
|
+
c.owner for c in comments if c.owner is not None and c.owner.content_type == "Employee"
|
|
120
|
+
]
|
|
121
|
+
owner_map = await self._load_related_entities(employee_owners, "employee", Employee)
|
|
122
|
+
for comment in comments:
|
|
123
|
+
if (
|
|
124
|
+
comment.owner is not None
|
|
125
|
+
and comment.owner.content_type == "Employee"
|
|
126
|
+
and comment.owner.id in owner_map
|
|
127
|
+
):
|
|
128
|
+
comment.owner = owner_map[comment.owner.id]
|
|
129
|
+
|
|
130
|
+
return comments
|
|
82
131
|
|
|
83
132
|
async def get(self, comment_id: int) -> Comment:
|
|
84
133
|
"""Get comment by ID.
|
|
@@ -119,6 +168,7 @@ class CommentsResource(BaseResource):
|
|
|
119
168
|
async def iterate(
|
|
120
169
|
self,
|
|
121
170
|
entity_id: int,
|
|
171
|
+
entity_type: str = "task",
|
|
122
172
|
limit: int = 100,
|
|
123
173
|
**kwargs: Any,
|
|
124
174
|
) -> AsyncIterator[Comment]:
|
|
@@ -126,6 +176,9 @@ class CommentsResource(BaseResource):
|
|
|
126
176
|
|
|
127
177
|
Args:
|
|
128
178
|
entity_id: Parent entity ID (task, project, deal, etc.).
|
|
179
|
+
entity_type: Entity type segment for the API path.
|
|
180
|
+
Allowed values: ``"task"`` | ``"project"`` | ``"deal"``.
|
|
181
|
+
Defaults to ``"task"``.
|
|
129
182
|
limit: Number of items per page.
|
|
130
183
|
**kwargs: Additional parameters to pass to list().
|
|
131
184
|
|
|
@@ -136,6 +189,9 @@ class CommentsResource(BaseResource):
|
|
|
136
189
|
>>> # Iterate over all comments for task #123
|
|
137
190
|
>>> async for comment in client.comments.iterate(entity_id=123):
|
|
138
191
|
... print(comment.content)
|
|
192
|
+
>>> # Iterate over all comments for project #55
|
|
193
|
+
>>> async for comment in client.comments.iterate(entity_id=55, entity_type="project"):
|
|
194
|
+
... print(comment.content)
|
|
139
195
|
"""
|
|
140
196
|
comment: Comment
|
|
141
197
|
async for comment in self._iterate_generic( # type: ignore[valid-type]
|
|
@@ -143,6 +199,7 @@ class CommentsResource(BaseResource):
|
|
|
143
199
|
self.list,
|
|
144
200
|
limit,
|
|
145
201
|
entity_id=entity_id,
|
|
202
|
+
entity_type=entity_type,
|
|
146
203
|
**kwargs,
|
|
147
204
|
):
|
|
148
205
|
yield comment
|
|
@@ -710,10 +710,17 @@ class DealsResource(BaseResource, FullDetailsMixin):
|
|
|
710
710
|
None = use global default (from MegaplanClient) or API default.
|
|
711
711
|
Explicit value overrides global default.
|
|
712
712
|
Example: comments_limit=50 returns max 50 comments.
|
|
713
|
+
Requires the matching include_* flag to be True; passing a
|
|
714
|
+
limit without it raises ValueError.
|
|
715
|
+
Note: the API caps a single comments page (~100); requesting
|
|
716
|
+
more returns at most one server page. Use client.comments.iterate
|
|
717
|
+
for full pagination.
|
|
713
718
|
history_limit: Limit for history (if included).
|
|
714
719
|
None = use global default (from MegaplanClient) or API default.
|
|
715
720
|
Explicit value overrides global default.
|
|
716
721
|
Example: history_limit=100 returns max 100 history entries.
|
|
722
|
+
Requires the matching include_* flag to be True; passing a
|
|
723
|
+
limit without it raises ValueError.
|
|
717
724
|
|
|
718
725
|
Returns:
|
|
719
726
|
DealFullDetails object with all requested data.
|
|
@@ -71,6 +71,21 @@ class FullDetailsMixin:
|
|
|
71
71
|
getter = getattr(self, entity_getter)
|
|
72
72
|
main_entity = await getter(entity_id)
|
|
73
73
|
|
|
74
|
+
# Guard: a *_limit without its include_* flag silently does nothing (#2).
|
|
75
|
+
# Reject it with a clear error instead of returning empty/stub data.
|
|
76
|
+
# This guard runs BEFORE the global-defaults block so that a client-level
|
|
77
|
+
# default (injected below) never triggers a false-positive error here.
|
|
78
|
+
for item_config in config:
|
|
79
|
+
limit_param = item_config.limit_param
|
|
80
|
+
if limit_param is None:
|
|
81
|
+
continue
|
|
82
|
+
if kwargs.get(limit_param) is not None and not kwargs.get(item_config.include_flag):
|
|
83
|
+
raise ValueError(
|
|
84
|
+
f"'{limit_param}' was provided but '{item_config.include_flag}' is False. "
|
|
85
|
+
f"Pass '{item_config.include_flag}=True' to load this data, "
|
|
86
|
+
f"or omit '{limit_param}'."
|
|
87
|
+
)
|
|
88
|
+
|
|
74
89
|
# Apply global defaults for limit parameters if not explicitly provided
|
|
75
90
|
# Note: parameters are always in kwargs (even if None), so check value instead of presence
|
|
76
91
|
if hasattr(self, "_default_comments_limit") and kwargs.get("comments_limit") is None:
|
|
@@ -852,10 +852,17 @@ class ProjectsResource(BaseResource, FullDetailsMixin):
|
|
|
852
852
|
None = use global default (from MegaplanClient) or API default.
|
|
853
853
|
Explicit value overrides global default.
|
|
854
854
|
Example: comments_limit=50 returns max 50 comments.
|
|
855
|
+
Requires the matching include_* flag to be True; passing a
|
|
856
|
+
limit without it raises ValueError.
|
|
857
|
+
Note: the API caps a single comments page (~100); requesting
|
|
858
|
+
more returns at most one server page. Use client.comments.iterate
|
|
859
|
+
for full pagination.
|
|
855
860
|
history_limit: Limit for history (if included).
|
|
856
861
|
None = use global default (from MegaplanClient) or API default.
|
|
857
862
|
Explicit value overrides global default.
|
|
858
863
|
Example: history_limit=100 returns max 100 history entries.
|
|
864
|
+
Requires the matching include_* flag to be True; passing a
|
|
865
|
+
limit without it raises ValueError.
|
|
859
866
|
|
|
860
867
|
Returns:
|
|
861
868
|
ProjectFullDetails object with all requested data.
|
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
from collections.abc import AsyncIterator
|
|
6
6
|
from typing import TYPE_CHECKING, Any, overload
|
|
7
7
|
|
|
8
|
-
from megaplan_sdk.constants import ContentType
|
|
8
|
+
from megaplan_sdk.constants import UNSUPPORTED_TASK_SORT_FIELDS, ContentType
|
|
9
9
|
from megaplan_sdk.models.comment import Comment
|
|
10
10
|
from megaplan_sdk.models.task import Task, TaskFullDetails
|
|
11
11
|
from megaplan_sdk.resources.base import BaseResource
|
|
@@ -219,6 +219,13 @@ class TasksResource(BaseResource, FullDetailsMixin):
|
|
|
219
219
|
page_before: Load page strictly before this entity.
|
|
220
220
|
page_with: Load page containing this entity.
|
|
221
221
|
fields: Additional fields to include.
|
|
222
|
+
**Important:** list endpoints do NOT return date fields
|
|
223
|
+
(timeCreated, activity, lastCommentTimeCreated, ...) unless
|
|
224
|
+
requested here. To filter by a time window, pass:
|
|
225
|
+
from megaplan_sdk import DEFAULT_TASK_LIST_FIELDS
|
|
226
|
+
tasks = await client.tasks.list(fields=list(DEFAULT_TASK_LIST_FIELDS))
|
|
227
|
+
Without this, those fields are None and time-window filters
|
|
228
|
+
silently match nothing.
|
|
222
229
|
sort_by: Sort fields.
|
|
223
230
|
only_requested_fields: Return only requested fields.
|
|
224
231
|
expand: List of fields to expand (e.g., ["responsible", "owner"]).
|
|
@@ -260,6 +267,18 @@ class TasksResource(BaseResource, FullDetailsMixin):
|
|
|
260
267
|
f"Valid values: {sorted(VALID_TASK_STATUSES)}"
|
|
261
268
|
)
|
|
262
269
|
|
|
270
|
+
# Validate sort_by against fields the API rejects with a raw 422 (#7).
|
|
271
|
+
if sort_by:
|
|
272
|
+
for rule in sort_by:
|
|
273
|
+
field_name = rule.get("fieldName")
|
|
274
|
+
if field_name in UNSUPPORTED_TASK_SORT_FIELDS:
|
|
275
|
+
suggestion = UNSUPPORTED_TASK_SORT_FIELDS[field_name]
|
|
276
|
+
raise ValueError(
|
|
277
|
+
f"Task cannot be sorted by '{field_name}' (API returns 422). "
|
|
278
|
+
f"Use '{suggestion}' instead — e.g. "
|
|
279
|
+
f'sort_by=[{{"fieldName": "{suggestion}", "desc": True}}].'
|
|
280
|
+
)
|
|
281
|
+
|
|
263
282
|
# Convert filter ID to object format if needed
|
|
264
283
|
processed_filter = filter
|
|
265
284
|
if filter is not None and isinstance(filter, int | str) and not isinstance(filter, dict):
|
|
@@ -1064,10 +1083,17 @@ class TasksResource(BaseResource, FullDetailsMixin):
|
|
|
1064
1083
|
None = use global default (from MegaplanClient) or API default.
|
|
1065
1084
|
Explicit value overrides global default.
|
|
1066
1085
|
Example: comments_limit=50 returns max 50 comments.
|
|
1086
|
+
Requires the matching include_* flag to be True; passing a
|
|
1087
|
+
limit without it raises ValueError.
|
|
1088
|
+
Note: the API caps a single comments page (~100); requesting
|
|
1089
|
+
more returns at most one server page. Use client.comments.iterate
|
|
1090
|
+
for full pagination.
|
|
1067
1091
|
history_limit: Limit for history (if included).
|
|
1068
1092
|
None = use global default (from MegaplanClient) or API default.
|
|
1069
1093
|
Explicit value overrides global default.
|
|
1070
1094
|
Example: history_limit=100 returns max 100 history entries.
|
|
1095
|
+
Requires the matching include_* flag to be True; passing a
|
|
1096
|
+
limit without it raises ValueError.
|
|
1071
1097
|
|
|
1072
1098
|
Returns:
|
|
1073
1099
|
TaskFullDetails object with all requested data.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: megaplan-sdk
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Professional Python SDK for Megaplan API v3
|
|
5
5
|
Author-email: Maxim Borzov <max@borzov.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -73,6 +73,7 @@ Dynamic: license-file
|
|
|
73
73
|
- [Глобальные дефолтные лимиты](#глобальные-дефолтные-лимиты)
|
|
74
74
|
- [Автоматическая подгрузка связанных сущностей](#автоматическая-подгрузка-связанных-сущностей)
|
|
75
75
|
- [Работа с фильтрами](#работа-с-фильтрами)
|
|
76
|
+
- [Работа с комментариями](#работа-с-комментариями)
|
|
76
77
|
- [Настройка HTTP-клиента](#настройка-http-клиента)
|
|
77
78
|
- [Работа через прокси](#работа-через-прокси)
|
|
78
79
|
- [Ручное управление токенами](#ручное-управление-токенами)
|
|
@@ -429,7 +430,7 @@ print(details.deal.name) # для сделок
|
|
|
429
430
|
# Связанные данные
|
|
430
431
|
if details.comments:
|
|
431
432
|
for comment in details.comments:
|
|
432
|
-
print(comment.
|
|
433
|
+
print(comment.content)
|
|
433
434
|
|
|
434
435
|
if details.history:
|
|
435
436
|
print(f"Записей в истории: {len(details.history)}")
|
|
@@ -519,6 +520,37 @@ tasks = await client.tasks.list(filter=filter_obj)
|
|
|
519
520
|
- `todos: list[BaseEntity]` - Подзадачи-чеклисты
|
|
520
521
|
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
521
522
|
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
523
|
+
- `activity: str | None` - Дата последней активности (API поле `activity`)
|
|
524
|
+
- `last_comment_time_created: str | None` - Время последнего комментария (API поле `lastCommentTimeCreated`)
|
|
525
|
+
- `status_change_time: str | None` - Время смены статуса (API поле `statusChangeTime`)
|
|
526
|
+
- `actual_start: str | None` - Фактическое время начала (API поле `actualStart`)
|
|
527
|
+
- `last_view: str | None` - Время последнего просмотра (API поле `lastView`)
|
|
528
|
+
|
|
529
|
+
### Фильтрация задач по временным полям
|
|
530
|
+
|
|
531
|
+
Временны́е поля (`activity`, `lastCommentTimeCreated` и др.) не возвращаются в `tasks.list()` по умолчанию.
|
|
532
|
+
Используйте константу `DEFAULT_TASK_LIST_FIELDS`, чтобы запросить их явно:
|
|
533
|
+
|
|
534
|
+
```python
|
|
535
|
+
from megaplan_sdk import DEFAULT_TASK_LIST_FIELDS
|
|
536
|
+
|
|
537
|
+
# Запросить задачи с временными полями (activity, lastCommentTimeCreated и др.)
|
|
538
|
+
tasks = await client.tasks.list(
|
|
539
|
+
limit=50,
|
|
540
|
+
fields=list(DEFAULT_TASK_LIST_FIELDS),
|
|
541
|
+
)
|
|
542
|
+
for task in tasks:
|
|
543
|
+
print(f"{task.name}: активность {task.activity}")
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
> **Примечание:** Сортировка по `"timeUpdated"` не поддерживается API — используйте `"activity"`:
|
|
547
|
+
>
|
|
548
|
+
> ```python
|
|
549
|
+
> tasks = await client.tasks.list(
|
|
550
|
+
> sort_by=[{"fieldName": "activity", "order": "desc"}],
|
|
551
|
+
> fields=list(DEFAULT_TASK_LIST_FIELDS),
|
|
552
|
+
> )
|
|
553
|
+
> ```
|
|
522
554
|
|
|
523
555
|
### Упрощенные методы создания
|
|
524
556
|
|
|
@@ -1502,6 +1534,56 @@ updated = await client.filters.update("task", filter_id=123, filter_config={...}
|
|
|
1502
1534
|
export_data = await client.filters.export("task", filter_id=123)
|
|
1503
1535
|
```
|
|
1504
1536
|
|
|
1537
|
+
### Работа с комментариями
|
|
1538
|
+
|
|
1539
|
+
Комментарии доступны для задач, проектов и сделок через `client.comments`.
|
|
1540
|
+
|
|
1541
|
+
#### Получение комментариев
|
|
1542
|
+
|
|
1543
|
+
```python
|
|
1544
|
+
# Комментарии задачи (entity_type по умолчанию "task")
|
|
1545
|
+
comments = await client.comments.list(entity_id=42)
|
|
1546
|
+
|
|
1547
|
+
# Комментарии проекта
|
|
1548
|
+
comments = await client.comments.list(entity_id=5, entity_type="project")
|
|
1549
|
+
|
|
1550
|
+
# Комментарии сделки
|
|
1551
|
+
comments = await client.comments.list(entity_id=200, entity_type="deal")
|
|
1552
|
+
|
|
1553
|
+
# Автоматическая пагинация
|
|
1554
|
+
async for comment in client.comments.iterate(entity_id=42):
|
|
1555
|
+
print(comment.content)
|
|
1556
|
+
```
|
|
1557
|
+
|
|
1558
|
+
#### Подгрузка авторов через expand
|
|
1559
|
+
|
|
1560
|
+
API Мегаплана не раскрывает поле `owner` (автор комментария) в списке комментариев.
|
|
1561
|
+
Используйте `expand=["owner"]`, чтобы SDK дозагрузил авторов отдельными запросами (с кэшированием):
|
|
1562
|
+
|
|
1563
|
+
```python
|
|
1564
|
+
# Комментарии задачи с именами авторов
|
|
1565
|
+
comments = await client.comments.list(entity_id=42, expand=["owner"])
|
|
1566
|
+
for comment in comments:
|
|
1567
|
+
author_name = comment.owner.name if comment.owner else "неизвестен"
|
|
1568
|
+
print(f"{author_name}: {comment.content}")
|
|
1569
|
+
```
|
|
1570
|
+
|
|
1571
|
+
#### Создание комментария
|
|
1572
|
+
|
|
1573
|
+
```python
|
|
1574
|
+
# Комментарий к задаче
|
|
1575
|
+
comment = await client.comments.create(entity_id=42, comment_data={"text": "Текст комментария"})
|
|
1576
|
+
|
|
1577
|
+
# Комментарий к проекту
|
|
1578
|
+
comment = await client.comments.create(entity_id=5, comment_data={"text": "Текст"}, entity_type="project")
|
|
1579
|
+
|
|
1580
|
+
# Комментарий к сделке
|
|
1581
|
+
comment = await client.comments.create(entity_id=200, comment_data={"text": "Текст"}, entity_type="deal")
|
|
1582
|
+
```
|
|
1583
|
+
|
|
1584
|
+
> **Ограничение:** Комментарии контрагентов не поддерживаются API (возвращает 500).
|
|
1585
|
+
> Используйте комментарии в связанных сделках или задачах.
|
|
1586
|
+
|
|
1505
1587
|
### Настройка HTTP-клиента
|
|
1506
1588
|
|
|
1507
1589
|
```python
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"""Constants for Megaplan SDK."""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class ContentType:
|
|
5
|
-
"""Content type constants for Megaplan API entities."""
|
|
6
|
-
|
|
7
|
-
TASK = "Task"
|
|
8
|
-
PROJECT = "Project"
|
|
9
|
-
DEAL = "Deal"
|
|
10
|
-
EMPLOYEE = "Employee"
|
|
11
|
-
CONTRACTOR = "Contractor"
|
|
12
|
-
CONTRACTOR_COMPANY = "ContractorCompany"
|
|
13
|
-
CONTRACTOR_HUMAN = "ContractorHuman"
|
|
14
|
-
CONTRACTOR_CATEGORY = "ContractorCategory"
|
|
15
|
-
DEPARTMENT = "Department"
|
|
16
|
-
COMMENT = "Comment"
|
|
17
|
-
GROUP = "Group"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|