mindbridge-api-python-client 1.4.8__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.
- mindbridge_api_python_client-1.4.8.dist-info/LICENSE.txt +11 -0
- mindbridge_api_python_client-1.4.8.dist-info/METADATA +64 -0
- mindbridge_api_python_client-1.4.8.dist-info/RECORD +43 -0
- mindbridge_api_python_client-1.4.8.dist-info/WHEEL +4 -0
- mindbridgeapi/__init__.py +69 -0
- mindbridgeapi/accounting_period.py +34 -0
- mindbridgeapi/analyses.py +383 -0
- mindbridgeapi/analysis_item.py +167 -0
- mindbridgeapi/analysis_period.py +68 -0
- mindbridgeapi/analysis_source_item.py +198 -0
- mindbridgeapi/analysis_source_type_item.py +55 -0
- mindbridgeapi/analysis_source_types.py +36 -0
- mindbridgeapi/analysis_sources.py +132 -0
- mindbridgeapi/analysis_type_item.py +45 -0
- mindbridgeapi/analysis_types.py +62 -0
- mindbridgeapi/async_results.py +194 -0
- mindbridgeapi/base_set.py +175 -0
- mindbridgeapi/chunked_file_item.py +37 -0
- mindbridgeapi/chunked_file_part_item.py +25 -0
- mindbridgeapi/chunked_files.py +70 -0
- mindbridgeapi/column_mapping.py +21 -0
- mindbridgeapi/common_validators.py +71 -0
- mindbridgeapi/data_tables.py +206 -0
- mindbridgeapi/engagement_item.py +100 -0
- mindbridgeapi/engagements.py +93 -0
- mindbridgeapi/enumerations/analysis_source_type.py +142 -0
- mindbridgeapi/enumerations/analysis_type.py +36 -0
- mindbridgeapi/enumerations/deprecated_enum.py +69 -0
- mindbridgeapi/enumerations/system_library.py +32 -0
- mindbridgeapi/exceptions.py +92 -0
- mindbridgeapi/file_manager.py +212 -0
- mindbridgeapi/file_manager_item.py +107 -0
- mindbridgeapi/generated_pydantic_model/model.py +7035 -0
- mindbridgeapi/libraries.py +54 -0
- mindbridgeapi/library_item.py +44 -0
- mindbridgeapi/organization_item.py +61 -0
- mindbridgeapi/organizations.py +82 -0
- mindbridgeapi/server.py +202 -0
- mindbridgeapi/task_item.py +47 -0
- mindbridgeapi/tasks.py +150 -0
- mindbridgeapi/transaction_id_selection.py +31 -0
- mindbridgeapi/users.py +42 -0
- mindbridgeapi/virtual_column.py +104 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright MindBridge Analytics Inc. all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# This material is confidential and may not be copied, distributed,
|
|
5
|
+
# reversed engineered, decompiled or otherwise disseminated without
|
|
6
|
+
# the prior written consent of MindBridge Analytics Inc.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Any
|
|
11
|
+
import warnings
|
|
12
|
+
from mindbridgeapi.generated_pydantic_model.model import ApiUserInfo, ApiUserInfoRead
|
|
13
|
+
from mindbridgeapi.generated_pydantic_model.model import ApiUserRead as UserItem
|
|
14
|
+
from mindbridgeapi.generated_pydantic_model.model import Role
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _warning_if_extra_fields(self: Any) -> Any:
|
|
20
|
+
"""Emits a warning if the model had any extra fields
|
|
21
|
+
|
|
22
|
+
This is used to create a warning if and only if the model had any extra fields, but
|
|
23
|
+
still allow the model to be used. For example:
|
|
24
|
+
```py
|
|
25
|
+
x = mbapi.AccountingPeriod(badkey="ksodhjksdl")
|
|
26
|
+
AccountingPeriod had extra fields, specifically:
|
|
27
|
+
badkey: ksodhjksdl
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Any: The original object
|
|
32
|
+
"""
|
|
33
|
+
if self.model_extra is None or len(self.model_extra) == 0:
|
|
34
|
+
return self
|
|
35
|
+
|
|
36
|
+
log_lines = [f"{type(self).__name__} had extra fields, specifically:"]
|
|
37
|
+
for key, value in self.model_extra.items():
|
|
38
|
+
log_lines.append(f"{key.rjust(20)}: {value}")
|
|
39
|
+
|
|
40
|
+
warn_message = "\n".join(log_lines)
|
|
41
|
+
warnings.warn(warn_message, stacklevel=2)
|
|
42
|
+
|
|
43
|
+
return self
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _convert_userinfo_to_useritem(v: Any) -> Any:
|
|
47
|
+
"""Converts from UserInfo to UserItem
|
|
48
|
+
|
|
49
|
+
If it's a ApiUserInfo or ApiUserInfoRead it changes to UserItem, with just the
|
|
50
|
+
relevant fields set. It also does some extra conversion if it seems to be an API
|
|
51
|
+
user.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
v (Any): The original data
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Any: UserItem if possible, the original data otherwise
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
if not isinstance(v, (ApiUserInfo, ApiUserInfoRead)):
|
|
61
|
+
return v
|
|
62
|
+
|
|
63
|
+
prefix = "(API) "
|
|
64
|
+
if v.user_name is None or not v.user_name.startswith(prefix):
|
|
65
|
+
return UserItem(id=v.user_id, first_name=v.user_name) # type: ignore[call-arg]
|
|
66
|
+
|
|
67
|
+
prefix_len = len(prefix)
|
|
68
|
+
first_name = v.user_name[prefix_len:]
|
|
69
|
+
return UserItem( # type: ignore[call-arg]
|
|
70
|
+
id=v.user_id, first_name=first_name, last_name="", role=Role.ROLE_ADMIN
|
|
71
|
+
)
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright MindBridge Analytics Inc. all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# This material is confidential and may not be copied, distributed,
|
|
5
|
+
# reversed engineered, decompiled or otherwise disseminated without
|
|
6
|
+
# the prior written consent of MindBridge Analytics Inc.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from functools import cached_property
|
|
11
|
+
import logging
|
|
12
|
+
from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional
|
|
13
|
+
import warnings
|
|
14
|
+
from mindbridgeapi.async_results import AsyncResults
|
|
15
|
+
from mindbridgeapi.base_set import BaseSet
|
|
16
|
+
from mindbridgeapi.exceptions import ItemError, ItemNotFoundError, ParameterError
|
|
17
|
+
from mindbridgeapi.generated_pydantic_model.model import (
|
|
18
|
+
ApiAsyncResult,
|
|
19
|
+
ApiDataTableExportRequest,
|
|
20
|
+
ApiDataTableQuerySortOrder,
|
|
21
|
+
ApiDataTableRead,
|
|
22
|
+
Direction,
|
|
23
|
+
MindBridgeQueryTerm,
|
|
24
|
+
)
|
|
25
|
+
from mindbridgeapi.generated_pydantic_model.model import Status2 as AsyncResultStatus
|
|
26
|
+
from mindbridgeapi.generated_pydantic_model.model import Type1 as AsyncResultType
|
|
27
|
+
from mindbridgeapi.generated_pydantic_model.model import Type4 as DataTableColumnType
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class DataTables(BaseSet):
|
|
37
|
+
def __post_init__(self) -> None:
|
|
38
|
+
self.async_result_set = AsyncResults(server=self.server)
|
|
39
|
+
|
|
40
|
+
@cached_property
|
|
41
|
+
def base_url(self) -> str:
|
|
42
|
+
return f"{self.server.base_url}/data-tables"
|
|
43
|
+
|
|
44
|
+
def get_by_id(self, id: str) -> ApiDataTableRead:
|
|
45
|
+
url = f"{self.base_url}/{id}"
|
|
46
|
+
resp_dict = super()._get_by_id(url=url)
|
|
47
|
+
|
|
48
|
+
return ApiDataTableRead.model_validate(resp_dict)
|
|
49
|
+
|
|
50
|
+
def get(
|
|
51
|
+
self, json: Optional[Dict[str, Any]] = None
|
|
52
|
+
) -> Generator[ApiDataTableRead, None, None]:
|
|
53
|
+
logger.info("Starting Query (get)")
|
|
54
|
+
|
|
55
|
+
if json is None:
|
|
56
|
+
json = {}
|
|
57
|
+
|
|
58
|
+
json_str = MindBridgeQueryTerm.model_construct(root=json).model_dump_json()
|
|
59
|
+
logger.info(f"Query (get) is: {json_str}")
|
|
60
|
+
|
|
61
|
+
if "analysisId" not in json_str:
|
|
62
|
+
raise ValueError( # noqa:TRY003
|
|
63
|
+
"At least one valid analysisId term must be provided when querying this"
|
|
64
|
+
" entity."
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
url = f"{self.base_url}/query"
|
|
68
|
+
for resp_dict in super()._get(url=url, json=json):
|
|
69
|
+
yield ApiDataTableRead.model_validate(resp_dict)
|
|
70
|
+
|
|
71
|
+
def export(
|
|
72
|
+
self,
|
|
73
|
+
input_item: ApiDataTableRead,
|
|
74
|
+
fields: Optional[List[str]] = None,
|
|
75
|
+
query: Optional[Dict[str, Any]] = None,
|
|
76
|
+
limit: Optional[int] = None,
|
|
77
|
+
sort_direction: Optional[Direction] = None,
|
|
78
|
+
sort_field: Optional[str] = None,
|
|
79
|
+
) -> ApiAsyncResult:
|
|
80
|
+
if getattr(input_item, "id", None) is None:
|
|
81
|
+
raise ItemNotFoundError
|
|
82
|
+
|
|
83
|
+
if input_item.columns is None:
|
|
84
|
+
raise ItemError(f"{input_item.columns=}")
|
|
85
|
+
|
|
86
|
+
if fields is None:
|
|
87
|
+
fields = []
|
|
88
|
+
|
|
89
|
+
if len(fields) == 0:
|
|
90
|
+
"""
|
|
91
|
+
"KEYWORD_SEARCH columns can't be included in data table exports. Attempting
|
|
92
|
+
to select them as part of fields will cause the export request to
|
|
93
|
+
fail.". Similarly fields that are filter only can't be included as
|
|
94
|
+
fields.
|
|
95
|
+
"""
|
|
96
|
+
fields = [
|
|
97
|
+
x.field
|
|
98
|
+
for x in input_item.columns
|
|
99
|
+
if x.type != DataTableColumnType.KEYWORD_SEARCH
|
|
100
|
+
and x.field is not None
|
|
101
|
+
and not x.filter_only
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
if query is None:
|
|
105
|
+
query = {}
|
|
106
|
+
|
|
107
|
+
if sort_direction is None:
|
|
108
|
+
sort_direction = Direction.ASC
|
|
109
|
+
|
|
110
|
+
if not isinstance(sort_direction, Direction):
|
|
111
|
+
try:
|
|
112
|
+
sort_direction = Direction(sort_direction)
|
|
113
|
+
except ValueError as err:
|
|
114
|
+
raise ParameterError(
|
|
115
|
+
parameter_name="sort_direction",
|
|
116
|
+
details="Not a valid Direction",
|
|
117
|
+
) from err
|
|
118
|
+
|
|
119
|
+
if sort_field is not None and not isinstance(sort_field, str):
|
|
120
|
+
raise ParameterError(
|
|
121
|
+
parameter_name="sort_field", details="Not provided as str"
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
if sort_field is None and input_item.type == "flows_compact":
|
|
125
|
+
sort_field = "flow_id"
|
|
126
|
+
elif sort_field is None and input_item.logical_name == "gl_journal_lines":
|
|
127
|
+
sort_field = "rowid"
|
|
128
|
+
elif sort_field is None and input_item.logical_name == "gl_journal_tx":
|
|
129
|
+
sort_field = "txid"
|
|
130
|
+
elif sort_field is None or sort_field == "":
|
|
131
|
+
# Don't send a sort
|
|
132
|
+
sort_field = None
|
|
133
|
+
sort_direction = None
|
|
134
|
+
|
|
135
|
+
url = f"{self.base_url}/{input_item.id}/export"
|
|
136
|
+
data_table_export_request = ApiDataTableExportRequest( # type: ignore[call-arg]
|
|
137
|
+
fields=fields,
|
|
138
|
+
query=MindBridgeQueryTerm.model_construct(root=query),
|
|
139
|
+
limit=limit,
|
|
140
|
+
sort=ApiDataTableQuerySortOrder(direction=sort_direction, field=sort_field),
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
json = data_table_export_request.model_dump(
|
|
144
|
+
mode="json", by_alias=True, exclude_none=True
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
resp_dict = super()._create(url=url, json=json)
|
|
148
|
+
|
|
149
|
+
return ApiAsyncResult.model_validate(resp_dict)
|
|
150
|
+
|
|
151
|
+
def wait_for_export(
|
|
152
|
+
self,
|
|
153
|
+
async_result: ApiAsyncResult,
|
|
154
|
+
check_interval_seconds: int = -873, # Depreciated
|
|
155
|
+
max_wait_minutes: int = (24 * 60),
|
|
156
|
+
) -> None:
|
|
157
|
+
"""Wait for the async result for the data table export to complete
|
|
158
|
+
|
|
159
|
+
Waits, at most the minutes specified, for the async result to be COMPLETE and
|
|
160
|
+
raises and error if any error
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
async_result (AsyncResultItem): Async result to check
|
|
164
|
+
max_wait_minutes (int): Maximum minutes to wait (default: `24 * 60`)
|
|
165
|
+
|
|
166
|
+
Raises:
|
|
167
|
+
ValueError: If not a DATA_TABLE_EXPORT
|
|
168
|
+
"""
|
|
169
|
+
if check_interval_seconds != -873:
|
|
170
|
+
warnings.warn(
|
|
171
|
+
"check_interval_seconds was provided to wait_for_export as "
|
|
172
|
+
f"{check_interval_seconds}. This will not be referenced as now the "
|
|
173
|
+
"check interval will be exponentially increasing to a max interval",
|
|
174
|
+
category=DeprecationWarning,
|
|
175
|
+
stacklevel=2,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
del check_interval_seconds
|
|
179
|
+
|
|
180
|
+
if async_result.type != AsyncResultType.DATA_TABLE_EXPORT:
|
|
181
|
+
raise ItemError(f"{async_result.type=}")
|
|
182
|
+
|
|
183
|
+
self.async_result_set._wait_for_async_result(
|
|
184
|
+
async_result=async_result,
|
|
185
|
+
max_wait_minutes=max_wait_minutes,
|
|
186
|
+
init_interval_sec=10,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
def download(
|
|
190
|
+
self, async_result: ApiAsyncResult, output_file_path: "Path"
|
|
191
|
+
) -> "Path":
|
|
192
|
+
# Get the current status
|
|
193
|
+
resp_dict = super()._get_by_id(
|
|
194
|
+
url=f"{self.server.base_url}/async-results/{async_result.id}"
|
|
195
|
+
)
|
|
196
|
+
async_result = ApiAsyncResult.model_validate(resp_dict)
|
|
197
|
+
|
|
198
|
+
url = f"{self.server.base_url}/file-results/{async_result.entity_id}/export"
|
|
199
|
+
|
|
200
|
+
if async_result.type != AsyncResultType.DATA_TABLE_EXPORT:
|
|
201
|
+
raise ItemError(f"{async_result.type=}")
|
|
202
|
+
|
|
203
|
+
if async_result.status != AsyncResultStatus.COMPLETE:
|
|
204
|
+
raise ItemError(f"{async_result.status=}")
|
|
205
|
+
|
|
206
|
+
return super()._download(url=url, output_path=output_file_path)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright MindBridge Analytics Inc. all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# This material is confidential and may not be copied, distributed,
|
|
5
|
+
# reversed engineered, decompiled or otherwise disseminated without
|
|
6
|
+
# the prior written consent of MindBridge Analytics Inc.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
from datetime import date
|
|
10
|
+
from typing import Any, Dict, Generator, Optional, Type, Union
|
|
11
|
+
from pydantic import ConfigDict, Field, field_validator, model_validator
|
|
12
|
+
from pydantic_core import PydanticUndefined
|
|
13
|
+
from mindbridgeapi.accounting_period import AccountingPeriod
|
|
14
|
+
from mindbridgeapi.analysis_item import AnalysisItem
|
|
15
|
+
from mindbridgeapi.common_validators import (
|
|
16
|
+
_convert_userinfo_to_useritem,
|
|
17
|
+
_warning_if_extra_fields,
|
|
18
|
+
)
|
|
19
|
+
from mindbridgeapi.file_manager_item import FileManagerItem
|
|
20
|
+
from mindbridgeapi.generated_pydantic_model.model import (
|
|
21
|
+
ApiEngagementCreateOnly,
|
|
22
|
+
ApiEngagementRead,
|
|
23
|
+
ApiEngagementUpdate,
|
|
24
|
+
)
|
|
25
|
+
from mindbridgeapi.library_item import LibraryItem
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _empty_analyses() -> Generator[AnalysisItem, None, None]:
|
|
29
|
+
"""Empty generator function
|
|
30
|
+
|
|
31
|
+
This returns an empty generator function, it's use is to ensure analyses is not None
|
|
32
|
+
for the EngagementItem class
|
|
33
|
+
"""
|
|
34
|
+
yield from ()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _empty_file_manager_items() -> Generator[FileManagerItem, None, None]:
|
|
38
|
+
"""Empty generator function
|
|
39
|
+
|
|
40
|
+
This returns an empty generator function, it's use is to ensure file_manager_items
|
|
41
|
+
is not None for the EngagementItem class
|
|
42
|
+
"""
|
|
43
|
+
yield from ()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class EngagementItem(ApiEngagementRead):
|
|
47
|
+
file_manager_items: Generator[FileManagerItem, None, None] = Field(
|
|
48
|
+
default_factory=_empty_file_manager_items, exclude=True
|
|
49
|
+
)
|
|
50
|
+
analyses: Generator[AnalysisItem, None, None] = Field(
|
|
51
|
+
default_factory=_empty_analyses, exclude=True
|
|
52
|
+
)
|
|
53
|
+
settings_based_on_engagement_id: Optional[str] = Field().merge_field_infos(
|
|
54
|
+
ApiEngagementCreateOnly.model_fields["settings_based_on_engagement_id"]
|
|
55
|
+
)
|
|
56
|
+
accounting_package: str = Field().merge_field_infos(
|
|
57
|
+
ApiEngagementRead.model_fields["accounting_package"], default="Other"
|
|
58
|
+
)
|
|
59
|
+
accounting_period: AccountingPeriod = Field().merge_field_infos(
|
|
60
|
+
ApiEngagementRead.model_fields["accounting_period"],
|
|
61
|
+
default=PydanticUndefined,
|
|
62
|
+
default_factory=lambda: AccountingPeriod(),
|
|
63
|
+
)
|
|
64
|
+
audit_period_end_date: date = Field().merge_field_infos(
|
|
65
|
+
ApiEngagementRead.model_fields["audit_period_end_date"],
|
|
66
|
+
default=date(date.today().year, 12, 31),
|
|
67
|
+
)
|
|
68
|
+
industry: str = Field().merge_field_infos(
|
|
69
|
+
ApiEngagementRead.model_fields["industry"], default="Other"
|
|
70
|
+
)
|
|
71
|
+
library_id: str = Field().merge_field_infos(
|
|
72
|
+
ApiEngagementRead.model_fields["library_id"],
|
|
73
|
+
default=LibraryItem.MINDBRIDGE_FOR_PROFIT,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
model_config = ConfigDict(
|
|
77
|
+
extra="allow",
|
|
78
|
+
validate_assignment=True,
|
|
79
|
+
validate_default=True,
|
|
80
|
+
validate_return=True,
|
|
81
|
+
)
|
|
82
|
+
_a = model_validator(mode="after")(_warning_if_extra_fields)
|
|
83
|
+
_b = field_validator("*")(_convert_userinfo_to_useritem)
|
|
84
|
+
|
|
85
|
+
def _get_post_json(
|
|
86
|
+
self, out_class: Type[Union[ApiEngagementCreateOnly, ApiEngagementUpdate]]
|
|
87
|
+
) -> Dict[str, Any]:
|
|
88
|
+
in_class_dict = self.model_dump()
|
|
89
|
+
out_class_object = out_class.model_validate(in_class_dict)
|
|
90
|
+
return out_class_object.model_dump(
|
|
91
|
+
mode="json", by_alias=True, exclude_none=True
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def create_json(self) -> Dict[str, Any]:
|
|
96
|
+
return self._get_post_json(out_class=ApiEngagementCreateOnly)
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def update_json(self) -> Dict[str, Any]:
|
|
100
|
+
return self._get_post_json(out_class=ApiEngagementUpdate)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright MindBridge Analytics Inc. all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# This material is confidential and may not be copied, distributed,
|
|
5
|
+
# reversed engineered, decompiled or otherwise disseminated without
|
|
6
|
+
# the prior written consent of MindBridge Analytics Inc.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from functools import cached_property
|
|
11
|
+
from typing import Any, Dict, Generator, Optional
|
|
12
|
+
from mindbridgeapi.analyses import Analyses
|
|
13
|
+
from mindbridgeapi.base_set import BaseSet
|
|
14
|
+
from mindbridgeapi.engagement_item import EngagementItem
|
|
15
|
+
from mindbridgeapi.exceptions import ItemAlreadyExistsError, ItemNotFoundError
|
|
16
|
+
from mindbridgeapi.file_manager import FileManager
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class Engagements(BaseSet):
|
|
21
|
+
@cached_property
|
|
22
|
+
def base_url(self) -> str:
|
|
23
|
+
return f"{self.server.base_url}/engagements"
|
|
24
|
+
|
|
25
|
+
def create(self, item: EngagementItem) -> EngagementItem:
|
|
26
|
+
if getattr(item, "id", None) is not None and item.id is not None:
|
|
27
|
+
raise ItemAlreadyExistsError(item.id)
|
|
28
|
+
|
|
29
|
+
url = self.base_url
|
|
30
|
+
resp_dict = super()._create(url=url, json=item.create_json)
|
|
31
|
+
engagement = EngagementItem.model_validate(resp_dict)
|
|
32
|
+
self.restart_file_manager_items(engagement)
|
|
33
|
+
self.restart_analyses(engagement)
|
|
34
|
+
|
|
35
|
+
return engagement
|
|
36
|
+
|
|
37
|
+
def get_by_id(self, id: str) -> EngagementItem:
|
|
38
|
+
url = f"{self.base_url}/{id}"
|
|
39
|
+
resp_dict = super()._get_by_id(url=url)
|
|
40
|
+
engagement = EngagementItem.model_validate(resp_dict)
|
|
41
|
+
self.restart_file_manager_items(engagement)
|
|
42
|
+
self.restart_analyses(engagement)
|
|
43
|
+
|
|
44
|
+
return engagement
|
|
45
|
+
|
|
46
|
+
def update(self, item: EngagementItem) -> EngagementItem:
|
|
47
|
+
if getattr(item, "id", None) is None:
|
|
48
|
+
raise ItemNotFoundError
|
|
49
|
+
|
|
50
|
+
url = f"{self.base_url}/{item.id}"
|
|
51
|
+
resp_dict = super()._update(url=url, json=item.update_json)
|
|
52
|
+
|
|
53
|
+
engagement = EngagementItem.model_validate(resp_dict)
|
|
54
|
+
self.restart_file_manager_items(engagement)
|
|
55
|
+
self.restart_analyses(engagement)
|
|
56
|
+
|
|
57
|
+
return engagement
|
|
58
|
+
|
|
59
|
+
def get(
|
|
60
|
+
self, json: Optional[Dict[str, Any]] = None
|
|
61
|
+
) -> Generator[EngagementItem, None, None]:
|
|
62
|
+
if json is None:
|
|
63
|
+
json = {}
|
|
64
|
+
|
|
65
|
+
url = f"{self.base_url}/query"
|
|
66
|
+
for resp_dict in super()._get(url=url, json=json):
|
|
67
|
+
engagement = EngagementItem.model_validate(resp_dict)
|
|
68
|
+
self.restart_file_manager_items(engagement)
|
|
69
|
+
self.restart_analyses(engagement)
|
|
70
|
+
yield engagement
|
|
71
|
+
|
|
72
|
+
def restart_file_manager_items(self, engagement_item: EngagementItem) -> None:
|
|
73
|
+
if getattr(engagement_item, "id", None) is None:
|
|
74
|
+
raise ItemNotFoundError
|
|
75
|
+
|
|
76
|
+
engagement_item.file_manager_items = FileManager(server=self.server).get(
|
|
77
|
+
json={"engagementId": {"$eq": engagement_item.id}}
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def restart_analyses(self, engagement_item: EngagementItem) -> None:
|
|
81
|
+
if getattr(engagement_item, "id", None) is None:
|
|
82
|
+
raise ItemNotFoundError
|
|
83
|
+
|
|
84
|
+
engagement_item.analyses = Analyses(server=self.server).get(
|
|
85
|
+
json={"engagementId": {"$eq": engagement_item.id}}
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def delete(self, item: EngagementItem) -> None:
|
|
89
|
+
if getattr(item, "id", None) is None:
|
|
90
|
+
raise ItemNotFoundError
|
|
91
|
+
|
|
92
|
+
url = f"{self.base_url}/{item.id}"
|
|
93
|
+
super()._delete(url=url)
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright MindBridge Analytics Inc. all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# This material is confidential and may not be copied, distributed,
|
|
5
|
+
# reversed engineered, decompiled or otherwise disseminated without
|
|
6
|
+
# the prior written consent of MindBridge Analytics Inc.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
from enum import unique
|
|
10
|
+
from mindbridgeapi.enumerations.deprecated_enum import DeprecatedStrEnum
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@unique
|
|
14
|
+
class AnalysisSourceType(DeprecatedStrEnum):
|
|
15
|
+
"""This is Deprecated, use mbapi.AnalysisSourceTypeItem instead to get these IDs"""
|
|
16
|
+
|
|
17
|
+
# General Ledger (for-profit) - 4b8360d00000000000000000
|
|
18
|
+
GENERAL_LEDGER_JOURNAL = (
|
|
19
|
+
"4b8361d00000000000000000",
|
|
20
|
+
"Use mbapi.AnalysisSourceTypeItem.GENERAL_LEDGER_JOURNAL instead",
|
|
21
|
+
)
|
|
22
|
+
OPENING_BALANCE = (
|
|
23
|
+
"4b8361d00000000000000002",
|
|
24
|
+
"Use mbapi.AnalysisSourceTypeItem.OPENING_BALANCE instead",
|
|
25
|
+
)
|
|
26
|
+
CLOSING_BALANCE = (
|
|
27
|
+
"4b8361d00000000000000003",
|
|
28
|
+
"Use mbapi.AnalysisSourceTypeItem.CLOSING_BALANCE instead",
|
|
29
|
+
)
|
|
30
|
+
CHART_OF_ACCOUNTS = (
|
|
31
|
+
"4b8361d00000000000000004",
|
|
32
|
+
"Use mbapi.AnalysisSourceTypeItem.CHART_OF_ACCOUNTS instead",
|
|
33
|
+
)
|
|
34
|
+
ADJUSTING_ENTRIES = (
|
|
35
|
+
"4b8361d00000000000000016",
|
|
36
|
+
"Use mbapi.AnalysisSourceTypeItem.ADJUSTING_ENTRIES instead",
|
|
37
|
+
)
|
|
38
|
+
RECLASSIFICATION_ENTRIES = (
|
|
39
|
+
"4b8361d00000000000000017",
|
|
40
|
+
"Use mbapi.AnalysisSourceTypeItem.RECLASSIFICATION_ENTRIES instead",
|
|
41
|
+
)
|
|
42
|
+
ELIMINATION_ENTRIES = (
|
|
43
|
+
"4b8361d00000000000000018",
|
|
44
|
+
"Use mbapi.AnalysisSourceTypeItem.ELIMINATION_ENTRIES instead",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# General Ledger (not-for-profit) - 4b8360d00000000000000001
|
|
48
|
+
NOT_FOR_PROFIT_GENERAL_LEDGER_JOURNAL = (
|
|
49
|
+
"4b8361d00000000000000051",
|
|
50
|
+
"Use mbapi.AnalysisSourceTypeItem.NOT_FOR_PROFIT_GENERAL_LEDGER_JOURNAL "
|
|
51
|
+
"instead",
|
|
52
|
+
)
|
|
53
|
+
NOT_FOR_PROFIT_OPENING_BALANCE = (
|
|
54
|
+
"4b8361d00000000000000057",
|
|
55
|
+
"Use mbapi.AnalysisSourceTypeItem.NOT_FOR_PROFIT_OPENING_BALANCE instead",
|
|
56
|
+
)
|
|
57
|
+
NOT_FOR_PROFIT_CLOSING_BALANCE = (
|
|
58
|
+
"4b8361d00000000000000058",
|
|
59
|
+
"Use mbapi.AnalysisSourceTypeItem.NOT_FOR_PROFIT_CLOSING_BALANCE instead",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# General Ledger (not-for-profit with funds) - 4b8360d00000000000000002
|
|
63
|
+
GENERAL_LEDGER_JOURNAL_FUND = (
|
|
64
|
+
"4b8361d00000000000000001",
|
|
65
|
+
"Use mbapi.AnalysisSourceTypeItem.GENERAL_LEDGER_JOURNAL_FUND instead",
|
|
66
|
+
)
|
|
67
|
+
FUND_OPENING_BALANCE = (
|
|
68
|
+
"4b8361d00000000000000059",
|
|
69
|
+
"Use mbapi.AnalysisSourceTypeItem.FUND_OPENING_BALANCE instead",
|
|
70
|
+
)
|
|
71
|
+
FUND_CLOSING_BALANCE = (
|
|
72
|
+
"4b8361d0000000000000005a",
|
|
73
|
+
"Use mbapi.AnalysisSourceTypeItem.FUND_CLOSING_BALANCE instead",
|
|
74
|
+
)
|
|
75
|
+
FUND_CHART_OF_ACCOUNTS = (
|
|
76
|
+
"4b8361d00000000000000005",
|
|
77
|
+
"Use mbapi.AnalysisSourceTypeItem.FUND_CHART_OF_ACCOUNTS instead",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Accounts Payable - 4b8360d00000000000000003
|
|
81
|
+
ACCOUNTS_PAYABLE_DETAIL = (
|
|
82
|
+
"4b8361d00000000000000006",
|
|
83
|
+
"Use mbapi.AnalysisSourceTypeItem.ACCOUNTS_PAYABLE_DETAIL instead",
|
|
84
|
+
)
|
|
85
|
+
CLOSING_PAYABLES_LIST = (
|
|
86
|
+
"4b8361d00000000000000010",
|
|
87
|
+
"Use mbapi.AnalysisSourceTypeItem.CLOSING_PAYABLES_LIST instead",
|
|
88
|
+
)
|
|
89
|
+
VENDOR_OPENING_BALANCES = (
|
|
90
|
+
"4b8361d00000000000000007",
|
|
91
|
+
"Use mbapi.AnalysisSourceTypeItem.VENDOR_OPENING_BALANCES instead",
|
|
92
|
+
)
|
|
93
|
+
VENDOR_LIST = (
|
|
94
|
+
"4b8361d00000000000000008",
|
|
95
|
+
"Use mbapi.AnalysisSourceTypeItem.VENDOR_LIST instead",
|
|
96
|
+
)
|
|
97
|
+
OPEN_PAYABLES_LIST = (
|
|
98
|
+
"4b8361d00000000000000009",
|
|
99
|
+
"Use mbapi.AnalysisSourceTypeItem.OPEN_PAYABLES_LIST instead",
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Accounts Receivable - 4b8360d00000000000000004
|
|
103
|
+
ACCOUNTS_RECEIVABLE_DETAIL = (
|
|
104
|
+
"4b8361d00000000000000011",
|
|
105
|
+
"Use mbapi.AnalysisSourceTypeItem.ACCOUNTS_RECEIVABLE_DETAIL instead",
|
|
106
|
+
)
|
|
107
|
+
CLOSING_RECEIVABLES_LIST = (
|
|
108
|
+
"4b8361d00000000000000014",
|
|
109
|
+
"Use mbapi.AnalysisSourceTypeItem.CLOSING_RECEIVABLES_LIST instead",
|
|
110
|
+
)
|
|
111
|
+
CUSTOMER_OPENING_BALANCES = (
|
|
112
|
+
"4b8361d00000000000000012",
|
|
113
|
+
"Use mbapi.AnalysisSourceTypeItem.CUSTOMER_OPENING_BALANCES instead",
|
|
114
|
+
)
|
|
115
|
+
CUSTOMER_LIST = (
|
|
116
|
+
"4b8361d00000000000000039",
|
|
117
|
+
"Use mbapi.AnalysisSourceTypeItem.CUSTOMER_LIST instead",
|
|
118
|
+
)
|
|
119
|
+
OPEN_RECEIVABLES_LIST = (
|
|
120
|
+
"4b8361d00000000000000013",
|
|
121
|
+
"Use mbapi.AnalysisSourceTypeItem.OPEN_RECEIVABLES_LIST instead",
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Additional Analysis Data
|
|
125
|
+
ADDITIONAL_ANALYSIS_DATA = (
|
|
126
|
+
"4b8361d00000000000000015",
|
|
127
|
+
"Use mbapi.AnalysisSourceTypeItem.ADDITIONAL_ANALYSIS_DATA instead",
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Other
|
|
131
|
+
FUND_ADJUSTING_ENTRIES = (
|
|
132
|
+
"4b8361d00000000000000019",
|
|
133
|
+
"Use mbapi.AnalysisSourceTypeItem.FUND_ADJUSTING_ENTRIES instead",
|
|
134
|
+
)
|
|
135
|
+
FUND_ELIMINATION_ENTRIES = (
|
|
136
|
+
"4b8361d00000000000000020",
|
|
137
|
+
"Use mbapi.AnalysisSourceTypeItem.FUND_ELIMINATION_ENTRIES instead",
|
|
138
|
+
)
|
|
139
|
+
FUND_RECLASSIFICATION_ENTRIES = (
|
|
140
|
+
"4b8361d00000000000000021",
|
|
141
|
+
"Use mbapi.AnalysisSourceTypeItem.FUND_RECLASSIFICATION_ENTRIES instead",
|
|
142
|
+
)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright MindBridge Analytics Inc. all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# This material is confidential and may not be copied, distributed,
|
|
5
|
+
# reversed engineered, decompiled or otherwise disseminated without
|
|
6
|
+
# the prior written consent of MindBridge Analytics Inc.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
from enum import unique
|
|
10
|
+
from mindbridgeapi.enumerations.deprecated_enum import DeprecatedStrEnum
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@unique
|
|
14
|
+
class AnalysisType(DeprecatedStrEnum):
|
|
15
|
+
"""This is Deprecated, use mbapi.AnalysisTypeItem instead to get these IDs"""
|
|
16
|
+
|
|
17
|
+
GENERAL_LEDGER = (
|
|
18
|
+
"4b8360d00000000000000000",
|
|
19
|
+
"Use mbapi.AnalysisTypeItem.GENERAL_LEDGER instead",
|
|
20
|
+
)
|
|
21
|
+
NOT_FOR_PROFIT_GENERAL_LEDGER = (
|
|
22
|
+
"4b8360d00000000000000001",
|
|
23
|
+
"Use mbapi.AnalysisTypeItem.NOT_FOR_PROFIT_GENERAL_LEDGER instead",
|
|
24
|
+
)
|
|
25
|
+
NOT_FOR_PROFIT_GENERAL_LEDGER_FUND = (
|
|
26
|
+
"4b8360d00000000000000002",
|
|
27
|
+
"Use mbapi.AnalysisTypeItem.NOT_FOR_PROFIT_GENERAL_LEDGER_FUND instead",
|
|
28
|
+
)
|
|
29
|
+
ACCOUNTS_PAYABLE_V2 = (
|
|
30
|
+
"4b8360d00000000000000003",
|
|
31
|
+
"Use mbapi.AnalysisTypeItem.ACCOUNTS_PAYABLE_V2 instead",
|
|
32
|
+
)
|
|
33
|
+
ACCOUNTS_RECEIVABLE_V2 = (
|
|
34
|
+
"4b8360d00000000000000004",
|
|
35
|
+
"Use mbapi.AnalysisTypeItem.ACCOUNTS_RECEIVABLE_V2 instead",
|
|
36
|
+
)
|