TM1py 2.2.1__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.
- TM1py/Exceptions/Exceptions.py +201 -0
- TM1py/Exceptions/__init__.py +10 -0
- TM1py/Objects/Annotation.py +247 -0
- TM1py/Objects/Application.py +188 -0
- TM1py/Objects/Axis.py +119 -0
- TM1py/Objects/Chore.py +165 -0
- TM1py/Objects/ChoreFrequency.py +68 -0
- TM1py/Objects/ChoreStartTime.py +93 -0
- TM1py/Objects/ChoreTask.py +80 -0
- TM1py/Objects/Cube.py +116 -0
- TM1py/Objects/Dimension.py +128 -0
- TM1py/Objects/Element.py +110 -0
- TM1py/Objects/ElementAttribute.py +75 -0
- TM1py/Objects/Git.py +76 -0
- TM1py/Objects/GitCommit.py +27 -0
- TM1py/Objects/GitPlan.py +101 -0
- TM1py/Objects/GitProject.py +525 -0
- TM1py/Objects/GitRemote.py +28 -0
- TM1py/Objects/Hierarchy.py +343 -0
- TM1py/Objects/MDXView.py +84 -0
- TM1py/Objects/NativeView.py +331 -0
- TM1py/Objects/Process.py +512 -0
- TM1py/Objects/ProcessDebugBreakpoint.py +236 -0
- TM1py/Objects/Rules.py +100 -0
- TM1py/Objects/Sandbox.py +87 -0
- TM1py/Objects/Server.py +30 -0
- TM1py/Objects/Subset.py +295 -0
- TM1py/Objects/TM1Object.py +27 -0
- TM1py/Objects/User.py +179 -0
- TM1py/Objects/View.py +39 -0
- TM1py/Objects/__init__.py +28 -0
- TM1py/Services/AnnotationService.py +96 -0
- TM1py/Services/ApplicationService.py +898 -0
- TM1py/Services/AuditLogService.py +100 -0
- TM1py/Services/CellService.py +5465 -0
- TM1py/Services/ChoreService.py +299 -0
- TM1py/Services/ConfigurationService.py +80 -0
- TM1py/Services/CubeService.py +426 -0
- TM1py/Services/DimensionService.py +213 -0
- TM1py/Services/ElementService.py +1489 -0
- TM1py/Services/FileService.py +419 -0
- TM1py/Services/GitService.py +292 -0
- TM1py/Services/HierarchyService.py +895 -0
- TM1py/Services/JobService.py +56 -0
- TM1py/Services/LoggerService.py +97 -0
- TM1py/Services/ManageService.py +219 -0
- TM1py/Services/MessageLogService.py +161 -0
- TM1py/Services/MonitoringService.py +94 -0
- TM1py/Services/ObjectService.py +85 -0
- TM1py/Services/PowerBiService.py +81 -0
- TM1py/Services/ProcessService.py +753 -0
- TM1py/Services/RestService.py +1395 -0
- TM1py/Services/SandboxService.py +135 -0
- TM1py/Services/SecurityService.py +255 -0
- TM1py/Services/ServerService.py +292 -0
- TM1py/Services/SessionService.py +65 -0
- TM1py/Services/SubsetService.py +305 -0
- TM1py/Services/TM1Service.py +183 -0
- TM1py/Services/ThreadService.py +73 -0
- TM1py/Services/TransactionLogService.py +107 -0
- TM1py/Services/UserService.py +71 -0
- TM1py/Services/ViewService.py +308 -0
- TM1py/Services/__init__.py +33 -0
- TM1py/Utils/MDXUtils.py +248 -0
- TM1py/Utils/Utils.py +1833 -0
- TM1py/Utils/__init__.py +2 -0
- TM1py/__init__.py +84 -0
- tm1py-2.2.1.dist-info/METADATA +181 -0
- tm1py-2.2.1.dist-info/RECORD +72 -0
- tm1py-2.2.1.dist-info/WHEEL +5 -0
- tm1py-2.2.1.dist-info/licenses/LICENSE +22 -0
- tm1py-2.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# TM1py Exceptions are defined here
|
|
4
|
+
from typing import List, Mapping
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TM1pyTimeout(Exception):
|
|
8
|
+
"""Exception for timeout during a REST request."""
|
|
9
|
+
|
|
10
|
+
def __init__(self, method: str, url: str, timeout: float):
|
|
11
|
+
"""
|
|
12
|
+
:param method: HTTP method used
|
|
13
|
+
:param url: URL of the request
|
|
14
|
+
:param timeout: Timeout in seconds
|
|
15
|
+
"""
|
|
16
|
+
self.method = method
|
|
17
|
+
self.url = url
|
|
18
|
+
self.timeout = timeout
|
|
19
|
+
|
|
20
|
+
def __str__(self):
|
|
21
|
+
return f"Timeout after {self.timeout} seconds for '{self.method}' request with url :'{self.url}'"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class TM1pyVersionException(Exception):
|
|
25
|
+
"""Exception for usage of a feature requiring a higher TM1 server version."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, function: str, required_version, feature: str = None):
|
|
28
|
+
"""
|
|
29
|
+
:param function: Name of the function
|
|
30
|
+
:param required_version: Required TM1 server version
|
|
31
|
+
:param feature: Optional feature name
|
|
32
|
+
"""
|
|
33
|
+
self.function = function
|
|
34
|
+
self.required_version = required_version
|
|
35
|
+
self.feature = feature
|
|
36
|
+
|
|
37
|
+
def __str__(self):
|
|
38
|
+
require_string = f"requires TM1 server version >= '{self.required_version}'"
|
|
39
|
+
if self.feature:
|
|
40
|
+
return f"'{self.feature}' feature of function '{self.function}' {require_string}"
|
|
41
|
+
else:
|
|
42
|
+
return f"Function '{self.function}' {require_string}"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class TM1pyVersionDeprecationException(Exception):
|
|
46
|
+
"""Exception for usage of a deprecated feature."""
|
|
47
|
+
|
|
48
|
+
def __init__(self, function: str, deprecated_in_version):
|
|
49
|
+
"""
|
|
50
|
+
:param function: Name of the function
|
|
51
|
+
:param deprecated_in_version: Version in which the function was deprecated
|
|
52
|
+
"""
|
|
53
|
+
self.function = function
|
|
54
|
+
self.deprecated_in_version = deprecated_in_version
|
|
55
|
+
|
|
56
|
+
def __str__(self):
|
|
57
|
+
return f"Function '{self.function}' has been deprecated in TM1 server version >= '{self.deprecated_in_version}'"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class TM1pyPermissionException(Exception):
|
|
61
|
+
"""Exception for missing permissions."""
|
|
62
|
+
|
|
63
|
+
def __init__(self, function: str, required_permission: str):
|
|
64
|
+
"""
|
|
65
|
+
:param function: Name of the function
|
|
66
|
+
:param required_permission: Name of the required permission (e.g., 'admin', 'DataAdmin', 'SecurityAdmin', 'OperationsAdmin')
|
|
67
|
+
"""
|
|
68
|
+
self.function = function
|
|
69
|
+
self.required_permission = required_permission
|
|
70
|
+
|
|
71
|
+
def __str__(self):
|
|
72
|
+
return f"Function '{self.function}' requires {self.required_permission} permissions"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class TM1pyNotAdminException(TM1pyPermissionException):
|
|
76
|
+
"""Exception for missing admin permissions."""
|
|
77
|
+
|
|
78
|
+
def __init__(self, function: str):
|
|
79
|
+
"""
|
|
80
|
+
:param function: Name of the function
|
|
81
|
+
"""
|
|
82
|
+
super().__init__(function, "admin")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class TM1pyNotDataAdminException(TM1pyPermissionException):
|
|
86
|
+
"""Exception for missing DataAdmin permissions."""
|
|
87
|
+
|
|
88
|
+
def __init__(self, function: str):
|
|
89
|
+
"""
|
|
90
|
+
:param function: Name of the function
|
|
91
|
+
"""
|
|
92
|
+
super().__init__(function, "DataAdmin")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class TM1pyNotSecurityAdminException(TM1pyPermissionException):
|
|
96
|
+
"""Exception for missing SecurityAdmin permissions."""
|
|
97
|
+
|
|
98
|
+
def __init__(self, function: str):
|
|
99
|
+
"""
|
|
100
|
+
:param function: Name of the function
|
|
101
|
+
"""
|
|
102
|
+
super().__init__(function, "SecurityAdmin")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class TM1pyNotOpsAdminException(TM1pyPermissionException):
|
|
106
|
+
"""Exception for missing OperationsAdmin permissions."""
|
|
107
|
+
|
|
108
|
+
def __init__(self, function: str):
|
|
109
|
+
"""
|
|
110
|
+
:param function: Name of the function
|
|
111
|
+
"""
|
|
112
|
+
super().__init__(function, "OperationsAdmin")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class TM1pyException(Exception):
|
|
116
|
+
"""The default exception for TM1py."""
|
|
117
|
+
|
|
118
|
+
def __init__(self, message):
|
|
119
|
+
"""
|
|
120
|
+
:param message: Exception message
|
|
121
|
+
"""
|
|
122
|
+
self.message = message
|
|
123
|
+
|
|
124
|
+
def __str__(self):
|
|
125
|
+
return self.message
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class TM1pyRestException(TM1pyException):
|
|
129
|
+
"""Exception for failing REST operations."""
|
|
130
|
+
|
|
131
|
+
def __init__(self, response: str, status_code: int, reason: str, headers: Mapping):
|
|
132
|
+
"""
|
|
133
|
+
:param response: Response text
|
|
134
|
+
:param status_code: HTTP status code
|
|
135
|
+
:param reason: Reason phrase
|
|
136
|
+
:param headers: HTTP headers
|
|
137
|
+
"""
|
|
138
|
+
super(TM1pyRestException, self).__init__(response)
|
|
139
|
+
self._status_code = status_code
|
|
140
|
+
self._reason = reason
|
|
141
|
+
self._headers = headers
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def status_code(self):
|
|
145
|
+
"""HTTP status code."""
|
|
146
|
+
return self._status_code
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def reason(self):
|
|
150
|
+
"""Reason phrase."""
|
|
151
|
+
return self._reason
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def response(self):
|
|
155
|
+
"""Response text."""
|
|
156
|
+
return self.message
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def headers(self):
|
|
160
|
+
"""HTTP headers."""
|
|
161
|
+
return self._headers
|
|
162
|
+
|
|
163
|
+
def __str__(self):
|
|
164
|
+
return "Text: '{}' - Status Code: {} - Reason: '{}' - Headers: {}".format(
|
|
165
|
+
self.message, self._status_code, self._reason, self._headers
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class TM1pyWriteFailureException(TM1pyException):
|
|
170
|
+
"""Exception for complete failure of write operations."""
|
|
171
|
+
|
|
172
|
+
def __init__(self, statuses: List[str], error_log_files: List[str]):
|
|
173
|
+
"""
|
|
174
|
+
:param statuses: List of failed statuses
|
|
175
|
+
:param error_log_files: List of error log file paths
|
|
176
|
+
"""
|
|
177
|
+
self.statuses = statuses
|
|
178
|
+
self.error_log_files = error_log_files
|
|
179
|
+
|
|
180
|
+
message = f"All {len(self.statuses)} write operations failed. Details: {self.error_log_files}"
|
|
181
|
+
super(TM1pyWriteFailureException, self).__init__(message)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class TM1pyWritePartialFailureException(TM1pyException):
|
|
185
|
+
"""Exception for partial failure of write operations."""
|
|
186
|
+
|
|
187
|
+
def __init__(self, statuses: List[str], error_log_files: List[str], attempts: int):
|
|
188
|
+
"""
|
|
189
|
+
:param statuses: List of failed statuses
|
|
190
|
+
:param error_log_files: List of error log file paths
|
|
191
|
+
:param attempts: Total number of attempts
|
|
192
|
+
"""
|
|
193
|
+
self.statuses = statuses
|
|
194
|
+
self.error_log_files = error_log_files
|
|
195
|
+
self.attempts = attempts
|
|
196
|
+
|
|
197
|
+
message = (
|
|
198
|
+
f"{len(self.statuses)} out of {self.attempts} write operations failed partially. "
|
|
199
|
+
f"Details: {self.error_log_files}"
|
|
200
|
+
)
|
|
201
|
+
super(TM1pyWritePartialFailureException, self).__init__(message)
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
import collections
|
|
4
|
+
import json
|
|
5
|
+
from typing import Dict, Iterable, List
|
|
6
|
+
|
|
7
|
+
from TM1py.Objects.TM1Object import TM1Object
|
|
8
|
+
from TM1py.Utils import format_url
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Annotation(TM1Object):
|
|
12
|
+
"""Abtraction of TM1 Annotation
|
|
13
|
+
|
|
14
|
+
:Notes:
|
|
15
|
+
- Class complete, functional and tested.
|
|
16
|
+
- doesn't cover Attachments though
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
comment_value: str,
|
|
22
|
+
object_name: str,
|
|
23
|
+
dimensional_context: Iterable[str],
|
|
24
|
+
comment_type: str = "ANNOTATION",
|
|
25
|
+
annotation_id: str = None,
|
|
26
|
+
text: str = "",
|
|
27
|
+
creator: str = None,
|
|
28
|
+
created: str = None,
|
|
29
|
+
last_updated_by: str = None,
|
|
30
|
+
last_updated: str = None,
|
|
31
|
+
):
|
|
32
|
+
"""
|
|
33
|
+
Initialize an Annotation object.
|
|
34
|
+
|
|
35
|
+
:param comment_value: The value of the annotation comment.
|
|
36
|
+
:param object_name: Name of the TM1 object the annotation is attached to.
|
|
37
|
+
:param dimensional_context: Iterable of dimension elements providing context.
|
|
38
|
+
:param comment_type: Type of the comment (default "ANNOTATION").
|
|
39
|
+
:param annotation_id: Unique ID of the annotation.
|
|
40
|
+
:param text: Text of the annotation.
|
|
41
|
+
:param creator: Creator of the annotation.
|
|
42
|
+
:param created: Creation timestamp.
|
|
43
|
+
:param last_updated_by: Last user who updated the annotation.
|
|
44
|
+
:param last_updated: Last update timestamp.
|
|
45
|
+
"""
|
|
46
|
+
self._id = annotation_id
|
|
47
|
+
self._text = text
|
|
48
|
+
self._creator = creator
|
|
49
|
+
self._created = created
|
|
50
|
+
self._last_updated_by = last_updated_by
|
|
51
|
+
self._last_updated = last_updated
|
|
52
|
+
self._dimensional_context = list(dimensional_context)
|
|
53
|
+
self._comment_type = comment_type
|
|
54
|
+
self._comment_value = comment_value
|
|
55
|
+
self._object_name = object_name
|
|
56
|
+
|
|
57
|
+
@classmethod
|
|
58
|
+
def from_json(cls, annotation_as_json: str) -> "Annotation":
|
|
59
|
+
"""Alternative constructor
|
|
60
|
+
|
|
61
|
+
:param annotation_as_json: String, JSON
|
|
62
|
+
:return: instance of Annotation
|
|
63
|
+
"""
|
|
64
|
+
annotation_as_dict = json.loads(annotation_as_json)
|
|
65
|
+
annotation_id = annotation_as_dict["ID"]
|
|
66
|
+
text = annotation_as_dict["Text"]
|
|
67
|
+
creator = annotation_as_dict["Creator"]
|
|
68
|
+
created = annotation_as_dict["Created"]
|
|
69
|
+
last_updated_by = annotation_as_dict["LastUpdatedBy"]
|
|
70
|
+
last_updated = annotation_as_dict["LastUpdated"]
|
|
71
|
+
dimensional_context = [item["Name"] for item in annotation_as_dict["DimensionalContext"]]
|
|
72
|
+
comment_type = annotation_as_dict["commentType"]
|
|
73
|
+
comment_value = annotation_as_dict["commentValue"]
|
|
74
|
+
object_name = annotation_as_dict["objectName"]
|
|
75
|
+
return cls(
|
|
76
|
+
comment_value=comment_value,
|
|
77
|
+
object_name=object_name,
|
|
78
|
+
dimensional_context=dimensional_context,
|
|
79
|
+
comment_type=comment_type,
|
|
80
|
+
annotation_id=annotation_id,
|
|
81
|
+
text=text,
|
|
82
|
+
creator=creator,
|
|
83
|
+
created=created,
|
|
84
|
+
last_updated_by=last_updated_by,
|
|
85
|
+
last_updated=last_updated,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def body(self) -> str:
|
|
90
|
+
"""
|
|
91
|
+
Get the annotation body as a JSON string.
|
|
92
|
+
|
|
93
|
+
:return: JSON string representation of the annotation.
|
|
94
|
+
"""
|
|
95
|
+
return json.dumps(self._construct_body())
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def body_as_dict(self) -> Dict:
|
|
99
|
+
"""
|
|
100
|
+
Get the annotation body as a dictionary.
|
|
101
|
+
|
|
102
|
+
:return: Dictionary representation of the annotation.
|
|
103
|
+
"""
|
|
104
|
+
return self._construct_body()
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def comment_value(self) -> str:
|
|
108
|
+
"""
|
|
109
|
+
Get the comment value.
|
|
110
|
+
|
|
111
|
+
:return: The comment value string.
|
|
112
|
+
"""
|
|
113
|
+
return self._comment_value
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def text(self) -> str:
|
|
117
|
+
"""
|
|
118
|
+
Get the annotation text.
|
|
119
|
+
|
|
120
|
+
:return: The annotation text.
|
|
121
|
+
"""
|
|
122
|
+
return self._text
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def dimensional_context(self) -> List[str]:
|
|
126
|
+
"""
|
|
127
|
+
Get the dimensional context.
|
|
128
|
+
|
|
129
|
+
:return: List of dimension elements providing context.
|
|
130
|
+
"""
|
|
131
|
+
return self._dimensional_context
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def created(self) -> str:
|
|
135
|
+
"""
|
|
136
|
+
Get the creation timestamp.
|
|
137
|
+
|
|
138
|
+
:return: Creation timestamp as string.
|
|
139
|
+
"""
|
|
140
|
+
return self._created
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def object_name(self) -> str:
|
|
144
|
+
"""
|
|
145
|
+
Get the object name.
|
|
146
|
+
|
|
147
|
+
:return: Name of the TM1 object.
|
|
148
|
+
"""
|
|
149
|
+
return self._object_name
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def last_updated(self) -> str:
|
|
153
|
+
"""
|
|
154
|
+
Get the last updated timestamp.
|
|
155
|
+
|
|
156
|
+
:return: Last update timestamp as string.
|
|
157
|
+
"""
|
|
158
|
+
return self._last_updated
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def last_updated_by(self) -> str:
|
|
162
|
+
"""
|
|
163
|
+
Get the last user who updated the annotation.
|
|
164
|
+
|
|
165
|
+
:return: Username of last updater.
|
|
166
|
+
"""
|
|
167
|
+
return self._last_updated_by
|
|
168
|
+
|
|
169
|
+
@comment_value.setter
|
|
170
|
+
def comment_value(self, value: str):
|
|
171
|
+
"""
|
|
172
|
+
Set the comment value.
|
|
173
|
+
|
|
174
|
+
:param value: New comment value.
|
|
175
|
+
"""
|
|
176
|
+
self._comment_value = value
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def id(self) -> str:
|
|
180
|
+
"""
|
|
181
|
+
Get the annotation ID.
|
|
182
|
+
|
|
183
|
+
:return: Annotation ID string.
|
|
184
|
+
"""
|
|
185
|
+
return self._id
|
|
186
|
+
|
|
187
|
+
def move(self, dimension_order: Iterable[str], dimension: str, target_element: str, source_element: str = None):
|
|
188
|
+
"""
|
|
189
|
+
Move annotation on given dimension from source_element to target_element.
|
|
190
|
+
|
|
191
|
+
:param dimension_order: List, order of the dimensions in the cube.
|
|
192
|
+
:param dimension: Dimension name.
|
|
193
|
+
:param target_element: Target element name.
|
|
194
|
+
:param source_element: Source element name (optional).
|
|
195
|
+
:return: None
|
|
196
|
+
"""
|
|
197
|
+
for i, dimension_name in enumerate(dimension_order):
|
|
198
|
+
if dimension_name.lower() == dimension.lower():
|
|
199
|
+
if not source_element or self._dimensional_context[i] == source_element:
|
|
200
|
+
self._dimensional_context[i] = target_element
|
|
201
|
+
|
|
202
|
+
def _construct_body(self) -> Dict:
|
|
203
|
+
"""
|
|
204
|
+
Construct the ODATA conform JSON representation for the Annotation entity.
|
|
205
|
+
|
|
206
|
+
:return: Dictionary, the valid JSON.
|
|
207
|
+
"""
|
|
208
|
+
dimensional_context = [{"Name": element} for element in self._dimensional_context]
|
|
209
|
+
body = collections.OrderedDict()
|
|
210
|
+
body["ID"] = self._id
|
|
211
|
+
body["Text"] = self._text
|
|
212
|
+
body["Creator"] = self._creator
|
|
213
|
+
body["Created"] = self._created
|
|
214
|
+
body["LastUpdatedBy"] = self._last_updated_by
|
|
215
|
+
body["LastUpdated"] = self._last_updated
|
|
216
|
+
body["DimensionalContext"] = dimensional_context
|
|
217
|
+
comment_locations = ",".join(self._dimensional_context)
|
|
218
|
+
body["commentLocation"] = comment_locations[1:]
|
|
219
|
+
body["commentType"] = self._comment_type
|
|
220
|
+
body["commentValue"] = self._comment_value
|
|
221
|
+
body["objectName"] = self._object_name
|
|
222
|
+
return body
|
|
223
|
+
|
|
224
|
+
def construct_body_for_post(self, cube_dimensions) -> Dict:
|
|
225
|
+
"""
|
|
226
|
+
Construct the body for POST requests to create an annotation.
|
|
227
|
+
|
|
228
|
+
:param cube_dimensions: List of cube dimension names.
|
|
229
|
+
:return: Dictionary for POST request body.
|
|
230
|
+
"""
|
|
231
|
+
body = collections.OrderedDict()
|
|
232
|
+
body["Text"] = self.text
|
|
233
|
+
body["ApplicationContext"] = [
|
|
234
|
+
{"Facet@odata.bind": "ApplicationContextFacets('}Cubes')", "Value": self.object_name}
|
|
235
|
+
]
|
|
236
|
+
body["DimensionalContext@odata.bind"] = []
|
|
237
|
+
|
|
238
|
+
for dimension, element in zip(cube_dimensions, self.dimensional_context):
|
|
239
|
+
coordinates = format_url("Dimensions('{}')/Hierarchies('{}')/Members('{}')", dimension, dimension, element)
|
|
240
|
+
body["DimensionalContext@odata.bind"].append(coordinates)
|
|
241
|
+
|
|
242
|
+
body["objectName"] = self.object_name
|
|
243
|
+
body["commentValue"] = self.comment_value
|
|
244
|
+
body["commentType"] = "ANNOTATION"
|
|
245
|
+
body["commentLocation"] = ",".join(self.dimensional_context)
|
|
246
|
+
|
|
247
|
+
return body
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import json
|
|
3
|
+
import warnings
|
|
4
|
+
from collections import OrderedDict, namedtuple
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import Dict, Union
|
|
7
|
+
|
|
8
|
+
from TM1py.Objects.TM1Object import TM1Object
|
|
9
|
+
from TM1py.Utils import format_url
|
|
10
|
+
|
|
11
|
+
ApplicationType = namedtuple("ApplicationType", ["value", "suffix", "odata_type"])
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ApplicationTypes(Enum):
|
|
15
|
+
CHORE = ApplicationType(1, ".chore", "tm1.ChoreReference")
|
|
16
|
+
CUBE = ApplicationType(2, ".cube", "tm1.CubeReference")
|
|
17
|
+
DIMENSION = ApplicationType(3, ".dimension", "tm1.DimensionReference")
|
|
18
|
+
DOCUMENT = ApplicationType(4, ".blob", "#ibm.tm1.api.v1.Document")
|
|
19
|
+
FOLDER = ApplicationType(5, "", "#ibm.tm1.api.v1.Folder")
|
|
20
|
+
LINK = ApplicationType(6, ".extr", "#ibm.tm1.api.v1.Link")
|
|
21
|
+
PROCESS = ApplicationType(7, ".process", "tm1.ProcessReference")
|
|
22
|
+
SUBSET = ApplicationType(8, ".subset", "tm1.SubsetReference")
|
|
23
|
+
VIEW = ApplicationType(9, ".view", "tm1.ViewReference")
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def _missing_(cls, value: str) -> ApplicationType:
|
|
27
|
+
for member in cls:
|
|
28
|
+
if member.name.lower() == value.lower():
|
|
29
|
+
return member
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def suffix(self) -> str:
|
|
33
|
+
return self.value.suffix
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def odata_type(self) -> str:
|
|
37
|
+
return self.value.odata_type
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Application(TM1Object):
|
|
41
|
+
|
|
42
|
+
def __init__(self, path: str, name: str, application_type: Union[ApplicationTypes, str]):
|
|
43
|
+
self.path = path
|
|
44
|
+
# remove suffix from name
|
|
45
|
+
if application_type.suffix and name.endswith(application_type.suffix):
|
|
46
|
+
self.name = name[: -len(application_type.suffix)]
|
|
47
|
+
else:
|
|
48
|
+
self.name = name
|
|
49
|
+
# raise ValueError if not a valid type
|
|
50
|
+
self.application_type = ApplicationTypes(application_type)
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def application_id(self) -> str:
|
|
54
|
+
return self.path + self.name + self.application_type.suffix
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def body_as_dict(self) -> Dict:
|
|
58
|
+
body_as_dict = OrderedDict()
|
|
59
|
+
body_as_dict["@odata.type"] = self.application_type.odata_type
|
|
60
|
+
body_as_dict["Name"] = self.name
|
|
61
|
+
return body_as_dict
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def body(self) -> str:
|
|
65
|
+
body_as_dict = self.body_as_dict
|
|
66
|
+
return json.dumps(body_as_dict, ensure_ascii=False)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class ChoreApplication(Application):
|
|
70
|
+
def __init__(self, path: str, name: str, chore_name: str):
|
|
71
|
+
super().__init__(path, name, ApplicationTypes.CHORE)
|
|
72
|
+
self.chore_name = chore_name
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def body(self) -> str:
|
|
76
|
+
body_as_dict = self.body_as_dict
|
|
77
|
+
body_as_dict["Chore@odata.bind"] = format_url("Chores('{}')", self.chore_name)
|
|
78
|
+
return json.dumps(body_as_dict, ensure_ascii=False)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class CubeApplication(Application):
|
|
82
|
+
def __init__(self, path: str, name: str, cube_name: str):
|
|
83
|
+
super().__init__(path, name, ApplicationTypes.CUBE)
|
|
84
|
+
self.cube_name = cube_name
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def body(self) -> str:
|
|
88
|
+
body_as_dict = self.body_as_dict
|
|
89
|
+
body_as_dict["Cube@odata.bind"] = format_url("Cubes('{}')", self.cube_name)
|
|
90
|
+
return json.dumps(body_as_dict, ensure_ascii=False)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class DimensionApplication(Application):
|
|
94
|
+
def __init__(self, path: str, name: str, dimension_name: str):
|
|
95
|
+
super().__init__(path, name, ApplicationTypes.DIMENSION)
|
|
96
|
+
self.dimension_name = dimension_name
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def body(self) -> str:
|
|
100
|
+
body_as_dict = self.body_as_dict
|
|
101
|
+
body_as_dict["Dimension@odata.bind"] = format_url("Dimensions('{}')", self.dimension_name)
|
|
102
|
+
return json.dumps(body_as_dict, ensure_ascii=False)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class DocumentApplication(Application):
|
|
106
|
+
def __init__(
|
|
107
|
+
self, path: str, name: str, content: bytes, file_id: str = None, file_name: str = None, last_updated: str = None
|
|
108
|
+
):
|
|
109
|
+
super().__init__(path, name, ApplicationTypes.DOCUMENT)
|
|
110
|
+
self.content = content
|
|
111
|
+
# below fields only populated for retrieved applications
|
|
112
|
+
self.file_id = file_id
|
|
113
|
+
self.file_name = file_name
|
|
114
|
+
self.last_updated = last_updated
|
|
115
|
+
|
|
116
|
+
def to_xlsx(self, path_to_file: str):
|
|
117
|
+
warnings.warn("Function 'to_xlsx' is deprecated. Use 'to_file' instead", DeprecationWarning, stacklevel=2)
|
|
118
|
+
return self.to_file(path_to_file=path_to_file)
|
|
119
|
+
|
|
120
|
+
def to_file(self, path_to_file: str):
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
:param path_to_file: path to newly to create file including the extension (e.g., xlsx, xlsm)
|
|
124
|
+
:return:
|
|
125
|
+
"""
|
|
126
|
+
with open(path_to_file, "wb") as file:
|
|
127
|
+
file.write(self.content)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class FolderApplication(Application):
|
|
131
|
+
def __init__(self, path: str, name: str):
|
|
132
|
+
super().__init__(path, name, ApplicationTypes.FOLDER)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class LinkApplication(Application):
|
|
136
|
+
def __init__(self, path: str, name: str, url: str):
|
|
137
|
+
super().__init__(path, name, ApplicationTypes.LINK)
|
|
138
|
+
self.url = url
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def body(self) -> str:
|
|
142
|
+
body_as_dict = self.body_as_dict
|
|
143
|
+
body_as_dict["URL"] = self.url
|
|
144
|
+
return json.dumps(body_as_dict, ensure_ascii=False)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class ProcessApplication(Application):
|
|
148
|
+
def __init__(self, path: str, name: str, process_name: str):
|
|
149
|
+
super().__init__(path, name, ApplicationTypes.PROCESS)
|
|
150
|
+
self.process_name = process_name
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def body(self) -> str:
|
|
154
|
+
body_as_dict = self.body_as_dict
|
|
155
|
+
body_as_dict["Process@odata.bind"] = format_url("Processes('{}')", self.process_name)
|
|
156
|
+
return json.dumps(body_as_dict, ensure_ascii=False)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class SubsetApplication(Application):
|
|
160
|
+
def __init__(self, path: str, name: str, dimension_name: str, hierarchy_name: str, subset_name: str):
|
|
161
|
+
super().__init__(path, name, ApplicationTypes.SUBSET)
|
|
162
|
+
self.dimension_name = dimension_name
|
|
163
|
+
self.hierarchy_name = hierarchy_name
|
|
164
|
+
self.subset_name = subset_name
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def body(self) -> str:
|
|
168
|
+
body_as_dict = self.body_as_dict
|
|
169
|
+
body_as_dict["Subset@odata.bind"] = format_url(
|
|
170
|
+
"Dimensions('{}')/Hierarchies('{}')/Subsets('{}')",
|
|
171
|
+
self.dimension_name,
|
|
172
|
+
self.hierarchy_name,
|
|
173
|
+
self.subset_name,
|
|
174
|
+
)
|
|
175
|
+
return json.dumps(body_as_dict, ensure_ascii=False)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class ViewApplication(Application):
|
|
179
|
+
def __init__(self, path: str, name: str, cube_name: str, view_name: str):
|
|
180
|
+
super().__init__(path, name, ApplicationTypes.VIEW)
|
|
181
|
+
self.cube_name = cube_name
|
|
182
|
+
self.view_name = view_name
|
|
183
|
+
|
|
184
|
+
@property
|
|
185
|
+
def body(self) -> str:
|
|
186
|
+
body_as_dict = self.body_as_dict
|
|
187
|
+
body_as_dict["View@odata.bind"] = format_url("Cubes('{}')/Views('{}')", self.cube_name, self.view_name)
|
|
188
|
+
return json.dumps(body_as_dict, ensure_ascii=False)
|