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.
@@ -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