def __init__( self, name: str, dimension_name: str, elements: Optional[Iterable['Element']] = None, element_attributes: Optional[Iterable['ElementAttribute']] = None, edges: Optional['Dict'] = None, subsets: Optional[Iterable[str]] = None, structure: Optional[int] = None, default_member: Optional[str] = None): self._name = name self._dimension_name = None self.dimension_name = dimension_name self._elements = CaseAndSpaceInsensitiveDict() if elements: for elem in elements: self._elements[elem.name] = elem self._element_attributes = list( element_attributes) if element_attributes else [] self._edges = CaseAndSpaceInsensitiveTuplesDict( edges) if edges else CaseAndSpaceInsensitiveTuplesDict() self._subsets = list(subsets) if subsets else [] # balanced is true, false or None (in versions < TM1 11) self._balanced = False if not structure else structure == 0 self._default_member = default_member
def __init__(self, name, dimension_name, elements=None, element_attributes=None, edges=None, subsets=None, structure=None, default_member=None): self._name = name self._dimension_name = dimension_name self._elements = CaseAndSpaceInsensitiveDict() if elements: for elem in elements: self._elements[elem.name] = elem self._element_attributes = element_attributes if element_attributes else [] self._edges = CaseAndSpaceInsensitiveTuplesDict(edges) if edges else CaseAndSpaceInsensitiveTuplesDict() self._subsets = subsets if subsets else [] # balanced is true, false or None (in versions < TM1 11) self._balanced = structure if not structure else structure == 0 self._default_member = default_member
def update_element_attributes(self, hierarchy: Hierarchy, **kwargs): """ Update the elementattributes of a hierarchy :param hierarchy: Instance of TM1py.Hierarchy :return: """ # get existing attributes first existing_element_attributes = self.elements.get_element_attributes( dimension_name=hierarchy.dimension_name, hierarchy_name=hierarchy.name, **kwargs) existing_element_attributes = CaseAndSpaceInsensitiveDict( {ea.name: ea for ea in existing_element_attributes}) attributes_to_create = list() attributes_to_delete = list() attributes_to_update = list() for element_attribute in hierarchy.element_attributes: if element_attribute.name not in existing_element_attributes: attributes_to_create.append(element_attribute) continue existing_element_attribute = existing_element_attributes[ element_attribute.name] if not existing_element_attribute.attribute_type == element_attribute.attribute_type: attributes_to_update.append(element_attribute) continue for existing_element_attribute in existing_element_attributes: if existing_element_attribute not in CaseAndSpaceInsensitiveSet( [ea.name for ea in hierarchy.element_attributes]): attributes_to_delete.append(existing_element_attribute) for element_attribute in attributes_to_create: self.elements.create_element_attribute( dimension_name=hierarchy.dimension_name, hierarchy_name=hierarchy.name, element_attribute=element_attribute, **kwargs) for element_attribute in attributes_to_delete: self.elements.delete_element_attribute( dimension_name=hierarchy.dimension_name, hierarchy_name=hierarchy.name, element_attribute=element_attribute, **kwargs) for element_attribute in attributes_to_update: self.elements.delete_element_attribute( dimension_name=hierarchy.dimension_name, hierarchy_name=hierarchy.name, element_attribute=element_attribute.name, **kwargs) self.elements.create_element_attribute( dimension_name=hierarchy.dimension_name, hierarchy_name=hierarchy.name, element_attribute=element_attribute, **kwargs)
def test_ne(self): _map = CaseAndSpaceInsensitiveDict() _map["key 1"] = "wrong" _map["key 2"] = "value2" _map["key3"] = "value3" self.assertNotEqual(self.map, _map) _map = CaseAndSpaceInsensitiveDict() _map["key1"] = "value1" _map["key 2"] = "wrong" _map["key3"] = "value3" self.assertNotEqual(self.map, _map) _map = CaseAndSpaceInsensitiveDict() _map["key1"] = "value1" _map["key2"] = "value2" _map["key4"] = "value4" self.assertNotEqual(self.map, _map)
def __init__(self, name, dimension_name, elements=None, element_attributes=None, edges=None, subsets=None, default_member=None): self._name = name self._dimension_name = dimension_name self._elements = CaseAndSpaceInsensitiveDict() if elements: for elem in elements: self._elements[elem.name] = elem self._element_attributes = element_attributes if element_attributes else [] self._edges = edges if edges else CaseAndSpaceInsensitiveTuplesDict() self._subsets = subsets if subsets else [] self._default_member = default_member
from TM1py import TM1Service from TM1py.Utils.Utils import CaseAndSpaceInsensitiveDict import methods from utils import set_current_directory METHODS = CaseAndSpaceInsensitiveDict({ "IRR": methods.irr, "NPV": methods.npv, "STDEV": methods.stdev, "STDEV_P": methods.stdev_p, "FV": methods.fv, "FV_SCHEDULE": methods.fv_schedule, "PV": methods.pv, "XNPV": methods.xnpv, "PMT": methods.pmt, "PPMT": methods.ppmt, "MIRR": methods.mirr, "XIRR": methods.xirr, "NPER": methods.nper, "RATE": methods.rate, "EFFECT": methods.effect, "NOMINAL": methods.nominal, "SLN": methods.sln }) APP_NAME = "CubeCalc" CURRENT_DIRECTORY = set_current_directory() LOGFILE = os.path.join(CURRENT_DIRECTORY, APP_NAME + ".log") CONFIG = os.path.join(CURRENT_DIRECTORY, "config.ini")
def get_message_log_entries(self, reverse: bool = True, since: datetime = None, until: datetime = None, top: int = None, logger: str = None, level: str = None, msg_contains: Iterable = None, msg_contains_operator: str = 'and', **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 logger: string, eg TM1.Server, TM1.Chore, TM1.Mdx.Interface, TM1.Process :param level: string, ERROR, WARNING, INFO, DEBUG, UNKNOWN :param msg_contains: iterable, find substring in log message; list of substrings will be queried as AND statement :param msg_contains_operator: 'and' or 'or' :param kwargs: :return: Dict of server log """ msg_contains_operator = msg_contains_operator.strip().lower() if msg_contains_operator not in ("and", "or"): raise ValueError( "'msg_contains_operator' must be either 'AND' or 'OR'") reverse = 'desc' if reverse else 'asc' url = '/api/v1/MessageLogEntries?$orderby=TimeStamp {}'.format(reverse) if since or until or logger or level or msg_contains: log_filters = [] 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"))) if until: # If until doesn't have tz information, UTC is assumed if not until.tzinfo: until = self.utc_localize_time(until) log_filters.append( format_url("TimeStamp le {}", until.strftime("%Y-%m-%dT%H:%M:%SZ"))) if logger: log_filters.append(format_url("Logger eq '{}'", logger)) if level: level_dict = CaseAndSpaceInsensitiveDict({ 'ERROR': 1, 'WARNING': 2, 'INFO': 3, 'DEBUG': 4, 'UNKNOWN': 5 }) level_index = level_dict.get(level) if level_index: log_filters.append("Level eq {}".format(level_index)) if msg_contains: if isinstance(msg_contains, str): log_filters.append( format_url("contains(toupper(Message),toupper('{}'))", msg_contains)) else: msg_filters = [ format_url("contains(toupper(Message),toupper('{}'))", wildcard) for wildcard in msg_contains ] log_filters.append("({})".format( f" {msg_contains_operator} ".join(msg_filters))) url += "&$filter={}".format(" and ".join(log_filters)) if top: url += '&$top={}'.format(top) response = self._rest.GET(url, **kwargs) return response.json()['value']
def remove_all_elements(self): self._elements = CaseAndSpaceInsensitiveDict() self.remove_all_edges()
class Hierarchy(TM1Object): """ Abstraction of TM1 Hierarchy Requires reference to a Dimension Elements modeled as a Dictionary where key is the element name and value an instance of TM1py.Element { 'US': instance of TM1py.Element, 'CN': instance of TM1py.Element, 'AU': instance of TM1py.Element } ElementAttributes of type TM1py.Objects.ElementAttribute Edges are represented as a TM1py.Utils.CaseAndSpaceInsensitiveTupleDict: { (parent1, component1) : 10, (parent1, component2) : 30 } Subsets is list of type TM1py.Subset """ def __init__( self, name: str, dimension_name: str, elements: Optional[Iterable['Element']] = None, element_attributes: Optional[Iterable['ElementAttribute']] = None, edges: Optional['Dict'] = None, subsets: Optional[Iterable[str]] = None, structure: Optional[int] = None, default_member: Optional[str] = None): self._name = name self._dimension_name = None self.dimension_name = dimension_name self._elements = CaseAndSpaceInsensitiveDict() if elements: for elem in elements: self._elements[elem.name] = elem self._element_attributes = list( element_attributes) if element_attributes else [] self._edges = CaseAndSpaceInsensitiveTuplesDict( edges) if edges else CaseAndSpaceInsensitiveTuplesDict() self._subsets = list(subsets) if subsets else [] # balanced is true, false or None (in versions < TM1 11) self._balanced = False if not structure else structure == 0 self._default_member = default_member @classmethod def from_dict(cls, hierarchy_as_dict: Dict) -> 'Hierarchy': # Build the Dictionary for the edges edges = CaseAndSpaceInsensitiveTuplesDict({ (edge['ParentName'], edge['ComponentName']): edge['Weight'] for edge in hierarchy_as_dict['Edges'] }) return cls(name=hierarchy_as_dict['Name'], dimension_name=hierarchy_as_dict['UniqueName'] [1:hierarchy_as_dict['UniqueName'].find("].[")], elements=[ Element.from_dict(elem) for elem in hierarchy_as_dict['Elements'] ], element_attributes=[ ElementAttribute(ea['Name'], ea['Type']) for ea in hierarchy_as_dict['ElementAttributes'] ], edges=edges, subsets=[ subset['Name'] for subset in hierarchy_as_dict['Subsets'] ], structure=hierarchy_as_dict['Structure'] if 'Structure' in hierarchy_as_dict else None, default_member=hierarchy_as_dict['DefaultMember']['Name'] if hierarchy_as_dict['DefaultMember'] else None) @property def name(self) -> str: return self._name @name.setter def name(self, value: str): self._name = value @property def dimension_name(self) -> str: return self._dimension_name @dimension_name.setter def dimension_name(self, dimension_name: str): self._dimension_name = dimension_name @property def elements(self) -> CaseAndSpaceInsensitiveDict: return self._elements @property def element_attributes(self) -> List[ElementAttribute]: return self._element_attributes @property def edges(self) -> 'CaseAndSpaceInsensitiveTuplesDict': return self._edges @property def subsets(self) -> List[str]: return self._subsets @property def balanced(self) -> bool: return self._balanced @property def default_member(self) -> str: return self._default_member @property def body(self) -> str: return json.dumps(self._construct_body()) @property def body_as_dict(self) -> Dict: return self._construct_body() def contains_element(self, element_name: str) -> bool: return element_name in self._elements def get_element(self, element_name: str) -> Element: if element_name in self._elements: return self._elements[element_name] else: raise ValueError("Element: {} not found in Hierarchy: {}".format( element_name, self.name)) def add_element(self, element_name: str, element_type: Union[str, Element.Types]): if element_name in self._elements: raise ValueError("Element name must be unique") self._elements[element_name] = Element(name=element_name, element_type=element_type) def update_element(self, element_name: str, element_type: Union[str, Element.Types]): self._elements[element_name].element_type = element_type def remove_element(self, element_name: str): if element_name not in self._elements: return del self._elements[element_name] self.remove_edges_related_to_element(element_name=element_name) def remove_all_elements(self): self._elements = CaseAndSpaceInsensitiveDict() self.remove_all_edges() def add_edge(self, parent: str, component: str, weight: int): self._edges[(parent, component)] = weight def update_edge(self, parent: str, component: str, weight: int): self._edges[(parent, component)] = weight def remove_edge(self, parent: str, component: str): if (parent, component) in self.edges: del self.edges[(parent, component)] def remove_edges(self, edges: Iterable[Tuple[str, str]]): for edge in edges: self.remove_edge(*edge) def remove_all_edges(self): self._edges = CaseAndSpaceInsensitiveTuplesDict() def remove_edges_related_to_element(self, element_name: str): element_name_adjusted = lower_and_drop_spaces(element_name) edges_to_remove = set() for edge in self._edges.adjusted_keys(): if element_name_adjusted in edge: edges_to_remove.add(edge) self.remove_edges(edges=edges_to_remove) def add_element_attribute(self, name: str, attribute_type: str): attribute = ElementAttribute(name, attribute_type) if attribute not in self.element_attributes: self.element_attributes.append(attribute) def remove_element_attribute(self, name: str): self._element_attributes = [ element_attribute for element_attribute in self.element_attributes if not case_and_space_insensitive_equals(element_attribute.name, name) ] def _construct_body(self, element_attributes: Optional[bool] = False) -> Dict: """ With TM1 10.2.2 Hierarchy and Element Attributes can't be created in one batch -> https://www.ibm.com/developerworks/community/forums/html/threadTopic?id=d91f3e0e-d305-44db-ac02-2fdcbee00393 Thus, no need to have the ElementAttribute included in the JSON :param element_attributes: Only include element_attributes in body if explicitly asked for :return: """ body_as_dict = collections.OrderedDict() body_as_dict['Name'] = self._name body_as_dict['Elements'] = [] body_as_dict['Edges'] = [] for element in self._elements.values(): body_as_dict['Elements'].append(element.body_as_dict) for edge in self._edges: edge_as_dict = collections.OrderedDict() edge_as_dict['ParentName'] = edge[0] edge_as_dict['ComponentName'] = edge[1] edge_as_dict['Weight'] = self._edges[edge] body_as_dict['Edges'].append(edge_as_dict) if element_attributes: body_as_dict['ElementAttributes'] = [ element_attribute.body_as_dict for element_attribute in self._element_attributes ] return body_as_dict def __iter__(self): return iter(self._elements.values()) def __len__(self): return len(self._elements) def __contains__(self, item): return self.contains_element(item) def __getitem__(self, item): return self.get_element(item)
class Hierarchy(TM1Object): """ Abstraction of TM1 Hierarchy Requires reference to a Dimension Elements modeled as a Dictionary where key is the element name and value an instance of TM1py.Element { 'US': instance of TM1py.Element, 'CN': instance of TM1py.Element, 'AU': instance of TM1py.Element } ElementAttributes of type TM1py.Objects.ElementAttribute Edges are represented as a TM1py.Utils.CaseAndSpaceInsensitiveTupleDict: { (parent1, component1) : 10, (parent1, component2) : 30 } Subsets is list of type TM1py.Subset """ def __init__(self, name, dimension_name, elements=None, element_attributes=None, edges=None, subsets=None, structure=None, default_member=None): self._name = name self._dimension_name = dimension_name self._elements = CaseAndSpaceInsensitiveDict() if elements: for elem in elements: self._elements[elem.name] = elem self._element_attributes = element_attributes if element_attributes else [] self._edges = edges if edges else CaseAndSpaceInsensitiveTuplesDict() self._subsets = subsets if subsets else [] # balanced is true, false or None (in versions < TM1 11) self._balanced = structure if not structure else structure == 0 self._default_member = default_member @classmethod def from_dict(cls, hierarchy_as_dict): # Build the Dictionary for the edges edges = CaseAndSpaceInsensitiveTuplesDict({ (edge['ParentName'], edge['ComponentName']): edge['Weight'] for edge in hierarchy_as_dict['Edges'] }) return cls(name=hierarchy_as_dict['Name'], dimension_name=hierarchy_as_dict['UniqueName'] [1:hierarchy_as_dict['UniqueName'].find("].[")], elements=[ Element.from_dict(elem) for elem in hierarchy_as_dict['Elements'] ], element_attributes=[ ElementAttribute(ea['Name'], ea['Type']) for ea in hierarchy_as_dict['ElementAttributes'] ], edges=edges, subsets=[ subset['Name'] for subset in hierarchy_as_dict['Subsets'] ], structure=hierarchy_as_dict['Structure'] if 'Structure' in hierarchy_as_dict else None, default_member=hierarchy_as_dict['DefaultMember']['Name'] if hierarchy_as_dict['DefaultMember'] else None) @property def name(self): return self._name @name.setter def name(self, value): self._name = value @property def dimension_name(self): return self._dimension_name @property def elements(self): return self._elements @property def element_attributes(self): return self._element_attributes @property def edges(self): return self._edges @property def subsets(self): return self._subsets @property def balanced(self): return self._balanced @property def default_member(self): return self._default_member @property def body(self): return json.dumps(self._construct_body()) @property def body_as_dict(self): return self._construct_body() def add_element(self, element_name, element_type): if element_name in self._elements: raise Exception("Elementname has to be unqiue") e = Element(name=element_name, element_type=element_type) self._elements[element_name] = e def update_element(self, element_name, element_type=None): self._elements[element_name].element_type = element_type def add_edge(self, parent, component, weight): self._edges[(parent, component)] = weight def update_edge(self, parent, component, weight): self._edges[(parent, component)] = weight def remove_edge(self, parent, component): if (parent, component) in self.edges: del self.edges[(parent, component)] def add_element_attribute(self, name, attribute_type): if name not in self.element_attributes: self.element_attributes.append( ElementAttribute(name, attribute_type)) def remove_element_attribute(self, name): if name in self.element_attributes: self.element_attributes.remove(name) def _construct_body(self, element_attributes=False): """ With TM1 10.2.2 Hierarchy and Element Attributes can't be created in one batch -> https://www.ibm.com/developerworks/community/forums/html/threadTopic?id=d91f3e0e-d305-44db-ac02-2fdcbee00393 Thus, no need to have the ElementAttribute included in the JSON :param element_attributes: Only include element_attributes in body if explicitly asked for :return: """ body_as_dict = collections.OrderedDict() body_as_dict['Name'] = self._name body_as_dict['Elements'] = [] body_as_dict['Edges'] = [] for element in self._elements.values(): body_as_dict['Elements'].append(element.body_as_dict) for edge in self._edges: edge_as_dict = collections.OrderedDict() edge_as_dict['ParentName'] = edge[0] edge_as_dict['ComponentName'] = edge[1] edge_as_dict['Weight'] = self._edges[edge] body_as_dict['Edges'].append(edge_as_dict) if element_attributes: body_as_dict['ElementAttributes'] = [ element_attribute.body_as_dict for element_attribute in self._element_attributes ] return body_as_dict def __iter__(self): return iter(self._elements.values()) def __len__(self): return len(self._elements.values())
def test_eq_case_and_space_insensitive(self): _map = CaseAndSpaceInsensitiveDict() _map["key1"] = "value1" _map["KEY2"] = "value2" _map["K e Y 3"] = "value3" self.assertEqual(self.map, _map)
def test_eq(self): _map = CaseAndSpaceInsensitiveDict() _map["key1"] = "value1" _map["key2"] = "value2" _map["key3"] = "value3" self.assertEqual(self.map, _map)
def setUp(cls): cls.map = CaseAndSpaceInsensitiveDict() cls.map["key1"] = "value1" cls.map["key2"] = "value2" cls.map["key3"] = "value3"
METHODS = CaseAndSpaceInsensitiveDict({ "IRR": methods.irr, "NPV": methods.npv, "STDEV": methods.stdev, "STDEV_P": methods.stdev_p, "FV": methods.fv, "FV_SCHEDULE": methods.fv_schedule, "PV": methods.pv, "XNPV": methods.xnpv, "PMT": methods.pmt, "PPMT": methods.ppmt, "MIRR": methods.mirr, "XIRR": methods.xirr, "NPER": methods.nper, "RATE": methods.rate, "EFFECT": methods.effect, "NOMINAL": methods.nominal, "SLN": methods.sln, "MEAN": methods.mean, "SEM": methods.sem, "MEDIAN": methods.median, "MODE": methods.mode, "VAR": methods.var, "KURT": methods.kurt, "SKEW": methods.skew, "RNG": methods.rng, "MIN": methods.min_, "MAX": methods.max_, "SUM": methods.sum_, "COUNT": methods.count })