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.
Files changed (79) hide show
  1. {tm1py-2.2.4 → tm1py-2.2.5}/PKG-INFO +1 -1
  2. tm1py-2.2.5/TM1py/Objects/DynamicPropertiesMixin.py +29 -0
  3. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/MDXView.py +18 -25
  4. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/NativeView.py +40 -27
  5. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ElementService.py +74 -11
  6. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ViewService.py +2 -5
  7. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py.egg-info/PKG-INFO +1 -1
  8. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py.egg-info/SOURCES.txt +1 -0
  9. {tm1py-2.2.4 → tm1py-2.2.5}/pyproject.toml +1 -1
  10. {tm1py-2.2.4 → tm1py-2.2.5}/LICENSE +0 -0
  11. {tm1py-2.2.4 → tm1py-2.2.5}/README.md +0 -0
  12. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Exceptions/Exceptions.py +0 -0
  13. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Exceptions/__init__.py +0 -0
  14. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Annotation.py +0 -0
  15. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Application.py +0 -0
  16. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Axis.py +0 -0
  17. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Chore.py +0 -0
  18. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/ChoreFrequency.py +0 -0
  19. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/ChoreStartTime.py +0 -0
  20. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/ChoreTask.py +0 -0
  21. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Cube.py +0 -0
  22. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Dimension.py +0 -0
  23. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Element.py +0 -0
  24. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/ElementAttribute.py +0 -0
  25. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Git.py +0 -0
  26. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/GitCommit.py +0 -0
  27. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/GitPlan.py +0 -0
  28. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/GitProject.py +0 -0
  29. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/GitRemote.py +0 -0
  30. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Hierarchy.py +0 -0
  31. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Process.py +0 -0
  32. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/ProcessDebugBreakpoint.py +0 -0
  33. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Rules.py +0 -0
  34. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Sandbox.py +0 -0
  35. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Server.py +0 -0
  36. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/Subset.py +0 -0
  37. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/TM1Object.py +0 -0
  38. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/User.py +0 -0
  39. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/View.py +0 -0
  40. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Objects/__init__.py +0 -0
  41. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/AnnotationService.py +0 -0
  42. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ApplicationService.py +0 -0
  43. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/AuditLogService.py +0 -0
  44. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/CellService.py +0 -0
  45. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ChoreService.py +0 -0
  46. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ConfigurationService.py +0 -0
  47. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/CubeService.py +0 -0
  48. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/DimensionService.py +0 -0
  49. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/FileService.py +0 -0
  50. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/GitService.py +0 -0
  51. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/HierarchyService.py +0 -0
  52. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/JobService.py +0 -0
  53. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/LoggerService.py +0 -0
  54. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ManageService.py +0 -0
  55. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/MessageLogService.py +0 -0
  56. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/MonitoringService.py +0 -0
  57. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ObjectService.py +0 -0
  58. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/PowerBiService.py +0 -0
  59. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ProcessService.py +0 -0
  60. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/RestService.py +0 -0
  61. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/SandboxService.py +0 -0
  62. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/SecurityService.py +0 -0
  63. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ServerService.py +0 -0
  64. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/SessionService.py +0 -0
  65. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/SubsetService.py +0 -0
  66. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/TM1Service.py +0 -0
  67. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/ThreadService.py +0 -0
  68. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/TransactionLogService.py +0 -0
  69. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/UserService.py +0 -0
  70. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Services/__init__.py +0 -0
  71. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Utils/MDXUtils.py +0 -0
  72. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Utils/Utils.py +0 -0
  73. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/Utils/__init__.py +0 -0
  74. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py/__init__.py +0 -0
  75. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py.egg-info/dependency_links.txt +0 -0
  76. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py.egg-info/requires.txt +0 -0
  77. {tm1py-2.2.4 → tm1py-2.2.5}/TM1py.egg-info/top_level.txt +0 -0
  78. {tm1py-2.2.4 → tm1py-2.2.5}/setup.cfg +0 -0
  79. {tm1py-2.2.4 → tm1py-2.2.5}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TM1py
3
- Version: 2.2.4
3
+ Version: 2.2.5
4
4
  Summary: A python module for TM1.
5
5
  Author-email: Marius Wirtz <MWirtz@cubewise.com>
6
6
  License: MIT
@@ -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
- MDX_VIEW_EXCLUDED_KEYS = frozenset(
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={k: v for k, v in view_as_dict.items() if k not in MDX_VIEW_EXCLUDED_KEYS},
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({k: v for k, v in self._dynamic_properties.items() if k not in MDX_VIEW_EXCLUDED_KEYS})
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
- top_json = '{"@odata.type": "ibm.tm1.api.v1.NativeView","Name": "' + self._name + '",'
307
- columns_json = ",".join([column.body for column in self._columns])
308
- rows_json = ",".join([row.body for row in self._rows])
309
- titles_json = ",".join([title.body for title in self._titles])
310
- bottom_json = (
311
- '"SuppressEmptyColumns": '
312
- + str(self._suppress_empty_columns).lower()
313
- + ',"SuppressEmptyRows":'
314
- + str(self._suppress_empty_rows).lower()
315
- + ',"FormatString": "'
316
- + self._format_string
317
- + '"}'
318
- )
319
- return "".join(
320
- [
321
- top_json,
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(dimension_name, hierarchy_name, edges, remove_blob, **kwargs)
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(self, dimension_name: str, hierarchy_name: str, edges: List[str] = None, **kwargs):
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
- f"HierarchyElementComponentDelete('{dimension_name}', '{hierarchy_name}', "
162
- f"'{escape_single_quote(parent)}', '{escape_single_quote(child)}');"
163
- for (parent, child) in edges
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, dimension_name: str, hierarchy_name: str, edges: List[str] = None, remove_blob: bool = True, **kwargs
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, dimension_name: str, hierarchy_name: str, process_name: str, blob_filename: str
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
- delete_component = f"\rHierarchyElementComponentDelete('{dimension_name}', '{hierarchy_name}', {parent_variable}, {child_variable});"
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 MDX_VIEW_EXCLUDED_KEYS, MDXView
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TM1py
3
- Version: 2.2.4
3
+ Version: 2.2.5
4
4
  Summary: A python module for TM1.
5
5
  Author-email: Marius Wirtz <MWirtz@cubewise.com>
6
6
  License: MIT
@@ -19,6 +19,7 @@ TM1py/Objects/ChoreStartTime.py
19
19
  TM1py/Objects/ChoreTask.py
20
20
  TM1py/Objects/Cube.py
21
21
  TM1py/Objects/Dimension.py
22
+ TM1py/Objects/DynamicPropertiesMixin.py
22
23
  TM1py/Objects/Element.py
23
24
  TM1py/Objects/ElementAttribute.py
24
25
  TM1py/Objects/Git.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "TM1py"
7
- version = "2.2.4"
7
+ version = "2.2.5"
8
8
  description = "A python module for TM1."
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
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