TM1py 2.2.4__tar.gz → 2.2.5__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.
- {tm1py-2.2.4 → tm1py-2.2.5}/PKG-INFO +1 -1
- tm1py-2.2.5/TM1py/Objects/DynamicPropertiesMixin.py +29 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/MDXView.py +18 -25
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/NativeView.py +40 -27
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ElementService.py +74 -11
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ViewService.py +2 -5
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py.egg-info/PKG-INFO +1 -1
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py.egg-info/SOURCES.txt +1 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/pyproject.toml +1 -1
- {tm1py-2.2.4 → tm1py-2.2.5}/LICENSE +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/README.md +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Exceptions/Exceptions.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Exceptions/__init__.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Annotation.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Application.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Axis.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Chore.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/ChoreFrequency.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/ChoreStartTime.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/ChoreTask.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Cube.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Dimension.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Element.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/ElementAttribute.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Git.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/GitCommit.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/GitPlan.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/GitProject.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/GitRemote.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Hierarchy.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Process.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/ProcessDebugBreakpoint.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Rules.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Sandbox.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Server.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Subset.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/TM1Object.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/User.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/View.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/__init__.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/AnnotationService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ApplicationService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/AuditLogService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/CellService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ChoreService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ConfigurationService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/CubeService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/DimensionService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/FileService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/GitService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/HierarchyService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/JobService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/LoggerService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ManageService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/MessageLogService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/MonitoringService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ObjectService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/PowerBiService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ProcessService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/RestService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/SandboxService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/SecurityService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ServerService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/SessionService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/SubsetService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/TM1Service.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ThreadService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/TransactionLogService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/UserService.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/__init__.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Utils/MDXUtils.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Utils/Utils.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Utils/__init__.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/__init__.py +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py.egg-info/dependency_links.txt +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py.egg-info/requires.txt +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/TM1py.egg-info/top_level.txt +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/setup.cfg +0 -0
- {tm1py-2.2.4 → tm1py-2.2.5}/setup.py +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from typing import Dict, Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DynamicPropertiesMixin:
|
|
7
|
+
"""Mixin that adds support for dynamic/extra properties from the TM1 REST API.
|
|
8
|
+
|
|
9
|
+
TM1 objects can carry additional properties beyond their known fields.
|
|
10
|
+
This mixin provides a common interface to store, filter, and serialize them.
|
|
11
|
+
|
|
12
|
+
Subclasses must define ``_DYNAMIC_PROPERTIES_EXCLUDED_KEYS`` as a frozenset
|
|
13
|
+
of keys that are already handled explicitly (e.g. "Name", "@odata.type").
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
_DYNAMIC_PROPERTIES_EXCLUDED_KEYS: frozenset = frozenset()
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def dynamic_properties(self) -> Dict:
|
|
20
|
+
return self._dynamic_properties
|
|
21
|
+
|
|
22
|
+
@dynamic_properties.setter
|
|
23
|
+
def dynamic_properties(self, value: Optional[Dict]) -> None:
|
|
24
|
+
self._dynamic_properties = value or {}
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def _filter_dynamic_properties(cls, properties: Dict) -> Dict:
|
|
28
|
+
"""Return a copy of *properties* with all reserved/excluded keys removed."""
|
|
29
|
+
return {k: v for k, v in properties.items() if k not in cls._DYNAMIC_PROPERTIES_EXCLUDED_KEYS}
|
|
@@ -5,29 +5,30 @@ import json
|
|
|
5
5
|
import re
|
|
6
6
|
from typing import Dict, Optional
|
|
7
7
|
|
|
8
|
+
from TM1py.Objects.DynamicPropertiesMixin import DynamicPropertiesMixin
|
|
8
9
|
from TM1py.Objects.View import View
|
|
9
10
|
from TM1py.Utils import case_and_space_insensitive_equals
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"@odata.type",
|
|
14
|
-
"@odata.context",
|
|
15
|
-
"@odata.etag",
|
|
16
|
-
"Name",
|
|
17
|
-
"MDX",
|
|
18
|
-
"Cube",
|
|
19
|
-
"Attributes",
|
|
20
|
-
"LocalizedAttributes",
|
|
21
|
-
}
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class MDXView(View):
|
|
12
|
+
|
|
13
|
+
class MDXView(DynamicPropertiesMixin, View):
|
|
26
14
|
"""Abstraction on TM1 MDX view
|
|
27
15
|
|
|
28
16
|
IMPORTANT. MDXViews can't be seen through the old TM1 clients (Architect, Perspectives). They do exist though!
|
|
29
17
|
"""
|
|
30
18
|
|
|
19
|
+
_DYNAMIC_PROPERTIES_EXCLUDED_KEYS = frozenset(
|
|
20
|
+
{
|
|
21
|
+
"@odata.type",
|
|
22
|
+
"@odata.context",
|
|
23
|
+
"@odata.etag",
|
|
24
|
+
"Name",
|
|
25
|
+
"MDX",
|
|
26
|
+
"Cube",
|
|
27
|
+
"Attributes",
|
|
28
|
+
"LocalizedAttributes",
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
|
|
31
32
|
def __init__(self, cube_name: str, view_name: str, MDX: str, dynamic_properties: Optional[Dict] = None):
|
|
32
33
|
View.__init__(self, cube_name, view_name)
|
|
33
34
|
self._mdx = MDX
|
|
@@ -50,14 +51,6 @@ class MDXView(View):
|
|
|
50
51
|
def MDX(self, value: str):
|
|
51
52
|
self._mdx = value
|
|
52
53
|
|
|
53
|
-
@property
|
|
54
|
-
def dynamic_properties(self) -> Dict:
|
|
55
|
-
return self._dynamic_properties
|
|
56
|
-
|
|
57
|
-
@dynamic_properties.setter
|
|
58
|
-
def dynamic_properties(self, value: Optional[Dict]) -> None:
|
|
59
|
-
self._dynamic_properties = value or {}
|
|
60
|
-
|
|
61
54
|
@property
|
|
62
55
|
def body(self) -> str:
|
|
63
56
|
return self.construct_body()
|
|
@@ -97,7 +90,7 @@ class MDXView(View):
|
|
|
97
90
|
cube_name=view_as_dict["Cube"]["Name"] if not cube_name else cube_name,
|
|
98
91
|
view_name=view_as_dict["Name"],
|
|
99
92
|
MDX=view_as_dict["MDX"],
|
|
100
|
-
dynamic_properties=
|
|
93
|
+
dynamic_properties=cls._filter_dynamic_properties(view_as_dict),
|
|
101
94
|
)
|
|
102
95
|
|
|
103
96
|
def construct_body(self) -> str:
|
|
@@ -105,5 +98,5 @@ class MDXView(View):
|
|
|
105
98
|
mdx_view_as_dict["@odata.type"] = "ibm.tm1.api.v1.MDXView"
|
|
106
99
|
mdx_view_as_dict["Name"] = self._name
|
|
107
100
|
mdx_view_as_dict["MDX"] = self._mdx
|
|
108
|
-
mdx_view_as_dict.update(
|
|
101
|
+
mdx_view_as_dict.update(self._filter_dynamic_properties(self._dynamic_properties))
|
|
109
102
|
return json.dumps(mdx_view_as_dict, ensure_ascii=False)
|
|
@@ -6,18 +6,37 @@ from typing import Dict, Iterable, List, Optional, Union
|
|
|
6
6
|
from mdxpy import MdxBuilder, MdxHierarchySet, Member
|
|
7
7
|
|
|
8
8
|
from TM1py.Objects.Axis import ViewAxisSelection, ViewTitleSelection
|
|
9
|
+
from TM1py.Objects.DynamicPropertiesMixin import DynamicPropertiesMixin
|
|
9
10
|
from TM1py.Objects.Subset import AnonymousSubset, Subset
|
|
10
11
|
from TM1py.Objects.View import View
|
|
11
12
|
from TM1py.Utils import case_and_space_insensitive_equals, read_object_name_from_url
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
class NativeView(View):
|
|
15
|
+
class NativeView(DynamicPropertiesMixin, View):
|
|
15
16
|
"""Abstraction of TM1 NativeView (classic cube view)
|
|
16
17
|
|
|
17
18
|
:Notes:
|
|
18
19
|
Complete, functional and tested
|
|
19
20
|
"""
|
|
20
21
|
|
|
22
|
+
_DYNAMIC_PROPERTIES_EXCLUDED_KEYS = frozenset(
|
|
23
|
+
{
|
|
24
|
+
"@odata.type",
|
|
25
|
+
"@odata.context",
|
|
26
|
+
"@odata.etag",
|
|
27
|
+
"Name",
|
|
28
|
+
"Columns",
|
|
29
|
+
"Rows",
|
|
30
|
+
"Titles",
|
|
31
|
+
"SuppressEmptyColumns",
|
|
32
|
+
"SuppressEmptyRows",
|
|
33
|
+
"FormatString",
|
|
34
|
+
"Cube",
|
|
35
|
+
"Attributes",
|
|
36
|
+
"LocalizedAttributes",
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
|
|
21
40
|
def __init__(
|
|
22
41
|
self,
|
|
23
42
|
cube_name: str,
|
|
@@ -28,6 +47,7 @@ class NativeView(View):
|
|
|
28
47
|
titles: Optional[Iterable[ViewTitleSelection]] = None,
|
|
29
48
|
columns: Optional[Iterable[ViewAxisSelection]] = None,
|
|
30
49
|
rows: Optional[Iterable[ViewAxisSelection]] = None,
|
|
50
|
+
dynamic_properties: Optional[Dict] = None,
|
|
31
51
|
):
|
|
32
52
|
super().__init__(cube_name, view_name)
|
|
33
53
|
self._suppress_empty_columns = suppress_empty_columns
|
|
@@ -36,6 +56,8 @@ class NativeView(View):
|
|
|
36
56
|
self._titles = list(titles) if titles else []
|
|
37
57
|
self._columns = list(columns) if columns else []
|
|
38
58
|
self._rows = list(rows) if rows else []
|
|
59
|
+
self._dynamic_properties = {}
|
|
60
|
+
self.dynamic_properties = dynamic_properties
|
|
39
61
|
|
|
40
62
|
@property
|
|
41
63
|
def body(self) -> str:
|
|
@@ -270,6 +292,7 @@ class NativeView(View):
|
|
|
270
292
|
titles=titles,
|
|
271
293
|
columns=columns,
|
|
272
294
|
rows=rows,
|
|
295
|
+
dynamic_properties=cls._filter_dynamic_properties(view_as_dict),
|
|
273
296
|
)
|
|
274
297
|
|
|
275
298
|
@classmethod
|
|
@@ -303,29 +326,19 @@ class NativeView(View):
|
|
|
303
326
|
|
|
304
327
|
:return: string, the valid JSON
|
|
305
328
|
"""
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
'"Columns":[',
|
|
323
|
-
columns_json,
|
|
324
|
-
'],"Rows":[',
|
|
325
|
-
rows_json,
|
|
326
|
-
'],"Titles":[',
|
|
327
|
-
titles_json,
|
|
328
|
-
"],",
|
|
329
|
-
bottom_json,
|
|
330
|
-
]
|
|
331
|
-
)
|
|
329
|
+
body = {
|
|
330
|
+
"@odata.type": "ibm.tm1.api.v1.NativeView",
|
|
331
|
+
"Name": self._name,
|
|
332
|
+
"Columns": [json.loads(column.body) for column in self._columns],
|
|
333
|
+
"Rows": [json.loads(row.body) for row in self._rows],
|
|
334
|
+
"Titles": [json.loads(title.body) for title in self._titles],
|
|
335
|
+
"SuppressEmptyColumns": self._suppress_empty_columns,
|
|
336
|
+
"SuppressEmptyRows": self._suppress_empty_rows,
|
|
337
|
+
"FormatString": self._format_string,
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
dynamic_props = self._filter_dynamic_properties(self._dynamic_properties)
|
|
341
|
+
if dynamic_props:
|
|
342
|
+
body.update(dynamic_props)
|
|
343
|
+
|
|
344
|
+
return json.dumps(body, ensure_ascii=False)
|
|
@@ -136,32 +136,74 @@ class ElementService(ObjectService):
|
|
|
136
136
|
use_ti: bool = False,
|
|
137
137
|
use_blob: bool = False,
|
|
138
138
|
remove_blob: bool = True,
|
|
139
|
+
skip_invalid_edges: bool = True,
|
|
139
140
|
**kwargs,
|
|
140
141
|
):
|
|
142
|
+
"""
|
|
143
|
+
Remove edges in TM1.
|
|
144
|
+
|
|
145
|
+
:param dimension_name: The name of the dimension.
|
|
146
|
+
:param hierarchy_name: The name of the hierarchy.
|
|
147
|
+
:param edges: A list of tuples representing the edges to remove, where each tuple contains a parent and a child.
|
|
148
|
+
:param use_ti: A boolean indicating whether to use a TI process to delete edges (default: False).
|
|
149
|
+
:param use_blob: A boolean indicating whether to use a blob file to delete edges (default: False).
|
|
150
|
+
:param remove_blob: A boolean indicating whether to remove the parent-child file after use (default: True).
|
|
151
|
+
:param skip_invalid_edges: A boolean indicating whether to skip invalid edges (default: True).
|
|
152
|
+
"""
|
|
141
153
|
if use_ti:
|
|
142
|
-
return self.delete_edges_use_ti(dimension_name, hierarchy_name, edges, **kwargs)
|
|
154
|
+
return self.delete_edges_use_ti(dimension_name, hierarchy_name, edges, skip_invalid_edges, **kwargs)
|
|
143
155
|
|
|
144
156
|
if use_blob:
|
|
145
|
-
return self.delete_edges_use_blob(
|
|
157
|
+
return self.delete_edges_use_blob(
|
|
158
|
+
dimension_name, hierarchy_name, edges, remove_blob, skip_invalid_edges, **kwargs
|
|
159
|
+
)
|
|
146
160
|
|
|
147
161
|
h_service = self._get_hierarchy_service()
|
|
148
162
|
h = h_service.get(dimension_name, hierarchy_name, **kwargs)
|
|
149
163
|
for edge in edges:
|
|
164
|
+
if edge not in h.edges and not skip_invalid_edges:
|
|
165
|
+
raise TM1pyException(f"Edge {edge} does not exist in hierarchy [{dimension_name}].[{hierarchy_name}]")
|
|
150
166
|
h.remove_edge(parent=edge[0], component=edge[1])
|
|
151
167
|
h_service.update(h, **kwargs)
|
|
152
168
|
|
|
153
|
-
def delete_edges_use_ti(
|
|
169
|
+
def delete_edges_use_ti(
|
|
170
|
+
self,
|
|
171
|
+
dimension_name: str,
|
|
172
|
+
hierarchy_name: str,
|
|
173
|
+
edges: List[str] = None,
|
|
174
|
+
skip_invalid_edges: bool = True,
|
|
175
|
+
**kwargs,
|
|
176
|
+
):
|
|
177
|
+
"""
|
|
178
|
+
Remove edges in TM1 via an unbound TI process.
|
|
179
|
+
:param dimension_name: The name of the dimension.
|
|
180
|
+
:param hierarchy_name: The name of the hierarchy.
|
|
181
|
+
:param edges: A list of tuples representing the edges to remove, where each tuple contains a parent and a child.
|
|
182
|
+
:param skip_invalid_edges: A boolean indicating whether to skip invalid edges (default: True).
|
|
183
|
+
"""
|
|
154
184
|
if not edges:
|
|
155
185
|
return
|
|
156
186
|
|
|
157
187
|
def escape_single_quote(text):
|
|
158
188
|
return text.replace("'", "''")
|
|
159
189
|
|
|
160
|
-
statements = [
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
190
|
+
statements = []
|
|
191
|
+
for parent, child in edges:
|
|
192
|
+
parent = escape_single_quote(parent)
|
|
193
|
+
child = escape_single_quote(child)
|
|
194
|
+
if skip_invalid_edges:
|
|
195
|
+
statements.extend(
|
|
196
|
+
[
|
|
197
|
+
f"IF(ElementIsParent('{dimension_name}','{hierarchy_name}','{parent}','{child}')=1);",
|
|
198
|
+
f"HierarchyElementComponentDelete('{dimension_name}','{hierarchy_name}','{parent}','{child}');",
|
|
199
|
+
"ENDIF;",
|
|
200
|
+
]
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
else:
|
|
204
|
+
statements.append(
|
|
205
|
+
f"HierarchyElementComponentDelete('{dimension_name}', '{hierarchy_name}', '{parent}', '{child}');"
|
|
206
|
+
)
|
|
165
207
|
|
|
166
208
|
unbound_process_name = self.suggest_unique_object_name()
|
|
167
209
|
|
|
@@ -174,7 +216,13 @@ class ElementService(ObjectService):
|
|
|
174
216
|
@require_data_admin
|
|
175
217
|
@require_ops_admin
|
|
176
218
|
def delete_edges_use_blob(
|
|
177
|
-
self,
|
|
219
|
+
self,
|
|
220
|
+
dimension_name: str,
|
|
221
|
+
hierarchy_name: str,
|
|
222
|
+
edges: List[str] = None,
|
|
223
|
+
remove_blob: bool = True,
|
|
224
|
+
skip_invalid_edges: bool = True,
|
|
225
|
+
**kwargs,
|
|
178
226
|
):
|
|
179
227
|
"""
|
|
180
228
|
Remove edges in TM1 via an unbound TI process having an uploaded CSV as the data source.
|
|
@@ -183,6 +231,7 @@ class ElementService(ObjectService):
|
|
|
183
231
|
:param hierarchy_name: The name of the hierarchy.
|
|
184
232
|
:param edges: A list of tuples representing the edges to remove, where each tuple contains a parent and a child.
|
|
185
233
|
:param remove_blob: A boolean indicating whether to remove the parent-child file after use (default: True).
|
|
234
|
+
:param skip_invalid_edges: A boolean indicating whether to skip invalid edges (default: True).
|
|
186
235
|
:param kwargs: Additional arguments for the process execution.
|
|
187
236
|
:return: None
|
|
188
237
|
"""
|
|
@@ -209,6 +258,7 @@ class ElementService(ObjectService):
|
|
|
209
258
|
hierarchy_name=hierarchy_name,
|
|
210
259
|
process_name=unique_name,
|
|
211
260
|
blob_filename=file_name,
|
|
261
|
+
skip_invalid_edges=skip_invalid_edges,
|
|
212
262
|
)
|
|
213
263
|
|
|
214
264
|
success, status, log_file = process_service.execute_process_with_return(process=process, **kwargs)
|
|
@@ -223,7 +273,12 @@ class ElementService(ObjectService):
|
|
|
223
273
|
file_service.delete(file_name=file_name)
|
|
224
274
|
|
|
225
275
|
def _build_unwind_hierarchy_edges_from_blob_process(
|
|
226
|
-
self,
|
|
276
|
+
self,
|
|
277
|
+
dimension_name: str,
|
|
278
|
+
hierarchy_name: str,
|
|
279
|
+
process_name: str,
|
|
280
|
+
blob_filename: str,
|
|
281
|
+
skip_invalid_edges: bool = True,
|
|
227
282
|
) -> Process:
|
|
228
283
|
|
|
229
284
|
# v11 automatically adds blb file extensions to documents created via the contents api
|
|
@@ -251,7 +306,15 @@ class ElementService(ObjectService):
|
|
|
251
306
|
hierarchyupdate_process.add_variable(name=child_variable, variable_type="String")
|
|
252
307
|
|
|
253
308
|
# Write the statement for delete component in hierarchy
|
|
254
|
-
|
|
309
|
+
if skip_invalid_edges:
|
|
310
|
+
delete_component = (
|
|
311
|
+
f"\r"
|
|
312
|
+
f"IF(ElementIsParent('{dimension_name}','{hierarchy_name}',{parent_variable},{child_variable})=1);"
|
|
313
|
+
f"HierarchyElementComponentDelete('{dimension_name}','{hierarchy_name}',{parent_variable},{child_variable});"
|
|
314
|
+
f"ENDIF;"
|
|
315
|
+
)
|
|
316
|
+
else:
|
|
317
|
+
delete_component = f"HierarchyElementComponentDelete('{dimension_name}','{hierarchy_name}',{parent_variable},{child_variable});"
|
|
255
318
|
|
|
256
319
|
# Define Metadata section
|
|
257
320
|
metadata_statement = delete_component
|
|
@@ -7,7 +7,7 @@ from requests import Response
|
|
|
7
7
|
|
|
8
8
|
from TM1py.Exceptions.Exceptions import TM1pyRestException
|
|
9
9
|
from TM1py.Objects import View
|
|
10
|
-
from TM1py.Objects.MDXView import
|
|
10
|
+
from TM1py.Objects.MDXView import MDXView
|
|
11
11
|
from TM1py.Objects.NativeView import NativeView
|
|
12
12
|
from TM1py.Services.ObjectService import ObjectService
|
|
13
13
|
from TM1py.Services.RestService import RestService
|
|
@@ -65,11 +65,8 @@ class ViewService(ObjectService):
|
|
|
65
65
|
url = format_url("/Cubes('{}')/{}('{}')?$expand=*", cube_name, view_type, view_name)
|
|
66
66
|
response = self._rest.GET(url, **kwargs)
|
|
67
67
|
view_as_dict = response.json()
|
|
68
|
-
dynamic_properties = {k: v for k, v in view_as_dict.items() if k not in MDX_VIEW_EXCLUDED_KEYS}
|
|
69
68
|
if "MDX" in view_as_dict:
|
|
70
|
-
return MDXView(
|
|
71
|
-
cube_name=cube_name, view_name=view_name, MDX=view_as_dict["MDX"], dynamic_properties=dynamic_properties
|
|
72
|
-
)
|
|
69
|
+
return MDXView.from_dict(view_as_dict)
|
|
73
70
|
else:
|
|
74
71
|
return self.get_native_view(cube_name=cube_name, view_name=view_name, private=private)
|
|
75
72
|
|
|
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
|
|
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
|