示例#1
0
    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
示例#2
0
    def test_eq(self):
        _map = CaseAndSpaceInsensitiveTuplesDict()
        _map[("Elem1", "Elem1")] = "Value1"
        _map[("Elem1", "Elem2")] = 2
        _map[("Elem1", "Elem3")] = 3
        self.assertEqual(_map, self.map)

        _map = CaseAndSpaceInsensitiveTuplesDict()
        _map[("Elem 1", "Elem1")] = "Value1"
        _map[("ELEM 1", "E L E M 2")] = 2
        _map[(" Elem1 ", "Elem 3")] = 3
        self.assertEqual(_map, self.map)
示例#3
0
 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
示例#4
0
    def extract_cellset_rows_and_values(self,
                                        cellset_id,
                                        element_unique_names=True,
                                        **kwargs):
        request = "/api/v1/Cellsets('{}')?$expand=" \
                  "Axes($filter=Ordinal eq 1;$expand=Tuples(" \
                  "$expand=Members($select=Element;$expand=Element($select={}))))," \
                  "Cells($select=Value)".format(cellset_id, "UniqueName" if element_unique_names else "Name")
        response = self._rest.GET(request=request, data='')
        response_json = response.json()
        rows = response_json["Axes"][0]["Tuples"]
        cell_values = [cell["Value"] for cell in response_json["Cells"]]

        result = CaseAndSpaceInsensitiveTuplesDict()

        number_rows = len(rows)
        # avoid division by zero
        if not number_rows:
            return result
        number_cells = len(cell_values)
        number_columns = int(number_cells / number_rows)

        cell_values_by_row = [
            cell_values[cell_counter:cell_counter + number_columns]
            for cell_counter in range(0, number_cells, number_columns)
        ]
        element_names_by_row = [
            tuple(member["Element"]
                  ["UniqueName" if element_unique_names else "Name"]
                  for member in tupl["Members"]) for tupl in rows
        ]
        for element_tuple, cells in zip(element_names_by_row,
                                        cell_values_by_row):
            result[element_tuple] = cells
        return result
示例#5
0
    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)
示例#6
0
    def test_ne(self):
        map = CaseAndSpaceInsensitiveTuplesDict()
        map[("Elem1", "Elem1")] = "Value1"
        map[("Elem1", "Elem2")] = 0
        map[("Elem1", "Elem3")] = 3
        self.assertNotEqual(map, self.map)

        map = CaseAndSpaceInsensitiveTuplesDict()
        map[("Elem 1", "Elem1")] = "Value1"
        map[("ELEM 1", "E L E M 2")] = "wrong"
        map[(" Elem1 ", "Elem 3")] = 3
        self.assertNotEqual(map, self.map)

        map = CaseAndSpaceInsensitiveTuplesDict()
        map[("wrong", "Elem1")] = "Value1"
        map[("Elem1", "Elem2")] = 2
        map[("Elem1", "Elem3")] = 3
        self.assertNotEqual(map, self.map)
示例#7
0
 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
示例#8
0
    def extract_cellset_rows_and_cells(self, cellset_id, **kwargs):
        request = "/api/v1/Cellsets('{}')?$expand=" \
                  "Axes($filter=Ordinal eq 1;$expand=Tuples($expand=Members($select=Element;$expand=Element($select=UniqueName))))," \
                  "Cells($select=Value)".format(cellset_id)
        response = self._rest.GET(request=request, data='')
        response_json = response.json()
        rows = response_json["Axes"][0]["Tuples"]
        cell_values = [cell["Value"] for cell in response_json["Cells"]]

        number_rows = len(rows)
        number_cells = len(cell_values)
        number_cells_in_row = int(number_cells / number_rows)

        cells_by_row = [cell_values[start:end]
                        for start, end in
                        zip(range(0, number_rows), range(number_cells_in_row, number_cells))]
        unique_element_names_by_row = [tuple(member["Element"]["UniqueName"] for member in tupl["Members"])
                                       for tupl
                                       in rows]
        result = CaseAndSpaceInsensitiveTuplesDict()
        for unique_element_names, cells in zip(unique_element_names_by_row, cells_by_row):
            result[unique_element_names] = cells
        return result
示例#9
0
 def remove_all_edges(self):
     self._edges = CaseAndSpaceInsensitiveTuplesDict()
示例#10
0
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)
示例#11
0
def build_date_dimension(tm1, dimension_name, first_date, last_date,
                         overwrite):
    date_span = last_date - first_date
    dates = [
        str(first_date + timedelta(day)) for day in range(date_span.days + 1)
    ]
    # Build Leaves
    leaves = [Element(name=date, element_type='Numeric') for date in dates]

    # Build Consolidations
    years = [str(year) for year in range(first_date.year, last_date.year + 1)]
    consolidations = [
        Element(name=year, element_type='Consolidated') for year in years
    ]

    for year in years:
        for month in range(1, 13):
            year_month = year + "-" + str(month).zfill(2)
            consolidations.append(
                Element(name=year_month, element_type="Consolidated"))

    # Build Elements
    elements = leaves + consolidations

    # Build Edges
    edges = CaseAndSpaceInsensitiveTuplesDict()
    for year in years:
        for month in range(1, 13):
            year_month = year + "-" + str(month).zfill(2)
            edges[(year, year_month)] = 1
        for date in filter(lambda d: d[0:4] == year, dates):
            year_month = date[0:7]
            edges[(year_month, date)] = 1

    # Build Attribute
    attributes = [
        ElementAttribute('Alias', 'Alias'),
        ElementAttribute('Year', 'String'),
        ElementAttribute('Month', 'String'),
        ElementAttribute('Day', 'String'),
        ElementAttribute('Weekday', 'String')
    ]

    # write Aliases
    attribute_values = {}
    for date in dates:
        # Year, Month, Day Attributes
        attribute_values[(date, 'Year')] = date.split('-')[0]
        attribute_values[(date, 'Month')] = date.split('-')[1]
        attribute_values[(date, 'Day')] = date.split('-')[2]
        attribute_values[(
            date, 'Weekday')] = dateutil.parser.parse(date).weekday() + 1
        # US Notation as Alias
        year, month, day = [str(int(ymd)) for ymd in date.split('-')]
        attribute_values[(date, 'Alias')] = month + "/" + day + "/" + year

    # Build Hierarchy, Dimension
    hier = Hierarchy(name=dimension_name,
                     dimension_name=dimension_name,
                     elements=elements,
                     edges=edges,
                     element_attributes=attributes)
    dim = Dimension(name=dimension_name, hierarchies=[hier])

    # Interaction with TM1
    exists = tm1.dimensions.exists(dimension_name)
    if not exists:
        tm1.dimensions.create(dim)
    elif exists and overwrite:
        tm1.dimensions.update(dim)
    if not exists or overwrite:
        tm1.cubes.cells.write_values(cube_name="}ElementAttributes_" +
                                     dimension_name,
                                     cellset_as_dict=attribute_values)

    # Year Subsets
    for year in years:
        expr = "{ FILTER ( {TM1SubsetAll([Date])}, [Date].[Year] = '" + year + "' ) }"
        subset = Subset(year,
                        dimension_name=dimension_name,
                        hierarchy_name=dimension_name,
                        expression=expr)
        if not tm1.dimensions.hierarchies.subsets.exists(
                year, dimension_name, dimension_name, private=False):
            tm1.dimensions.hierarchies.subsets.create(subset, private=False)
        else:
            tm1.dimensions.hierarchies.subsets.update(subset, private=False)
示例#12
0
 def setUp(self):
     self.map = CaseAndSpaceInsensitiveTuplesDict()
     self.map[("Elem1", "Elem1")] = "Value1"
     self.map[("Elem1", "Elem2")] = 2
     self.map[("Elem1", "Elem3")] = 3
示例#13
0
class TestCaseAndSpaceInsensitiveTuplesDict(unittest.TestCase):
    tm1: TM1Service
    map: CaseAndSpaceInsensitiveTuplesDict

    @classmethod
    def setUpClass(cls):
        """
        Establishes a connection to TM1 and creates TM! objects to use across all tests
        """

        # Connection to TM1
        config = configparser.ConfigParser()
        config.read(Path(__file__).parent.joinpath('config.ini'))
        cls.tm1 = TM1Service(**config['tm1srv01'])

    def setUp(self):
        self.map = CaseAndSpaceInsensitiveTuplesDict()
        self.map[("Elem1", "Elem1")] = "Value1"
        self.map[("Elem1", "Elem2")] = 2
        self.map[("Elem1", "Elem3")] = 3

    def tearDown(self):
        del self.map

    def test_del(self):
        self.assertIn(("Elem1", "Elem1"), self.map)
        self.assertIn(("Elem1", "Elem2"), self.map)
        self.assertIn(("Elem1", "Elem3"), self.map)
        del self.map[("El em1", "ELEM1")]
        del self.map[("El em1", "E L E M 2")]
        del self.map[("El em1", " eLEM3")]
        self.assertNotIn(("Elem1", "Elem1"), self.map)
        self.assertNotIn(("Elem1", "Elem2"), self.map)
        self.assertNotIn(("Elem1", "Elem3"), self.map)

    def test_eq(self):
        _map = CaseAndSpaceInsensitiveTuplesDict()
        _map[("Elem1", "Elem1")] = "Value1"
        _map[("Elem1", "Elem2")] = 2
        _map[("Elem1", "Elem3")] = 3
        self.assertEqual(_map, self.map)

        _map = CaseAndSpaceInsensitiveTuplesDict()
        _map[("Elem 1", "Elem1")] = "Value1"
        _map[("ELEM 1", "E L E M 2")] = 2
        _map[(" Elem1 ", "Elem 3")] = 3
        self.assertEqual(_map, self.map)

    def test_ne(self):
        _map = CaseAndSpaceInsensitiveTuplesDict()
        _map[("Elem1", "Elem1")] = "Value1"
        _map[("Elem1", "Elem2")] = 0
        _map[("Elem1", "Elem3")] = 3
        self.assertNotEqual(_map, self.map)

        _map = CaseAndSpaceInsensitiveTuplesDict()
        _map[("Elem 1", "Elem1")] = "Value1"
        _map[("ELEM 1", "E L E M 2")] = "wrong"
        _map[(" Elem1 ", "Elem 3")] = 3
        self.assertNotEqual(_map, self.map)

        _map = CaseAndSpaceInsensitiveTuplesDict()
        _map[("wrong", "Elem1")] = "Value1"
        _map[("Elem1", "Elem2")] = 2
        _map[("Elem1", "Elem3")] = 3
        self.assertNotEqual(_map, self.map)

    def test_get(self):
        self.assertEqual(self.map[("ELEM1", "ELEM1")], "Value1")
        self.assertEqual(self.map[("elem1", "e l e m 2")], 2)
        self.assertEqual(self.map[("e l e M 1", "elem3")], 3)
        self.assertNotEqual(self.map[("e l e M 1", "elem3")], 2)

    def test_iter(self):
        for tuple1, tuple2 in zip(self.map, [("Elem1", "Elem1"),
                                             ("Elem1", "Elem2"),
                                             ("Elem1", "Elem3")]):
            self.assertEqual(tuple1, tuple2)

    def test_len(self):
        self.assertEqual(len(self.map), 3)

    def test_set(self):
        self.map[("E L E M 1", "E L E M 2")] = 3
        self.assertEqual(self.map[("Elem1", "Elem2")], 3)

    def test_copy(self):
        c = self.map.copy()
        self.assertIsNot(c, self.map)
        self.assertEqual(c, self.map)

    def test_full(self):
        mdx_rows = '[}Clients].Members'
        mdx_columns = '[}Groups].Members'
        cube_name = '[}ClientGroups]'
        mdx = 'SELECT {} ON ROWS, {} ON COLUMNS FROM {}'.format(
            mdx_rows, mdx_columns, cube_name)
        data = self.tm1.cubes.cells.execute_mdx(mdx)

        # Get
        if self.tm1.version[0:2] == '10':
            coordinates = ('[}Clients].[ad min]', '[}Groups].[ADM IN]')
        else:
            coordinates = ('[}Clients].[}Clients].[ad min]',
                           '[}Groups].[}Groups].[ADM IN]')
        self.assertIsNotNone(data[coordinates])

        # Delete
        if self.tm1.version[0:2] == '10':
            coordinates = ('[}clients].[}clients].[admin]',
                           '[}groups].[}groups].[admin]')
        else:
            coordinates = ('[}clients].[}clients].[admin]',
                           '[}groups].[}groups].[admin]')
        self.assertTrue(coordinates in data)
        del data[coordinates]
        self.assertFalse(coordinates in data)

        # Copy
        data_cloned = data.copy()
        self.assertTrue(data_cloned == data)
        self.assertFalse(data_cloned is data)

    @classmethod
    def tearDownClass(cls):
        cls.tm1.logout()
示例#14
0
class TestCaseAndSpaceInsensitiveTuplesDict(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.tm1 = TM1Service(**config['tm1srv01'])

    def setUp(self):
        self.map = CaseAndSpaceInsensitiveTuplesDict()
        self.map[("Elem1", "Elem1")] = "Value1"
        self.map[("Elem1", "Elem2")] = 2
        self.map[("Elem1", "Elem3")] = 3

    def tearDown(self):
        del self.map

    def test_del(self):
        self.assertIn(("Elem1", "Elem1"), self.map)
        self.assertIn(("Elem1", "Elem2"), self.map)
        self.assertIn(("Elem1", "Elem3"), self.map)
        del self.map[("El em1", "ELEM1")]
        del self.map[("El em1", "E L E M 2")]
        del self.map[("El em1", " eLEM3")]
        self.assertNotIn(("Elem1", "Elem1"), self.map)
        self.assertNotIn(("Elem1", "Elem2"), self.map)
        self.assertNotIn(("Elem1", "Elem3"), self.map)

    def test_eq(self):
        map = CaseAndSpaceInsensitiveTuplesDict()
        map[("Elem1", "Elem1")] = "Value1"
        map[("Elem1", "Elem2")] = 2
        map[("Elem1", "Elem3")] = 3
        self.assertEqual(map, self.map)

        map = CaseAndSpaceInsensitiveTuplesDict()
        map[("Elem 1", "Elem1")] = "Value1"
        map[("ELEM 1", "E L E M 2")] = 2
        map[(" Elem1 ", "Elem 3")] = 3
        self.assertEqual(map, self.map)

    def test_ne(self):
        map = CaseAndSpaceInsensitiveTuplesDict()
        map[("Elem1", "Elem1")] = "Value1"
        map[("Elem1", "Elem2")] = 0
        map[("Elem1", "Elem3")] = 3
        self.assertNotEqual(map, self.map)

        map = CaseAndSpaceInsensitiveTuplesDict()
        map[("Elem 1", "Elem1")] = "Value1"
        map[("ELEM 1", "E L E M 2")] = "wrong"
        map[(" Elem1 ", "Elem 3")] = 3
        self.assertNotEqual(map, self.map)

        map = CaseAndSpaceInsensitiveTuplesDict()
        map[("wrong", "Elem1")] = "Value1"
        map[("Elem1", "Elem2")] = 2
        map[("Elem1", "Elem3")] = 3
        self.assertNotEqual(map, self.map)

    def test_get(self):
        self.assertEqual(self.map[("ELEM1", "ELEM1")], "Value1")
        self.assertEqual(self.map[("elem1", "e l e m 2")], 2)
        self.assertEqual(self.map[("e l e M 1", "elem3")], 3)
        self.assertNotEqual(self.map[("e l e M 1", "elem3")], 2)

    def test_iter(self):
        for tuple1, tuple2 in zip(self.map, [("Elem1", "Elem1"), ("Elem1", "Elem2"), ("Elem1", "Elem3")]):
            self.assertEqual(tuple1, tuple2)

    def test_len(self):
        self.assertEqual(len(self.map), 3)

    def test_set(self):
        self.map[("E L E M 1", "E L E M 2")] = 3
        self.assertEqual(self.map[("Elem1", "Elem2")], 3)

    def test_copy(self):
        c = self.map.copy()
        self.assertIsNot(c, self.map)
        self.assertEqual(c, self.map)

    def test_full(self):
        mdx_rows = '[}Clients].Members'
        mdx_columns = '[}Groups].Members'
        cube_name = '[}ClientGroups]'
        mdx = 'SELECT {} ON ROWS, {} ON COLUMNS FROM {}'.format(mdx_rows, mdx_columns, cube_name)
        data = self.tm1.cubes.cells.execute_mdx(mdx)

        # Get
        if self.tm1.version[0:2] == '10':
            coordinates = ('[}Clients].[ad min]', '[}Groups].[ADM IN]')
        else:
            coordinates = ('[}Clients].[}Clients].[ad min]', '[}Groups].[}Groups].[ADM IN]')
        self.assertIsNotNone(data[coordinates])

        # Delete
        if self.tm1.version[0:2] == '10':
            coordinates = ('[}clients].[}clients].[admin]', '[}groups].[}groups].[admin]')
        else:
            coordinates = ('[}clients].[}clients].[admin]', '[}groups].[}groups].[admin]')
        self.assertTrue(coordinates in data)
        del data[coordinates]
        self.assertFalse(coordinates in data)

        # Copy
        data_cloned = data.copy()
        self.assertTrue(data_cloned == data)
        self.assertFalse(data_cloned is data)

    @classmethod
    def tearDownClass(cls):
        cls.tm1.logout()