def get(self, cube_name: str, **kwargs) -> Cube: """ get cube from TM1 Server :param cube_name: :return: instance of TM1py.Cube """ url = format_url( "/api/v1/Cubes('{}')?$expand=Dimensions($select=Name)", cube_name) response = self._rest.GET(url=url, **kwargs) cube = Cube.from_json(response.text) return cube
def determine_actual_object_name(self, object_class: str, object_name: str, **kwargs) -> str: url = format_url( "/api/v1/{}?$filter=tolower(replace(Name, ' ', '')) eq '{}'", object_class, object_name.replace(" ", "").lower()) response = self._rest.GET(url, **kwargs) if len(response.json()["value"]) == 0: raise ValueError("Object '{}' of type '{}' doesn't exist".format(object_name, object_class)) return response.json()["value"][0]["Name"]
def _get_task(self, chore_name: str, step: int, **kwargs) -> ChoreTask: """ Get task from chore :param chore_name: name of the chore :param step: :return: instance of TM1py.ChoreTask """ url = format_url( "/api/v1/Chores('{}')/Tasks({})?$expand=*,Process($select=Name),Chore($select=Name)", chore_name, str(step)) response = self._rest.GET(url, **kwargs) return ChoreTask.from_dict(response.json())
def _construct_body_dynamic(self) -> Dict: body_as_dict = collections.OrderedDict() body_as_dict['Name'] = self._subset_name if self.alias: body_as_dict['Alias'] = self._alias body_as_dict['*****@*****.**'] = format_url( "Dimensions('{}')/Hierarchies('{}')", self._dimension_name, self._hierarchy_name) body_as_dict['Expression'] = self._expression return body_as_dict
def check_rules(self, cube_name: str, **kwargs) -> Response: """ Check rules syntax for existing cube on TM1 Server :param cube_name: name of a cube :return: response """ url = format_url("/api/v1/Cubes('{}')/tm1.CheckRules", cube_name) response = self._rest.POST(url, **kwargs) errors = response.json()["value"] return errors
def get_message_log_entries(self, reverse: bool = True, since: datetime = None, until: datetime = None, top: int = None, **kwargs) -> Dict: """ :param reverse: Boolean :param since: of type datetime. If it doesn't have tz information, UTC is assumed. :param until: of type datetime. If it doesn't have tz information, UTC is assumed. :param top: Integer :param kwargs: :return: Dict of server log """ reverse = 'desc' if reverse else 'asc' url = '/api/v1/MessageLogEntries?$orderby=TimeStamp {}'.format(reverse) if since or until: time_filters = [] if since: # If since doesn't have tz information, UTC is assumed if not since.tzinfo: since = self.utc_localize_time(since) time_filters.append( format_url("TimeStamp ge {}", since.strftime("%Y-%m-%dT%H:%M:%SZ"))) if until: # If until doesn't have tz information, UTC is assumed if not until.tzinfo: until = self.utc_localize_time(until) time_filters.append( format_url("TimeStamp le {}", until.strftime("%Y-%m-%dT%H:%M:%SZ"))) url += "&$filter={}".format(" and ".join(time_filters)) if top: url += '&$top={}'.format(top) response = self._rest.GET(url, **kwargs) return response.json()['value']
def get_storage_dimension_order(self, cube_name: str, **kwargs) -> List[str]: """ Get the storage dimension order of a cube :param cube_name: :return: List of dimension names """ url = format_url( "/api/v1/Cubes('{}')/tm1.DimensionsStorageOrder()?$select=Name", cube_name) response = self._rest.GET(url, **kwargs) return [dimension["Name"] for dimension in response.json()["value"]]
def add_elements(self, dimension_name: str, hierarchy_name: str, elements: List[Element], **kwargs): """ Add elements to hierarchy. Fails if one element already exists. :param dimension_name: :param hierarchy_name: :param elements: :return: """ url = format_url("/api/v1/Dimensions('{}')/Hierarchies('{}')/Elements", dimension_name, hierarchy_name) body = [element.body_as_dict for element in elements] return self._rest.POST(url=url, data=json.dumps(body), **kwargs)
def get(self, cube_name: str, **kwargs) -> Cube: """ get cube from TM1 Server :param cube_name: :return: instance of TM1py.Cube """ url = format_url("/api/v1/Cubes('{}')?$expand=Dimensions($select=Name)", cube_name) response = self._rest.GET(url=url, **kwargs) cube = Cube.from_json(response.text) # cater for potential EnableSandboxDimension=T setup if case_and_space_insensitive_equals(cube.dimensions[0], "Sandboxes"): cube.dimensions = cube.dimensions[1:] return cube
def get_dimension_names(self, cube_name: str, skip_sandbox_dimension: bool = True, **kwargs) -> List[str]: """ get name of the dimensions of a cube in their correct order :param cube_name: :param skip_sandbox_dimension: :return: List : [dim1, dim2, dim3, etc.] """ url = format_url("/api/v1/Cubes('{}')/Dimensions?$select=Name", cube_name) response = self._rest.GET(url, **kwargs) dimension_names = [element['Name'] for element in response.json()['value']] if skip_sandbox_dimension and dimension_names[0] == CellService.SANDBOX_DIMENSION: return dimension_names[1:] return dimension_names
def update(self, process: Process, **kwargs) -> Response: """ Update an existing Process on TM1 Server :param process: Instance of TM1py.Process class :return: Response """ url = format_url("/api/v1/Processes('{}')", process.name) # Adjust process body if TM1 version is lower than 11 due to change in Process Parameters structure # https://www.ibm.com/developerworks/community/forums/html/topic?id=9188d139-8905-4895-9229-eaaf0e7fa683 if int(self.version[0:2]) < 11: process.drop_parameter_types() response = self._rest.PATCH(url, process.body, **kwargs) return response
def get_transaction_log_entries(self, reverse: bool = True, user: str = None, cube: str = None, since: datetime = None, top: int = None, **kwargs) -> Dict: """ :param reverse: :param user: :param cube: :param since: of type datetime. If it doesn't have tz information, UTC is assumed. :param top: :return: """ reverse = 'desc' if reverse else 'asc' url = '/api/v1/TransactionLogEntries?$orderby=TimeStamp {} '.format( reverse) # filter on user, cube and time if user or cube or since: log_filters = [] if user: log_filters.append(format_url("User eq '{}'", user)) if cube: log_filters.append(format_url("Cube eq '{}'", cube)) if since: # If since doesn't have tz information, UTC is assumed if not since.tzinfo: since = self.utc_localize_time(since) log_filters.append( format_url("TimeStamp ge {}", since.strftime("%Y-%m-%dT%H:%M:%SZ"))) url += "&$filter={}".format(" and ".join(log_filters)) # top limit if top: url += '&$top={}'.format(top) response = self._rest.GET(url, **kwargs) return response.json()['value']
def get_element_attribute_names(self, dimension_name: str, hierarchy_name: str, **kwargs) -> List[str]: """ Get element attributes from hierarchy :param dimension_name: :param hierarchy_name: :return: """ url = format_url( "/api/v1/Dimensions('{}')/Hierarchies('{}')/ElementAttributes?$select=Name", dimension_name, hierarchy_name) response = self._rest.GET(url, **kwargs) return [ea["Name"] for ea in response.json()['value']]
def get_element_types_from_all_hierarchies( self, dimension_name: str, skip_consolidations: bool = False, **kwargs) -> CaseAndSpaceInsensitiveDict: url = format_url( "/api/v1/Dimensions('{}')?$expand=Hierarchies($select=Elements;$expand=Elements($select=Name,Type{}", dimension_name, ";$filter=Type ne 3))" if skip_consolidations else "))") response = self._rest.GET(url, **kwargs) result = CaseAndSpaceInsensitiveDict() for hierarchy in response.json()["Hierarchies"]: for element in hierarchy["Elements"]: result[element['Name']] = element["Type"] return result
def delete(self, cube_name: str, view_name: str, private: bool = False, **kwargs) -> Response: """ Delete an existing view (MDXView or NativeView) on the TM1 Server :param cube_name: String, name of the cube :param view_name: String, name of the view :param private: Boolean :return: String, the response """ view_type = 'PrivateViews' if private else 'Views' url = format_url("/api/v1/Cubes('{}')/{}('{}')", cube_name, view_type, view_name) response = self._rest.DELETE(url, **kwargs) return response
def get_element_types(self, dimension_name: str, hierarchy_name: str, skip_consolidations: bool = False, **kwargs) -> CaseAndSpaceInsensitiveDict: url = format_url( "/api/v1/Dimensions('{}')/Hierarchies('{}')/Elements?$select=Name,Type{}", dimension_name, hierarchy_name, "&$filter=Type ne 3" if skip_consolidations else "") response = self._rest.GET(url, **kwargs) result = CaseAndSpaceInsensitiveDict() for element in response.json()["value"]: result[element['Name']] = element["Type"] return result
def get_element_names(self, dimension_name: str, hierarchy_name: str, **kwargs) -> List[str]: """ Get all element names :param dimension_name: :param hierarchy_name: :return: Generator of element-names """ url = format_url( "/api/v1/Dimensions('{}')/Hierarchies('{}')/Elements?$select=Name", dimension_name, hierarchy_name) response = self._rest.GET(url, **kwargs) return [e["Name"] for e in response.json()['value']]
def get_document(self, path: str, name: str, private: bool = False, **kwargs) -> DocumentApplication: """ Get Excel Application from TM1 Server in binary format. Can be dumped to file. :param path: path through folder structure to application. For instance: "Finance/P&L.xlsx" :param name: name of the application :param private: boolean :return: Return DocumentApplication """ if not name.endswith(ApplicationTypes.DOCUMENT.suffix): name += ApplicationTypes.DOCUMENT.suffix contents = 'PrivateContents' if private else 'Contents' mid = "".join([ format_url("/Contents('{}')", element) for element in path.split('/') ]) url = format_url("/api/v1/Contents('Applications')" + mid + "/" + contents + "('{name}')/Document/Content", name=name) content = self._rest.GET(url, **kwargs).content url = format_url("/api/v1/Contents('Applications')" + mid + "/" + contents + "('{name}')/Document", name=name) document_fields = self._rest.GET(url, **kwargs).json() return DocumentApplication( path=path, name=name, content=content, file_id=document_fields.get("ID"), file_name=document_fields.get("Name"), last_updated=document_fields.get("LastUpdated"))
def get_all_names(self, skip_control_cubes: bool = False, **kwargs) -> List[str]: """ Ask TM1 Server for list of all cube names :skip_control_cubes: bool, True will exclude control cubes from list :return: List of Strings """ url = format_url( "/api/v1/{}?$select=Name", 'ModelCubes()' if skip_control_cubes else 'Cubes' ) response = self._rest.GET(url, **kwargs) cubes = list(entry['Name'] for entry in response.json()['value']) return cubes
def create_element_attribute(self, dimension_name: str, hierarchy_name: str, element_attribute: ElementAttribute, **kwargs) -> Response: """ like AttrInsert :param dimension_name: :param hierarchy_name: :param element_attribute: instance of TM1py.ElementAttribute :return: """ url = format_url( "/api/v1/Dimensions('{}')/Hierarchies('{}')/ElementAttributes", dimension_name, hierarchy_name) return self._rest.POST(url, element_attribute.body, **kwargs)
def get_element_attributes(self, dimension_name: str, hierarchy_name: str, **kwargs) -> List[ElementAttribute]: """ Get element attributes from hierarchy :param dimension_name: :param hierarchy_name: :return: """ url = format_url( "/api/v1/Dimensions('{}')/Hierarchies('{}')/ElementAttributes", dimension_name, hierarchy_name) response = self._rest.GET(url, **kwargs) element_attributes = [ElementAttribute.from_dict(ea) for ea in response.json()['value']] return element_attributes
def set_local_start_time(self, chore_name: str, date_time: datetime, **kwargs) -> Response: """ Makes Server crash if chore is activated (10.2.2 FP6) :) :param chore_name: :param date_time: :return: """ url = format_url("/api/v1/Chores('{}')/tm1.SetServerLocalStartTime", chore_name) data = { "StartDate": "{}-{}-{}".format( date_time.year, date_time.month, date_time.day), "StartTime": "{}:{}:{}".format( self.zfill_two(date_time.hour), self.zfill_two(date_time.minute), self.zfill_two(date_time.second)) } return self._rest.POST(url, json.dumps(data), **kwargs)
def get_elements_by_level(self, dimension_name: str, hierarchy_name: str, level: int, **kwargs) -> List[str]: """ Get all element names by level in a hierarchy :param dimension_name: Name of the dimension :param hierarchy_name: Name of the hierarchy :param level: Level to filter :return: List of element names """ url = format_url( "/api/v1/Dimensions('{}')/Hierarchies('{}')/Elements?$select=Name&$filter=Level eq {}", dimension_name, hierarchy_name, str(level)) response = self._rest.GET(url, **kwargs) return [e["Name"] for e in response.json()['value']]
def get_all_names_without_rules(self, skip_control_cubes: bool = False, **kwargs) -> List[str]: """ Ask TM1 Server for list of all cube names that do not have rules :skip_control_cubes: bool, True will exclude control cubes from list :return: List of Strings """ url = format_url( "/api/v1/{}?$select=Name,Rules&$filter=Rules eq null", 'ModelCubes()' if skip_control_cubes else 'Cubes' ) response = self._rest.GET(url, **kwargs) cubes = list(cube['Name'] for cube in response.json()['value']) return cubes
def get_mdx_view(self, cube_name: str, view_name: str, private: bool = False, **kwargs) -> MDXView: """ Get an MDXView from TM1 Server :param cube_name: String, name of the cube :param view_name: String, name of the MDX view :param private: boolean :return: instance of TM1py.MDXView """ view_type = 'PrivateViews' if private else 'Views' url = format_url("/api/v1/Cubes('{}')/{}('{}')?$expand=*", cube_name, view_type, view_name) response = self._rest.GET(url, **kwargs) mdx_view = MDXView.from_json(view_as_json=response.text) return mdx_view
def _construct_body(self) -> Dict: """ construct the ODATA conform JSON represenation for the ViewAxisSelection entity. :return: dictionary """ body_as_dict = collections.OrderedDict() if isinstance(self._subset, AnonymousSubset): body_as_dict['Subset'] = json.loads(self._subset.body) elif isinstance(self._subset, Subset): subset_path = format_url( "Dimensions('{}')/Hierarchies('{}')/Subsets('{}')", self._dimension_name, self._hierarchy_name, self._subset.name) body_as_dict['*****@*****.**'] = subset_path return body_as_dict
def create(self, application: Union[Application, DocumentApplication], private: bool = False, **kwargs) -> Response: """ Create Planning Analytics application :param application: instance of Application :param private: boolean :return: """ contents = 'PrivateContents' if private else 'Contents' mid = "" if application.path.strip() != '': mid = "".join([format_url("/Contents('{}')", element) for element in application.path.split('/')]) url = "/api/v1/Contents('Applications')" + mid + "/" + contents response = self._rest.POST(url, application.body, **kwargs) if application.application_type == ApplicationTypes.DOCUMENT: url = format_url( "/api/v1/Contents('Applications')" + mid + "/" + contents + "('{name}.blob')/Document/Content", name=application.name) response = self._rest.PUT(url, application.content, headers=self.BINARY_HTTP_HEADER, **kwargs) return response
def delete_element_attribute(self, dimension_name: str, hierarchy_name: str, element_attribute: str, **kwargs) -> Response: """ like AttrDelete :param dimension_name: :param hierarchy_name: :param element_attribute: instance of TM1py.ElementAttribute :return: """ url = format_url( "/api/v1/Dimensions('}}ElementAttributes_{}')/Hierarchies('}}ElementAttributes_{}')/Elements('{}')", dimension_name, hierarchy_name, element_attribute) return self._rest.DELETE(url, **kwargs)
def create(self, view: Union[MDXView, NativeView], private: bool = False, **kwargs) -> Response: """ create a new view on TM1 Server :param view: instance of subclass of TM1py.View (TM1py.NativeView or TM1py.MDXView) :param private: boolean :return: Response """ view_type = "PrivateViews" if private else "Views" url = format_url("/api/v1/Cubes('{}')/{}", view.cube, view_type) return self._rest.POST(url, view.body, **kwargs)
def get_last_process_message_from_messagelog(self, process_name: str, **kwargs) -> Optional[str]: """ Get the latest message log entry for a process :param process_name: name of the process :return: String - the message, for instance: "Ausführung normal beendet, verstrichene Zeit 0.03 Sekunden" """ url = format_url( "/api/v1/MessageLog()?$orderby='TimeStamp'&$filter=Logger eq 'TM1.Process' and contains(Message, '{}')", process_name) response = self._rest.GET(url=url, **kwargs) response_as_list = response.json()['value'] if len(response_as_list) > 0: message_log_entry = response_as_list[0] return message_log_entry['Message']