megaplan-sdk 0.1.0__py3-none-any.whl
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/__init__.py +67 -0
- megaplan_sdk/auth.py +185 -0
- megaplan_sdk/cache.py +192 -0
- megaplan_sdk/client.py +201 -0
- megaplan_sdk/constants.py +16 -0
- megaplan_sdk/exceptions.py +180 -0
- megaplan_sdk/helpers.py +108 -0
- megaplan_sdk/http_client.py +390 -0
- megaplan_sdk/logging_config.py +53 -0
- megaplan_sdk/models/__init__.py +22 -0
- megaplan_sdk/models/base.py +16 -0
- megaplan_sdk/models/comment.py +58 -0
- megaplan_sdk/models/common.py +107 -0
- megaplan_sdk/models/contractor.py +137 -0
- megaplan_sdk/models/deal.py +96 -0
- megaplan_sdk/models/department.py +40 -0
- megaplan_sdk/models/employee.py +117 -0
- megaplan_sdk/models/project.py +76 -0
- megaplan_sdk/models/task.py +75 -0
- megaplan_sdk/resources/__init__.py +15 -0
- megaplan_sdk/resources/auth.py +73 -0
- megaplan_sdk/resources/base.py +794 -0
- megaplan_sdk/resources/comments.py +148 -0
- megaplan_sdk/resources/contractors.py +173 -0
- megaplan_sdk/resources/deals.py +625 -0
- megaplan_sdk/resources/departments.py +70 -0
- megaplan_sdk/resources/employees.py +216 -0
- megaplan_sdk/resources/full_details.py +143 -0
- megaplan_sdk/resources/projects.py +854 -0
- megaplan_sdk/resources/tasks.py +932 -0
- megaplan_sdk/types.py +56 -0
- megaplan_sdk-0.1.0.dist-info/METADATA +1383 -0
- megaplan_sdk-0.1.0.dist-info/RECORD +36 -0
- megaplan_sdk-0.1.0.dist-info/WHEEL +5 -0
- megaplan_sdk-0.1.0.dist-info/licenses/LICENSE +21 -0
- megaplan_sdk-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""Comments resource for Megaplan API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import AsyncIterator
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from megaplan_sdk.models.comment import Comment
|
|
9
|
+
from megaplan_sdk.resources.base import BaseResource
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CommentsResource(BaseResource):
|
|
13
|
+
"""Resource for working with comments."""
|
|
14
|
+
|
|
15
|
+
async def create(self, entity_id: int, comment_data: dict[str, Any]) -> Comment:
|
|
16
|
+
"""Create a new comment for an entity.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
entity_id: Parent entity ID (task, project, deal, etc.).
|
|
20
|
+
comment_data: Comment data dictionary.
|
|
21
|
+
Required: text
|
|
22
|
+
Optional: work (hours), attaches (file IDs)
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Created comment.
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
>>> # Create comment for task #123
|
|
29
|
+
>>> comment = await client.comments.create(
|
|
30
|
+
... entity_id=123,
|
|
31
|
+
... comment_data={"text": "Comment text", "work": 2.5}
|
|
32
|
+
... )
|
|
33
|
+
"""
|
|
34
|
+
path = self._build_path("api", "v3", "todo", str(entity_id), "comments")
|
|
35
|
+
response = await self._http.post(path, json_data=comment_data)
|
|
36
|
+
return Comment(**response["data"])
|
|
37
|
+
|
|
38
|
+
async def list(
|
|
39
|
+
self,
|
|
40
|
+
entity_id: int,
|
|
41
|
+
limit: int | None = None,
|
|
42
|
+
page_after: dict[str, Any] | None = None,
|
|
43
|
+
page_before: dict[str, Any] | None = None,
|
|
44
|
+
page_with: dict[str, Any] | None = None,
|
|
45
|
+
fields: Any | None = None,
|
|
46
|
+
sort_by: list[dict[str, str]] | None = None,
|
|
47
|
+
only_requested_fields: bool | None = None,
|
|
48
|
+
) -> list[Comment]:
|
|
49
|
+
"""Get list of comments for an entity.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
entity_id: Parent entity ID (task, project, deal, etc.).
|
|
53
|
+
limit: Number of items per page.
|
|
54
|
+
page_after: Load page starting from this entity.
|
|
55
|
+
page_before: Load page strictly before this entity.
|
|
56
|
+
page_with: Load page containing this entity.
|
|
57
|
+
fields: Additional fields to include.
|
|
58
|
+
sort_by: Sort fields.
|
|
59
|
+
only_requested_fields: Return only requested fields.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
List of comments.
|
|
63
|
+
|
|
64
|
+
Examples:
|
|
65
|
+
>>> # Get all comments for task #123
|
|
66
|
+
>>> comments = await client.comments.list(entity_id=123)
|
|
67
|
+
"""
|
|
68
|
+
path = self._build_path("api", "v3", "todo", str(entity_id), "comments")
|
|
69
|
+
|
|
70
|
+
# Use base method to build params (DRY)
|
|
71
|
+
params = self._build_list_params(
|
|
72
|
+
limit=limit,
|
|
73
|
+
page_after=page_after,
|
|
74
|
+
page_before=page_before,
|
|
75
|
+
page_with=page_with,
|
|
76
|
+
fields=fields,
|
|
77
|
+
sort_by=sort_by,
|
|
78
|
+
only_requested_fields=only_requested_fields,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return await self._get_list(path, Comment, params)
|
|
82
|
+
|
|
83
|
+
async def get(self, comment_id: int) -> Comment:
|
|
84
|
+
"""Get comment by ID.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
comment_id: Comment identifier.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Comment details.
|
|
91
|
+
"""
|
|
92
|
+
path = self._build_path("api", "v3", "comment", str(comment_id))
|
|
93
|
+
response = await self._http.get(path)
|
|
94
|
+
return Comment(**response["data"])
|
|
95
|
+
|
|
96
|
+
async def update(self, comment_id: int, comment_data: dict[str, Any]) -> Comment:
|
|
97
|
+
"""Update comment.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
comment_id: Comment identifier.
|
|
101
|
+
comment_data: Updated comment data.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Updated comment.
|
|
105
|
+
"""
|
|
106
|
+
path = self._build_path("api", "v3", "comment", str(comment_id))
|
|
107
|
+
response = await self._http.post(path, json_data=comment_data)
|
|
108
|
+
return Comment(**response["data"])
|
|
109
|
+
|
|
110
|
+
async def delete(self, comment_id: int) -> None:
|
|
111
|
+
"""Delete comment.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
comment_id: Comment identifier.
|
|
115
|
+
"""
|
|
116
|
+
path = self._build_path("api", "v3", "comment", str(comment_id))
|
|
117
|
+
await self._http.delete(path)
|
|
118
|
+
|
|
119
|
+
async def iterate(
|
|
120
|
+
self,
|
|
121
|
+
entity_id: int,
|
|
122
|
+
limit: int = 100,
|
|
123
|
+
**kwargs: Any,
|
|
124
|
+
) -> AsyncIterator[Comment]:
|
|
125
|
+
"""Iterate over all comments for an entity with automatic pagination.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
entity_id: Parent entity ID (task, project, deal, etc.).
|
|
129
|
+
limit: Number of items per page.
|
|
130
|
+
**kwargs: Additional parameters to pass to list().
|
|
131
|
+
|
|
132
|
+
Yields:
|
|
133
|
+
Comment objects.
|
|
134
|
+
|
|
135
|
+
Examples:
|
|
136
|
+
>>> # Iterate over all comments for task #123
|
|
137
|
+
>>> async for comment in client.comments.iterate(entity_id=123):
|
|
138
|
+
... print(comment.text)
|
|
139
|
+
"""
|
|
140
|
+
comment: Comment
|
|
141
|
+
async for comment in self._iterate_generic( # type: ignore[valid-type]
|
|
142
|
+
"Comment",
|
|
143
|
+
self.list,
|
|
144
|
+
limit,
|
|
145
|
+
entity_id=entity_id,
|
|
146
|
+
**kwargs,
|
|
147
|
+
):
|
|
148
|
+
yield comment
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Contractors resource for Megaplan API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import AsyncIterator
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from megaplan_sdk.constants import ContentType
|
|
9
|
+
from megaplan_sdk.models.contractor import Contractor
|
|
10
|
+
from megaplan_sdk.resources.base import BaseResource
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ContractorsResource(BaseResource):
|
|
14
|
+
"""Resource for working with contractors.
|
|
15
|
+
|
|
16
|
+
Note:
|
|
17
|
+
Contractor comments are not supported by Megaplan API (returns 500 error).
|
|
18
|
+
Use action history or other entities for tracking contractor-related notes.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
async def create(self, contractor_data: dict[str, Any]) -> Contractor:
|
|
22
|
+
"""Create a new contractor.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
contractor_data: Contractor data dictionary.
|
|
26
|
+
contentType: "ContractorCompany" or "ContractorHuman"
|
|
27
|
+
Required fields vary by type.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Created contractor.
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
>>> # Create company
|
|
34
|
+
>>> company_data = {
|
|
35
|
+
... "contentType": "ContractorCompany",
|
|
36
|
+
... "name": "Company Name",
|
|
37
|
+
... "inn": "1234567890",
|
|
38
|
+
... }
|
|
39
|
+
>>> contractor = await client.contractors.create(company_data)
|
|
40
|
+
"""
|
|
41
|
+
path = self._build_path("api", "v3", "contractor")
|
|
42
|
+
response = await self._http.post(path, json_data=contractor_data)
|
|
43
|
+
data = response["data"]
|
|
44
|
+
return self._parse_contractor_response(data)
|
|
45
|
+
|
|
46
|
+
async def list(
|
|
47
|
+
self,
|
|
48
|
+
q: str | None = None,
|
|
49
|
+
category_id: int | None = None,
|
|
50
|
+
limit: int | None = None,
|
|
51
|
+
page_after: dict[str, Any] | None = None,
|
|
52
|
+
page_before: dict[str, Any] | None = None,
|
|
53
|
+
page_with: dict[str, Any] | None = None,
|
|
54
|
+
fields: Any | None = None,
|
|
55
|
+
sort_by: list[dict[str, str]] | None = None,
|
|
56
|
+
only_requested_fields: bool | None = None,
|
|
57
|
+
) -> list[Contractor]:
|
|
58
|
+
"""Get list of contractors.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
q: Search query (name, email, phone, INN).
|
|
62
|
+
category_id: Filter by category ID.
|
|
63
|
+
limit: Number of items per page.
|
|
64
|
+
page_after: Load page starting from this entity.
|
|
65
|
+
page_before: Load page strictly before this entity.
|
|
66
|
+
page_with: Load page containing this entity.
|
|
67
|
+
fields: Additional fields to include.
|
|
68
|
+
sort_by: Sort fields.
|
|
69
|
+
only_requested_fields: Return only requested fields.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
List of contractors.
|
|
73
|
+
|
|
74
|
+
Examples:
|
|
75
|
+
>>> # Search contractors by name
|
|
76
|
+
>>> contractors = await client.contractors.list(q="Company")
|
|
77
|
+
"""
|
|
78
|
+
path = self._build_path("api", "v3", "contractor")
|
|
79
|
+
|
|
80
|
+
# Prepare contractor-specific parameters
|
|
81
|
+
extra_params: dict[str, Any] = {}
|
|
82
|
+
if q:
|
|
83
|
+
extra_params["q"] = q
|
|
84
|
+
if category_id:
|
|
85
|
+
extra_params["category"] = {
|
|
86
|
+
"id": category_id,
|
|
87
|
+
"contentType": ContentType.CONTRACTOR_CATEGORY,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Use base method to build params (DRY)
|
|
91
|
+
params = self._build_list_params(
|
|
92
|
+
limit=limit,
|
|
93
|
+
page_after=page_after,
|
|
94
|
+
page_before=page_before,
|
|
95
|
+
page_with=page_with,
|
|
96
|
+
fields=fields,
|
|
97
|
+
sort_by=sort_by,
|
|
98
|
+
only_requested_fields=only_requested_fields,
|
|
99
|
+
**extra_params,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
response = await self._http.get(path, params=params if params else None)
|
|
103
|
+
data = self._parse_list_response(response)
|
|
104
|
+
|
|
105
|
+
result: list[Contractor] = []
|
|
106
|
+
for item in data:
|
|
107
|
+
if isinstance(item, dict):
|
|
108
|
+
result.append(self._parse_contractor_response(item))
|
|
109
|
+
else:
|
|
110
|
+
result.append(item)
|
|
111
|
+
return result
|
|
112
|
+
|
|
113
|
+
async def get(self, contractor_id: int) -> Contractor:
|
|
114
|
+
"""Get contractor by ID.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
contractor_id: Contractor identifier.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Contractor details.
|
|
121
|
+
"""
|
|
122
|
+
path = self._build_path("api", "v3", "contractor", str(contractor_id))
|
|
123
|
+
response = await self._http.get(path)
|
|
124
|
+
return self._parse_contractor_response(response["data"])
|
|
125
|
+
|
|
126
|
+
async def update(self, contractor_id: int, contractor_data: dict[str, Any]) -> Contractor:
|
|
127
|
+
"""Update contractor.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
contractor_id: Contractor identifier.
|
|
131
|
+
contractor_data: Updated contractor data.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Updated contractor.
|
|
135
|
+
"""
|
|
136
|
+
path = self._build_path("api", "v3", "contractor", str(contractor_id))
|
|
137
|
+
response = await self._http.post(path, json_data=contractor_data)
|
|
138
|
+
return self._parse_contractor_response(response["data"])
|
|
139
|
+
|
|
140
|
+
async def delete(self, contractor_id: int) -> None:
|
|
141
|
+
"""Delete contractor.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
contractor_id: Contractor identifier.
|
|
145
|
+
"""
|
|
146
|
+
await self._delete_entity("contractor", contractor_id)
|
|
147
|
+
|
|
148
|
+
async def iterate(
|
|
149
|
+
self,
|
|
150
|
+
limit: int = 100,
|
|
151
|
+
**kwargs: Any,
|
|
152
|
+
) -> AsyncIterator[Contractor]:
|
|
153
|
+
"""Iterate over all contractors with automatic pagination.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
limit: Number of items per page.
|
|
157
|
+
**kwargs: Additional parameters to pass to list().
|
|
158
|
+
|
|
159
|
+
Yields:
|
|
160
|
+
Contractor objects.
|
|
161
|
+
|
|
162
|
+
Examples:
|
|
163
|
+
>>> async for contractor in client.contractors.iterate():
|
|
164
|
+
... print(contractor.name)
|
|
165
|
+
"""
|
|
166
|
+
contractor: Contractor
|
|
167
|
+
async for contractor in self._iterate_generic( # type: ignore[valid-type]
|
|
168
|
+
ContentType.CONTRACTOR,
|
|
169
|
+
self.list,
|
|
170
|
+
limit,
|
|
171
|
+
**kwargs,
|
|
172
|
+
):
|
|
173
|
+
yield contractor
|