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.
Files changed (72) hide show
  1. TM1py/Exceptions/Exceptions.py +201 -0
  2. TM1py/Exceptions/__init__.py +10 -0
  3. TM1py/Objects/Annotation.py +247 -0
  4. TM1py/Objects/Application.py +188 -0
  5. TM1py/Objects/Axis.py +119 -0
  6. TM1py/Objects/Chore.py +165 -0
  7. TM1py/Objects/ChoreFrequency.py +68 -0
  8. TM1py/Objects/ChoreStartTime.py +93 -0
  9. TM1py/Objects/ChoreTask.py +80 -0
  10. TM1py/Objects/Cube.py +116 -0
  11. TM1py/Objects/Dimension.py +128 -0
  12. TM1py/Objects/Element.py +110 -0
  13. TM1py/Objects/ElementAttribute.py +75 -0
  14. TM1py/Objects/Git.py +76 -0
  15. TM1py/Objects/GitCommit.py +27 -0
  16. TM1py/Objects/GitPlan.py +101 -0
  17. TM1py/Objects/GitProject.py +525 -0
  18. TM1py/Objects/GitRemote.py +28 -0
  19. TM1py/Objects/Hierarchy.py +343 -0
  20. TM1py/Objects/MDXView.py +84 -0
  21. TM1py/Objects/NativeView.py +331 -0
  22. TM1py/Objects/Process.py +512 -0
  23. TM1py/Objects/ProcessDebugBreakpoint.py +236 -0
  24. TM1py/Objects/Rules.py +100 -0
  25. TM1py/Objects/Sandbox.py +87 -0
  26. TM1py/Objects/Server.py +30 -0
  27. TM1py/Objects/Subset.py +295 -0
  28. TM1py/Objects/TM1Object.py +27 -0
  29. TM1py/Objects/User.py +179 -0
  30. TM1py/Objects/View.py +39 -0
  31. TM1py/Objects/__init__.py +28 -0
  32. TM1py/Services/AnnotationService.py +96 -0
  33. TM1py/Services/ApplicationService.py +898 -0
  34. TM1py/Services/AuditLogService.py +100 -0
  35. TM1py/Services/CellService.py +5465 -0
  36. TM1py/Services/ChoreService.py +299 -0
  37. TM1py/Services/ConfigurationService.py +80 -0
  38. TM1py/Services/CubeService.py +426 -0
  39. TM1py/Services/DimensionService.py +213 -0
  40. TM1py/Services/ElementService.py +1489 -0
  41. TM1py/Services/FileService.py +419 -0
  42. TM1py/Services/GitService.py +292 -0
  43. TM1py/Services/HierarchyService.py +895 -0
  44. TM1py/Services/JobService.py +56 -0
  45. TM1py/Services/LoggerService.py +97 -0
  46. TM1py/Services/ManageService.py +219 -0
  47. TM1py/Services/MessageLogService.py +161 -0
  48. TM1py/Services/MonitoringService.py +94 -0
  49. TM1py/Services/ObjectService.py +85 -0
  50. TM1py/Services/PowerBiService.py +81 -0
  51. TM1py/Services/ProcessService.py +753 -0
  52. TM1py/Services/RestService.py +1395 -0
  53. TM1py/Services/SandboxService.py +135 -0
  54. TM1py/Services/SecurityService.py +255 -0
  55. TM1py/Services/ServerService.py +292 -0
  56. TM1py/Services/SessionService.py +65 -0
  57. TM1py/Services/SubsetService.py +305 -0
  58. TM1py/Services/TM1Service.py +183 -0
  59. TM1py/Services/ThreadService.py +73 -0
  60. TM1py/Services/TransactionLogService.py +107 -0
  61. TM1py/Services/UserService.py +71 -0
  62. TM1py/Services/ViewService.py +308 -0
  63. TM1py/Services/__init__.py +33 -0
  64. TM1py/Utils/MDXUtils.py +248 -0
  65. TM1py/Utils/Utils.py +1833 -0
  66. TM1py/Utils/__init__.py +2 -0
  67. TM1py/__init__.py +84 -0
  68. tm1py-2.2.1.dist-info/METADATA +181 -0
  69. tm1py-2.2.1.dist-info/RECORD +72 -0
  70. tm1py-2.2.1.dist-info/WHEEL +5 -0
  71. tm1py-2.2.1.dist-info/licenses/LICENSE +22 -0
  72. tm1py-2.2.1.dist-info/top_level.txt +1 -0
TM1py/Objects/Axis.py ADDED
@@ -0,0 +1,119 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import collections
4
+ import json
5
+ from typing import Dict, Union
6
+
7
+ from TM1py.Objects.Subset import AnonymousSubset, Subset
8
+ from TM1py.Objects.TM1Object import TM1Object
9
+ from TM1py.Utils import format_url
10
+
11
+
12
+ class ViewAxisSelection(TM1Object):
13
+ """Describes what is selected in a dimension on an axis. Can be a Registered Subset or an Anonymous Subset"""
14
+
15
+ def __init__(self, dimension_name: str, subset: Union[Subset, AnonymousSubset]):
16
+ """
17
+ :Parameters:
18
+ `dimension_name` : String
19
+ `subset` : Subset or AnonymousSubset
20
+ """
21
+ self._subset = subset
22
+ self._dimension_name = dimension_name
23
+ self._hierarchy_name = dimension_name
24
+
25
+ @property
26
+ def subset(self) -> Union[Subset, AnonymousSubset]:
27
+ return self._subset
28
+
29
+ @property
30
+ def dimension_name(self) -> str:
31
+ return self._dimension_name
32
+
33
+ @property
34
+ def hierarchy_name(self) -> str:
35
+ return self._hierarchy_name
36
+
37
+ @property
38
+ def body(self) -> str:
39
+ return json.dumps(self._construct_body(), ensure_ascii=False)
40
+
41
+ @property
42
+ def body_as_dict(self) -> Dict:
43
+ return self._construct_body()
44
+
45
+ def _construct_body(self) -> Dict:
46
+ """construct the ODATA conform JSON represenation for the ViewAxisSelection entity.
47
+
48
+ :return: dictionary
49
+ """
50
+ body_as_dict = collections.OrderedDict()
51
+ if isinstance(self._subset, AnonymousSubset):
52
+ body_as_dict["Subset"] = json.loads(self._subset.body)
53
+ elif isinstance(self._subset, Subset):
54
+ subset_path = format_url(
55
+ "Dimensions('{}')/Hierarchies('{}')/Subsets('{}')",
56
+ self._dimension_name,
57
+ self._hierarchy_name,
58
+ self._subset.name,
59
+ )
60
+ body_as_dict["Subset@odata.bind"] = subset_path
61
+ return body_as_dict
62
+
63
+
64
+ class ViewTitleSelection:
65
+ """Describes what is selected in a dimension on the view title.
66
+ Can be a Registered Subset or an Anonymous Subset
67
+
68
+ """
69
+
70
+ def __init__(self, dimension_name: str, subset: Union[AnonymousSubset, Subset], selected: str):
71
+ self._dimension_name = dimension_name
72
+ self._hierarchy_name = dimension_name
73
+ self._subset = subset
74
+ self._selected = selected
75
+
76
+ @property
77
+ def subset(self) -> Union[Subset, AnonymousSubset]:
78
+ return self._subset
79
+
80
+ @property
81
+ def dimension_name(self) -> str:
82
+ return self._dimension_name
83
+
84
+ @property
85
+ def hierarchy_name(self) -> str:
86
+ return self._hierarchy_name
87
+
88
+ @property
89
+ def selected(self) -> str:
90
+ return self._selected
91
+
92
+ @property
93
+ def body(self) -> str:
94
+ return json.dumps(self._construct_body(), ensure_ascii=False)
95
+
96
+ def _construct_body(self) -> Dict:
97
+ """construct the ODATA conform JSON represenation for the ViewTitleSelection entity.
98
+
99
+ :return: string, the valid JSON
100
+ """
101
+ body_as_dict = collections.OrderedDict()
102
+ if isinstance(self._subset, AnonymousSubset):
103
+ body_as_dict["Subset"] = json.loads(self._subset.body)
104
+ elif isinstance(self._subset, Subset):
105
+ subset_path = format_url(
106
+ "Dimensions('{}')/Hierarchies('{}')/Subsets('{}')",
107
+ self._dimension_name,
108
+ self._hierarchy_name,
109
+ self._subset.name,
110
+ )
111
+ body_as_dict["Subset@odata.bind"] = subset_path
112
+ element_path = format_url(
113
+ "Dimensions('{}')/Hierarchies('{}')/Elements('{}')",
114
+ self._dimension_name,
115
+ self._hierarchy_name,
116
+ self._selected,
117
+ )
118
+ body_as_dict["Selected@odata.bind"] = element_path
119
+ return body_as_dict
TM1py/Objects/Chore.py ADDED
@@ -0,0 +1,165 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import collections
4
+ import json
5
+ from typing import Dict, Iterable, List
6
+
7
+ from TM1py.Objects.ChoreFrequency import ChoreFrequency
8
+ from TM1py.Objects.ChoreStartTime import ChoreStartTime
9
+ from TM1py.Objects.ChoreTask import ChoreTask
10
+ from TM1py.Objects.TM1Object import TM1Object
11
+
12
+
13
+ class Chore(TM1Object):
14
+ """Abstraction of TM1 Chore"""
15
+
16
+ SINGLE_COMMIT = "SingleCommit"
17
+ MULTIPLE_COMMIT = "MultipleCommit"
18
+
19
+ def __init__(
20
+ self,
21
+ name: str,
22
+ start_time: ChoreStartTime,
23
+ dst_sensitivity: bool,
24
+ active: bool,
25
+ execution_mode: str,
26
+ frequency: ChoreFrequency,
27
+ tasks: Iterable[ChoreTask],
28
+ ):
29
+ self._name = name
30
+ self._start_time = start_time
31
+ self._dst_sensitivity = dst_sensitivity
32
+ self._active = active
33
+ self._execution_mode = execution_mode
34
+ self._frequency = frequency
35
+ self._tasks = list(tasks)
36
+
37
+ @classmethod
38
+ def from_json(cls, chore_as_json: str) -> "Chore":
39
+ """Alternative constructor
40
+
41
+ :param chore_as_json: string, JSON. Response of /Chores('x')/Tasks?$expand=*
42
+ :return: Chore, an instance of this class
43
+ """
44
+ chore_as_dict = json.loads(chore_as_json)
45
+ return cls.from_dict(chore_as_dict)
46
+
47
+ @classmethod
48
+ def from_dict(cls, chore_as_dict: Dict) -> "Chore":
49
+ """Alternative constructor
50
+
51
+ :param chore_as_dict: Chore as dict
52
+ :return: Chore, an instance of this class
53
+ """
54
+ return cls(
55
+ name=chore_as_dict["Name"],
56
+ start_time=ChoreStartTime.from_string(chore_as_dict["StartTime"]),
57
+ dst_sensitivity=chore_as_dict["DSTSensitive"],
58
+ active=chore_as_dict["Active"],
59
+ execution_mode=chore_as_dict["ExecutionMode"],
60
+ frequency=ChoreFrequency.from_string(chore_as_dict["Frequency"]),
61
+ tasks=[ChoreTask.from_dict(task, step) for step, task in enumerate(chore_as_dict["Tasks"])],
62
+ )
63
+
64
+ @property
65
+ def name(self) -> str:
66
+ return self._name
67
+
68
+ @name.setter
69
+ def name(self, name: str):
70
+ self._name = name
71
+
72
+ @property
73
+ def start_time(self) -> ChoreStartTime:
74
+ return self._start_time
75
+
76
+ @start_time.setter
77
+ def start_time(self, start_time: ChoreStartTime):
78
+ self._start_time = start_time
79
+
80
+ @property
81
+ def dst_sensitivity(self) -> bool:
82
+ return self._dst_sensitivity
83
+
84
+ @dst_sensitivity.setter
85
+ def dst_sensitivity(self, dst_sensitivity: bool):
86
+ self._dst_sensitivity = dst_sensitivity
87
+
88
+ @property
89
+ def active(self) -> bool:
90
+ return self._active
91
+
92
+ @property
93
+ def execution_mode(self) -> str:
94
+ return self._execution_mode
95
+
96
+ @execution_mode.setter
97
+ def execution_mode(self, execution_mode):
98
+ self._execution_mode = execution_mode
99
+
100
+ @property
101
+ def frequency(self) -> ChoreFrequency:
102
+ return self._frequency
103
+
104
+ @frequency.setter
105
+ def frequency(self, frequency: ChoreFrequency):
106
+ self._frequency = frequency
107
+
108
+ @property
109
+ def tasks(self) -> List[ChoreTask]:
110
+ return self._tasks
111
+
112
+ @tasks.setter
113
+ def tasks(self, tasks: List[ChoreTask]):
114
+ self._tasks = tasks
115
+
116
+ @property
117
+ def body(self) -> str:
118
+ return self.construct_body()
119
+
120
+ @property
121
+ def body_as_dict(self) -> Dict:
122
+ return json.loads(self.body)
123
+
124
+ @property
125
+ def execution_path(self) -> Dict:
126
+ """
127
+ 1 chore together with its executed processes
128
+ Use case: building out a tree of chores and their processes (and again the processes that are called by the latter (if any)).
129
+ :return: dictionary containing chore name as the key and a list of process names as the value
130
+ """
131
+ return {self.name: [task.process_name for task in self.tasks]}
132
+
133
+ def add_task(self, task: ChoreTask):
134
+ self._tasks.append(task)
135
+
136
+ def insert_task(self, new_task: ChoreTask):
137
+ task_list = self.tasks
138
+ for task in task_list[new_task._step :]:
139
+ task._step = task._step + 1
140
+ task_list.insert(new_task._step, new_task)
141
+ self.tasks = task_list
142
+
143
+ def activate(self):
144
+ self._active = True
145
+
146
+ def deactivate(self):
147
+ self._active = False
148
+
149
+ def reschedule(self, days: int = 0, hours: int = 0, minutes: int = 0, seconds: int = 0):
150
+ self._start_time.add(days=days, hours=hours, minutes=minutes, seconds=seconds)
151
+
152
+ def construct_body(self) -> str:
153
+ """
154
+ construct self.body (json) from the class attributes
155
+ :return: String, TM1 JSON representation of a chore
156
+ """
157
+ body_as_dict = collections.OrderedDict()
158
+ body_as_dict["Name"] = self._name
159
+ body_as_dict["StartTime"] = self._start_time.start_time_string
160
+ body_as_dict["DSTSensitive"] = self._dst_sensitivity
161
+ body_as_dict["Active"] = self._active
162
+ body_as_dict["ExecutionMode"] = self._execution_mode
163
+ body_as_dict["Frequency"] = self._frequency.frequency_string
164
+ body_as_dict["Tasks"] = [task.body_as_dict for task in self._tasks]
165
+ return json.dumps(body_as_dict, ensure_ascii=False)
@@ -0,0 +1,68 @@
1
+ # -*- coding: utf-8 -*-
2
+ from typing import Union
3
+
4
+ from TM1py.Objects.TM1Object import TM1Object
5
+
6
+
7
+ class ChoreFrequency(TM1Object):
8
+ """Utility class to handle time representation fore Chore Frequency"""
9
+
10
+ def __init__(
11
+ self, days: Union[str, int], hours: Union[str, int], minutes: Union[str, int], seconds: Union[str, int]
12
+ ):
13
+ self._days = str(days).zfill(2)
14
+ self._hours = str(hours).zfill(2)
15
+ self._minutes = str(minutes).zfill(2)
16
+ self._seconds = str(seconds).zfill(2)
17
+
18
+ @property
19
+ def days(self) -> str:
20
+ return self._days
21
+
22
+ @property
23
+ def hours(self) -> str:
24
+ return self._hours
25
+
26
+ @property
27
+ def minutes(self) -> str:
28
+ return self._minutes
29
+
30
+ @property
31
+ def seconds(self) -> str:
32
+ return self._seconds
33
+
34
+ @days.setter
35
+ def days(self, value: Union[str, int]):
36
+ self._days = str(value).zfill(2)
37
+
38
+ @hours.setter
39
+ def hours(self, value: Union[str, int]):
40
+ self._hours = str(value).zfill(2)
41
+
42
+ @minutes.setter
43
+ def minutes(self, value: Union[str, int]):
44
+ self._minutes = str(value).zfill(2)
45
+
46
+ @seconds.setter
47
+ def seconds(self, value: Union[str, int]):
48
+ self._seconds = str(value).zfill(2)
49
+
50
+ @classmethod
51
+ def from_string(cls, frequency_string: str) -> "ChoreFrequency":
52
+ pos_dt = frequency_string.find("DT", 1)
53
+ pos_h = frequency_string.find("H", pos_dt)
54
+ pos_m = frequency_string.find("M", pos_h)
55
+ pos_s = len(frequency_string) - 1
56
+ return cls(
57
+ days=frequency_string[1:pos_dt],
58
+ hours=frequency_string[pos_dt + 2 : pos_h],
59
+ minutes=frequency_string[pos_h + 1 : pos_m],
60
+ seconds=frequency_string[pos_m + 1 : pos_s],
61
+ )
62
+
63
+ @property
64
+ def frequency_string(self) -> str:
65
+ return "P{}DT{}H{}M{}S".format(self._days, self._hours, self._minutes, self._seconds)
66
+
67
+ def __str__(self) -> str:
68
+ return self.frequency_string
@@ -0,0 +1,93 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import datetime
4
+
5
+
6
+ class ChoreStartTime:
7
+ """Utility class to handle time representation for Chore Start Time"""
8
+
9
+ def __init__(self, year: int, month: int, day: int, hour: int, minute: int, second: int, tz: str = None):
10
+ """
11
+
12
+ :param year: year
13
+ :param month: month
14
+ :param day: day
15
+ :param hour: hour or None
16
+ :param minute: minute or None
17
+ :param second: second or None
18
+ """
19
+ self._datetime = datetime.datetime.combine(datetime.date(year, month, day), datetime.time(hour, minute, second))
20
+ self.tz = tz
21
+
22
+ @classmethod
23
+ def from_string(cls, start_time_string: str) -> "ChoreStartTime":
24
+ # extract optional tz info (e.g., +01:00) from string end
25
+ if "+" in start_time_string:
26
+ # case "2020-11-05T08:00:01+01:00",
27
+ tz = "+" + start_time_string.split("+")[1]
28
+ elif start_time_string.count("-") == 3:
29
+ # case: "2020-11-05T08:00:01-01:00",
30
+ tz = "-" + start_time_string.split("-")[-1]
31
+ else:
32
+ tz = None
33
+
34
+ # f to handle strange timestamp 2016-09-25T20:25Z instead of common 2016-09-25T20:25:00Z
35
+ # second is defaulted to 0 if not specified in the chore schedule
36
+ def format_time(value: int) -> str:
37
+ return int(value or 0)
38
+
39
+ return cls(
40
+ year=format_time(start_time_string[0:4]),
41
+ month=format_time(start_time_string[5:7]),
42
+ day=format_time(start_time_string[8:10]),
43
+ hour=format_time(start_time_string[11:13]),
44
+ minute=format_time(start_time_string[14:16]),
45
+ second=format_time(0 if start_time_string[16] != ":" else start_time_string[17:19]),
46
+ tz=tz,
47
+ )
48
+
49
+ @property
50
+ def start_time_string(self) -> str:
51
+ # produce timestamp 2016-09-25T20:25:00Z instead of common 2016-09-25T20:25Z where no seconds are specified
52
+ start_time = self._datetime.strftime("%Y-%m-%dT%H:%M:%S")
53
+
54
+ if self.tz:
55
+ start_time += self.tz
56
+ else:
57
+ start_time += "Z"
58
+
59
+ return start_time
60
+
61
+ @property
62
+ def datetime(self) -> datetime:
63
+ return self._datetime
64
+
65
+ def __str__(self):
66
+ return self.start_time_string
67
+
68
+ def set_time(
69
+ self,
70
+ year: int = None,
71
+ month: int = None,
72
+ day: int = None,
73
+ hour: int = None,
74
+ minute: int = None,
75
+ second: int = None,
76
+ ):
77
+
78
+ _year = year if year is not None else self._datetime.year
79
+ _month = month if month is not None else self._datetime.month
80
+ _day = day if day is not None else self._datetime.day
81
+ _hour = hour if hour is not None else self._datetime.hour
82
+ _minute = minute if minute is not None else self._datetime.minute
83
+ _second = second if second is not None else self._datetime.second
84
+
85
+ self._datetime = self._datetime.replace(
86
+ year=_year, month=_month, day=_day, hour=_hour, minute=_minute, second=_second
87
+ )
88
+
89
+ def add(self, days: int = 0, hours: int = 0, minutes: int = 0, seconds: int = 0):
90
+ self._datetime = self._datetime + datetime.timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds)
91
+
92
+ def subtract(self, days: int = 0, hours: int = 0, minutes: int = 0, seconds: int = 0):
93
+ self._datetime = self._datetime - datetime.timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds)
@@ -0,0 +1,80 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import collections
4
+ import json
5
+ from typing import Dict, List
6
+
7
+ from TM1py.Objects.TM1Object import TM1Object
8
+ from TM1py.Utils import format_url
9
+
10
+
11
+ class ChoreTask(TM1Object):
12
+ """Abstraction of a Chore Task
13
+
14
+ A Chore task always conistst of
15
+ - The step integer ID: it's order in the execution plan.
16
+ 1 to n, where n is the last Process in the Chore
17
+ - The name of the process to execute
18
+ - The parameters for the process
19
+
20
+ """
21
+
22
+ def __init__(self, step: int, process_name: str, parameters: List[Dict[str, str]]):
23
+ """
24
+
25
+ :param step: step in the execution order of the Chores' processes. 1 to n, where n the number of processes
26
+ :param process_name: name of the process
27
+ :param parameters: list of dictionaries with 'Name' and 'Value' property:
28
+ [{
29
+ 'Name': '..',
30
+ 'Value': '..'
31
+ },
32
+ ...
33
+ ]
34
+ """
35
+ self._step = step
36
+ self._process_name = process_name
37
+ self._parameters = parameters
38
+
39
+ @classmethod
40
+ def from_dict(cls, chore_task_as_dict: Dict, step: int = None):
41
+ if "Process" in chore_task_as_dict:
42
+ process_name = chore_task_as_dict["Process"]["Name"]
43
+ else:
44
+ # Extract "ProcessName" from "Processes('ProcessName')"
45
+ process_name = chore_task_as_dict["Process@odata.bind"][11:-2]
46
+
47
+ return cls(
48
+ step=step if step is not None else int(chore_task_as_dict["Step"]),
49
+ process_name=process_name,
50
+ parameters=[{"Name": p["Name"], "Value": p["Value"]} for p in chore_task_as_dict["Parameters"]],
51
+ )
52
+
53
+ @property
54
+ def body_as_dict(self) -> Dict:
55
+ body_as_dict = collections.OrderedDict()
56
+ body_as_dict["Process@odata.bind"] = format_url("Processes('{}')", self._process_name)
57
+ body_as_dict["Parameters"] = self._parameters
58
+ return body_as_dict
59
+
60
+ @property
61
+ def step(self) -> int:
62
+ return self._step
63
+
64
+ @property
65
+ def process_name(self) -> str:
66
+ return self._process_name
67
+
68
+ @property
69
+ def parameters(self) -> List[Dict[str, str]]:
70
+ return self._parameters
71
+
72
+ @property
73
+ def body(self) -> str:
74
+ return json.dumps(self.body_as_dict, ensure_ascii=False)
75
+
76
+ def __eq__(self, other: "ChoreTask") -> bool:
77
+ return self.process_name == other.process_name and self.parameters == other.parameters
78
+
79
+ def __ne__(self, other: "ChoreTask") -> bool:
80
+ return self.process_name != other.process_name or self._parameters != other.parameters
TM1py/Objects/Cube.py ADDED
@@ -0,0 +1,116 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import collections
4
+ import json
5
+ from typing import Dict, Iterable, List, Optional, Union
6
+
7
+ from TM1py.Objects.Rules import Rules
8
+ from TM1py.Objects.TM1Object import TM1Object
9
+ from TM1py.Utils import format_url
10
+
11
+
12
+ class Cube(TM1Object):
13
+ """Abstraction of a TM1 Cube"""
14
+
15
+ def __init__(self, name: str, dimensions: Iterable[str], rules: Optional[Union[str, Rules]] = None):
16
+ """
17
+
18
+ :param name: name of the Cube
19
+ :param dimensions: list of (existing) dimension names
20
+ :param rules: instance of TM1py.Objects.Rules
21
+ """
22
+ self._name = name
23
+ self.dimensions = list(dimensions)
24
+ self.rules = rules
25
+
26
+ @property
27
+ def name(self) -> str:
28
+ return self._name
29
+
30
+ @property
31
+ def dimensions(self) -> List[str]:
32
+ return self._dimensions
33
+
34
+ @dimensions.setter
35
+ def dimensions(self, value: List[str]):
36
+ self._dimensions = value
37
+
38
+ @property
39
+ def has_rules(self) -> bool:
40
+ if self._rules:
41
+ return True
42
+ return False
43
+
44
+ @property
45
+ def rules(self) -> Rules:
46
+ return self._rules
47
+
48
+ @rules.setter
49
+ def rules(self, value: Union[str, Rules]):
50
+ if value is None:
51
+ self._rules = None
52
+ elif isinstance(value, str):
53
+ self._rules = Rules(rules=value)
54
+ elif isinstance(value, Rules):
55
+ self._rules = value
56
+ else:
57
+ raise ValueError("value must None or of type str or Rules")
58
+
59
+ @property
60
+ def skipcheck(self) -> bool:
61
+ if self.has_rules:
62
+ return self.rules.skipcheck
63
+ return False
64
+
65
+ @property
66
+ def undefvals(self) -> bool:
67
+ if self.has_rules:
68
+ return self.rules.undefvals
69
+ return False
70
+
71
+ @property
72
+ def feedstrings(self) -> bool:
73
+ if self.has_rules:
74
+ return self.rules.feedstrings
75
+ return False
76
+
77
+ @classmethod
78
+ def from_json(cls, cube_as_json: str) -> "Cube":
79
+ """Alternative constructor
80
+
81
+ :param cube_as_json: user as JSON string
82
+ :return: cube, an instance of this class
83
+ """
84
+ cube_as_dict = json.loads(cube_as_json)
85
+ return cls.from_dict(cube_as_dict)
86
+
87
+ @classmethod
88
+ def from_dict(cls, cube_as_dict: Dict) -> "Cube":
89
+ """Alternative constructor
90
+
91
+ :param cube_as_dict: user as dict
92
+ :return: user, an instance of this class
93
+ """
94
+ return cls(
95
+ name=cube_as_dict["Name"],
96
+ dimensions=[dimension["Name"] for dimension in cube_as_dict["Dimensions"]],
97
+ rules=Rules(cube_as_dict["Rules"]) if cube_as_dict["Rules"] else None,
98
+ )
99
+
100
+ @property
101
+ def body(self) -> str:
102
+ return self._construct_body()
103
+
104
+ def _construct_body(self) -> str:
105
+ """
106
+ construct body (json) from the class attributes
107
+ :return: String, TM1 JSON representation of a cube
108
+ """
109
+ body_as_dict = collections.OrderedDict()
110
+ body_as_dict["Name"] = self.name
111
+ body_as_dict["Dimensions@odata.bind"] = [
112
+ format_url("Dimensions('{}')", dimension) for dimension in self.dimensions
113
+ ]
114
+ if self.has_rules:
115
+ body_as_dict["Rules"] = str(self.rules)
116
+ return json.dumps(body_as_dict, ensure_ascii=False)