def exists(self, dimension_name: str, **kwargs) -> bool: """ Check if dimension exists :return: """ url = format_url("/api/v1/Dimensions('{}')", dimension_name) return self._exists(url, **kwargs)
def update_default_member(self, dimension_name: str, hierarchy_name: str = None, member_name: str = "", **kwargs) -> Response: """ Update the default member of a hierarchy. Currently implemented through TI, since TM1 API does not supports default member updates yet. :param dimension_name: :param hierarchy_name: :param member_name: :return: """ from TM1py import ProcessService, CellService if hierarchy_name and not case_and_space_insensitive_equals( dimension_name, hierarchy_name): dimension = "{}:{}".format(dimension_name, hierarchy_name) else: dimension = dimension_name cells = {(dimension, 'hierarchy0', 'defaultMember'): member_name} CellService(self._rest).write_values( cube_name="}HierarchyProperties", cellset_as_dict=cells, dimensions=('}Dimensions', '}Hierarchies', '}HierarchyProperties'), **kwargs) return ProcessService(self._rest).execute_ti_code( lines_prolog=format_url("RefreshMdxHierarchy('{}');", dimension_name), **kwargs)
def add_user_to_groups(self, user_name: str, groups: Iterable[str], **kwargs) -> Response: """ :param user_name: name of user :param groups: iterable of groups :return: response """ user_name = self.determine_actual_user_name(user_name, **kwargs) url = format_url("/api/v1/Users('{}')", user_name) body = { "Name": user_name, "*****@*****.**": [ format_url("Groups('{}')", self.determine_actual_group_name(group)) for group in groups] } return self._rest.PATCH(url, json.dumps(body), **kwargs)
def delete(self, dimension_name: str, **kwargs) -> Response: """ Delete a dimension :param dimension_name: Name of the dimension :return: """ url = format_url("/api/v1/Dimensions('{}')", dimension_name) return self._rest.DELETE(url, **kwargs)
def get(self, dimension_name: str, **kwargs) -> Dimension: """ Get a Dimension :param dimension_name: :return: """ url = format_url("/api/v1/Dimensions('{}')?$expand=Hierarchies($expand=*)", dimension_name) response = self._rest.GET(url, **kwargs) return Dimension.from_json(response.text)
def delete_user(self, user_name: str, **kwargs) -> Response: """ Delete user on TM1 Server :param user_name: :return: response """ user_name = self.determine_actual_user_name(user_name, **kwargs) url = format_url("/api/v1/Users('{}')", user_name) return self._rest.DELETE(url, **kwargs)
def delete_group(self, group_name: str, **kwargs) -> Response: """ Delete a group in the TM1 Server :param group_name: :return: """ group_name = self.determine_actual_group_name(group_name, **kwargs) url = format_url("/api/v1/Groups('{}')", group_name) return self._rest.DELETE(url, **kwargs)
def remove_all_edges(self, dimension_name: str, hierarchy_name: str = None, **kwargs) -> Response: if not hierarchy_name: hierarchy_name = dimension_name url = format_url("/api/v1/Dimensions('{}')/Hierarchies('{}')", dimension_name, hierarchy_name) body = {"Edges": []} return self._rest.PATCH(url=url, data=json.dumps(body), **kwargs)
def get_groups(self, user_name: str, **kwargs) -> List[str]: """ Get the groups of a user in TM1 Server :param user_name: :return: List of strings """ user_name = self.determine_actual_user_name(user_name, **kwargs) url = format_url("/api/v1/Users('{}')/Groups", user_name) response = self._rest.GET(url, **kwargs) return [group['Name'] for group in response.json()['value']]
def create(self, hierarchy: Hierarchy, **kwargs): """ Create a hierarchy in an existing dimension :param hierarchy: :return: """ url = format_url("/api/v1/Dimensions('{}')/Hierarchies", hierarchy.dimension_name) response = self._rest.POST(url, hierarchy.body, **kwargs) return response
def get_user_names_from_group(self, group_name: str, **kwargs) -> List[str]: """ Get all users from group :param group_name: :return: List of strings """ url = format_url("/api/v1/Groups('{}')?$expand=Users($expand=Groups)", group_name) response = self._rest.GET(url, **kwargs) users = [user["Name"] for user in response.json()['Users']] return users
def get_all_names(self, dimension_name: str, **kwargs): """ get all names of existing Hierarchies in a dimension :param dimension_name: :return: """ url = format_url("/api/v1/Dimensions('{}')/Hierarchies?$select=Name", dimension_name) response = self._rest.GET(url, **kwargs) return [hierarchy["Name"] for hierarchy in response.json()["value"]]
def exists(self, dimension_name: str, hierarchy_name: str, **kwargs) -> bool: """ :param dimension_name: :param hierarchy_name: :return: """ url = format_url("/api/v1/Dimensions('{}')/Hierarchies('{}')", dimension_name, hierarchy_name) return self._exists(url, **kwargs)
def remove_user_from_group(self, group_name: str, user_name: str, **kwargs) -> Response: """ Remove user from group in TM1 Server :param group_name: :param user_name: :return: response """ user_name = self.determine_actual_user_name(user_name, **kwargs) group_name = self.determine_actual_group_name(group_name, **kwargs) url = format_url("/api/v1/Users('{}')/Groups?$id=Groups('{}')", user_name, group_name) return self._rest.DELETE(url, **kwargs)
def get_users_from_group(self, group_name: str, **kwargs): """ Get all users from group :param group_name: :return: List of TM1py.User instances """ url = format_url( "/api/v1/Groups('{}')?$expand=Users($select=Name,FriendlyName,Password,Type,Enabled;$expand=Groups)", group_name) response = self._rest.GET(url, **kwargs) users = [User.from_dict(user) for user in response.json()['Users']] return users
def update_user(self, user: User, **kwargs) -> Response: """ Update user on TM1 Server :param user: instance of TM1py.User :return: response """ user.name = self.determine_actual_user_name(user.name, **kwargs) for current_group in self.get_groups(user.name, **kwargs): if current_group not in user.groups: self.remove_user_from_group(current_group, user.name, **kwargs) url = format_url("/api/v1/Users('{}')", user.name) return self._rest.PATCH(url, user.body, **kwargs)
def get_user(self, user_name: str, **kwargs) -> User: """ Get user from TM1 Server :param user_name: :return: instance of TM1py.User """ user_name = self.determine_actual_user_name(user_name, **kwargs) url = format_url( "/api/v1/Users('{}')?$select=Name,FriendlyName,Password,Type,Enabled&$expand=Groups", user_name) response = self._rest.GET(url, **kwargs) return User.from_dict(response.json())
def get(self, dimension_name: str, hierarchy_name: str, **kwargs): """ get hierarchy :param dimension_name: name of the dimension :param hierarchy_name: name of the hierarchy :return: """ url = format_url( "/api/v1/Dimensions('{}')/Hierarchies('{}')?$expand=Edges,Elements,ElementAttributes,Subsets,DefaultMember", dimension_name, hierarchy_name) response = self._rest.GET(url, **kwargs) return Hierarchy.from_dict(response.json())
def get_hierarchy_summary(self, dimension_name: str, hierarchy_name: str, **kwargs) -> Dict[str, int]: hierarchy_properties = ("Elements", "Edges", "ElementAttributes", "Members", "Levels") url = format_url( "/api/v1/Dimensions('{}')/Hierarchies('{}')?$expand=Edges/$count,Elements/$count," "ElementAttributes/$count,Members/$count,Levels/$count&$select=Cardinality", dimension_name, hierarchy_name) hierary_summary_raw = self._rest.GET(url, **kwargs).json() return { hierarchy_property: hierary_summary_raw[hierarchy_property + "@odata.count"] for hierarchy_property in hierarchy_properties }
def construct_body(self) -> str: """ construct body (json) from the class attributes :return: String, TM1 JSON representation of a user """ body_as_dict = collections.OrderedDict() body_as_dict['Name'] = self.name body_as_dict['FriendlyName'] = self.friendly_name or self.name body_as_dict['Enabled'] = self._enabled body_as_dict['Type'] = str(self._user_type) if self.password: body_as_dict['Password'] = self._password body_as_dict['*****@*****.**'] = [ format_url("Groups('{}')", group) for group in self.groups ] return json.dumps(body_as_dict, ensure_ascii=False)
def get_all_names(self, skip_control_dims: bool = False, **kwargs) -> List[str]: """Ask TM1 Server for list of all dimension names :skip_control_dims: bool, True to skip control dims :Returns: List of Strings """ url = format_url( "/api/v1/{}?$select=Name", 'ModelDimensions()' if skip_control_dims else 'Dimensions') response = self._rest.GET(url, **kwargs) dimension_names = list(entry['Name'] for entry in response.json()['value']) return dimension_names
def update(self, hierarchy: Hierarchy, **kwargs) -> List[Response]: """ update a hierarchy. It's a two step process: 1. Update Hierarchy 2. Update Element-Attributes Function caters for Bug with Edge Creation: https://www.ibm.com/developerworks/community/forums/html/topic?id=75f2b99e-6961-4c71-9364-1d5e1e083eff :param hierarchy: instance of TM1py.Hierarchy :return: list of responses """ # functions returns multiple responses responses = list() # 1. Update Hierarchy url = format_url("/api/v1/Dimensions('{}')/Hierarchies('{}')", hierarchy.dimension_name, hierarchy.name) # Workaround EDGES: Handle Issue, that Edges cant be created in one batch with the Hierarchy in certain versions hierarchy_body = hierarchy.body_as_dict if self.version[0:8] in self.EDGES_WORKAROUND_VERSIONS: del hierarchy_body["Edges"] responses.append( self._rest.PATCH(url, json.dumps(hierarchy_body), **kwargs)) # 2. Update Attributes responses.append( self.update_element_attributes(hierarchy=hierarchy, **kwargs)) # Workaround EDGES if self.version[0:8] in self.EDGES_WORKAROUND_VERSIONS: from TM1py.Services import ProcessService process_service = ProcessService(self._rest) ti_function = "HierarchyElementComponentAdd('{}', '{}', '{}', '{}', {});" ti_statements = [ ti_function.format(hierarchy.dimension_name, hierarchy.name, edge[0], edge[1], hierarchy.edges[(edge[0], edge[1])]) for edge in hierarchy.edges ] responses.append( process_service.execute_ti_code(lines_prolog=ti_statements, **kwargs)) return responses
def is_balanced(self, dimension_name: str, hierarchy_name: str, **kwargs): """ Check if hierarchy is balanced :param dimension_name: :param hierarchy_name: :return: """ url = format_url( "/api/v1/Dimensions('{}')/Hierarchies('{}')/Structure/$value", dimension_name, hierarchy_name) structure = int(self._rest.GET(url, **kwargs).text) # 0 = balanced, 2 = unbalanced if structure == 0: return True elif structure == 2: return False else: raise RuntimeError( f"Unexpected return value from TM1 API request: {str(structure)}" )
def get_default_member(self, dimension_name: str, hierarchy_name: str = None, **kwargs) -> Optional[str]: """ Get the defined default_member for a Hierarchy. Will return the element with index 1, if default member is not specified explicitly in }HierarchyProperty Cube :param dimension_name: :param hierarchy_name: :return: String, name of Member """ url = format_url( "/api/v1/Dimensions('{dimension}')/Hierarchies('{hierarchy}')/DefaultMember", dimension=dimension_name, hierarchy=hierarchy_name if hierarchy_name else dimension_name) response = self._rest.GET(url=url, **kwargs) if not response.text: return None return response.json()["Name"]
def add_edges(self, dimension_name: str, hierarchy_name: str = None, edges: Dict[Tuple[str, str], int] = None, **kwargs) -> Response: """ Add Edges to hierarchy. Fails if any edge already exists. :param dimension_name: :param hierarchy_name: :param edges: :return: """ if not hierarchy_name: hierarchy_name = dimension_name url = format_url("/api/v1/Dimensions('{}')/Hierarchies('{}')/Edges", dimension_name, hierarchy_name) body = [{ "ParentName": parent, "ComponentName": component, "Weight": float(weight) } for (parent, component), weight in edges.items()] return self._rest.POST(url=url, data=json.dumps(body), **kwargs)
def git_execute_plan(self, plan_id: str, **kwargs) -> Response: """ Executes a plan based on the planid :param plan_id: GitPlan id """ url = format_url("/api/v1/GitPlans('{}')/tm1.Execute", plan_id) return self._rest.POST(url=url, **kwargs)
def user_exists(self, user_name: str, **kwargs) -> bool: url = format_url("/api/v1/Users('{}')", user_name) return self._exists(url, **kwargs)
def group_exists(self, group_name: str, **kwargs) -> bool: url = format_url("/api/v1/Groups('{}')", group_name) return self._exists(url, **kwargs)
def update_user_password(self, user_name: str, password: str, **kwargs) -> Response: url = format_url("/api/v1/Users('{}')", user_name) body = {"Password": password} return self._rest.PATCH(url, json.dumps(body), **kwargs)
def delete(self, dimension_name: str, hierarchy_name: str, **kwargs) -> Response: url = format_url("/api/v1/Dimensions('{}')/Hierarchies('{}')", dimension_name, hierarchy_name) return self._rest.DELETE(url, **kwargs)