Skip to content

DimensionService

DimensionService(rest)

Bases: ObjectService

Service to handle Object Updates for TM1 Dimensions

Source code in TM1py/Services/DimensionService.py
def __init__(self, rest: RestService):
    super().__init__(rest)
    self.hierarchies = HierarchyService(rest)
    self.subsets = SubsetService(rest)

hierarchies = HierarchyService(rest) instance-attribute

subsets = SubsetService(rest) instance-attribute

create(dimension, **kwargs)

Create a dimension

Parameters:

Name Type Description Default
dimension Dimension

instance of TM1py.Dimension

required

Returns:

Type Description
Response

response

Source code in TM1py/Services/DimensionService.py
def create(self, dimension: Dimension, **kwargs) -> Response:
    """Create a dimension

    :param dimension: instance of TM1py.Dimension
    :return: response
    """
    # If Dimension exists. throw Exception
    if self.exists(dimension.name):
        raise RuntimeError("Dimension '{}' already exists".format(dimension.name))
    # If not all subsequent calls successful -> undo everything that has been done in this function
    try:
        # Create Dimension, Hierarchies, Elements, Edges.
        url = "/Dimensions"
        response = self._rest.POST(url, dimension.body, **kwargs)
        # Create ElementAttributes
        for hierarchy in dimension:
            if not case_and_space_insensitive_equals(hierarchy.name, "Leaves"):
                self.hierarchies.update_element_attributes(hierarchy, **kwargs)
    except TM1pyException as e:
        # undo everything if problem in step 1 or 2
        if self.exists(dimension.name, **kwargs):
            self.delete(dimension.name)
        raise e
    return response

create_element_attributes_through_ti(dimension, **kwargs)

Parameters:

Name Type Description Default
dimension Dimension

Instance of TM1py.Objects.Dimension class

required

Returns:

Type Description
Source code in TM1py/Services/DimensionService.py
def create_element_attributes_through_ti(self, dimension: Dimension, **kwargs):
    """

    :param dimension: Instance of TM1py.Objects.Dimension class
    :return:
    """
    process_service = ProcessService(self._rest)
    for h in dimension:
        statements = [
            "AttrInsert('{}', '', '{}', '{}');".format(dimension.name, ea.name, ea.attribute_type[0])
            for ea in h.element_attributes
        ]
        process_service.execute_ti_code(lines_prolog=statements, **kwargs)

delete(dimension_name, **kwargs)

Delete a dimension

Parameters:

Name Type Description Default
dimension_name str

Name of the dimension

required

Returns:

Type Description
Response
Source code in TM1py/Services/DimensionService.py
def delete(self, dimension_name: str, **kwargs) -> Response:
    """Delete a dimension

    :param dimension_name: Name of the dimension
    :return:
    """
    url = format_url("/Dimensions('{}')", dimension_name)
    return self._rest.DELETE(url, **kwargs)

execute_mdx(dimension_name, mdx, **kwargs)

Execute MDX against Dimension. Requires }ElementAttributes_ Cube of the dimension to exist !

Parameters:

Name Type Description Default
dimension_name str

Name of the Dimension

required
mdx str

valid Dimension-MDX Statement

required

Returns:

Type Description
List

List of Element names

Source code in TM1py/Services/DimensionService.py
def execute_mdx(self, dimension_name: str, mdx: str, **kwargs) -> List:
    """Execute MDX against Dimension.
    Requires }ElementAttributes_ Cube of the dimension to exist !

    :param dimension_name: Name of the Dimension
    :param mdx: valid Dimension-MDX Statement
    :return: List of Element names
    """

    warnings.warn(
        "execute_mdx() will be deprecated; use ElementService execute_set_mdx_element_names().",
        DeprecationWarning,
        stacklevel=2,
    )

    mdx_skeleton = (
        "SELECT "
        "{} ON ROWS, "
        "{{ [}}ElementAttributes_{}].DefaultMember }} ON COLUMNS  "
        "FROM [}}ElementAttributes_{}]"
    )
    mdx_full = mdx_skeleton.format(mdx, dimension_name, dimension_name)
    request = (
        "/ExecuteMDX?$expand=Axes("
        "$filter=Ordinal eq 1;"
        "$expand=Tuples($expand=Members($select=Ordinal;$expand=Element($select=Name))))"
    )
    payload = {"MDX": mdx_full}
    response = self._rest.POST(request, json.dumps(payload, ensure_ascii=False), **kwargs)
    raw_dict = response.json()
    return [row_tuple["Members"][0]["Element"]["Name"] for row_tuple in raw_dict["Axes"][0]["Tuples"]]

exists(dimension_name, **kwargs)

Check if dimension exists

Returns:

Type Description
bool
Source code in TM1py/Services/DimensionService.py
def exists(self, dimension_name: str, **kwargs) -> bool:
    """Check if dimension exists

    :return:
    """
    url = format_url("/Dimensions('{}')", dimension_name)
    return self._exists(url, **kwargs)

get(dimension_name, **kwargs)

Get a Dimension

Parameters:

Name Type Description Default
dimension_name str
required

Returns:

Type Description
Dimension
Source code in TM1py/Services/DimensionService.py
def get(self, dimension_name: str, **kwargs) -> Dimension:
    """Get a Dimension

    :param dimension_name:
    :return:
    """
    url = format_url("/Dimensions('{}')?$expand=Hierarchies($expand=*)", dimension_name)
    response = self._rest.GET(url, **kwargs)
    return Dimension.from_json(response.text)

get_all_names(skip_control_dims=False, **kwargs)

Ask TM1 Server for list of all dimension names

:skip_control_dims: bool, True to skip control dims :Returns: List of Strings

Source code in TM1py/Services/DimensionService.py
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("/{}?$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

get_number_of_dimensions(skip_control_dims=False, **kwargs)

Ask TM1 Server for number of dimensions

:skip_control_dims: bool, True to exclude control dims from count

Returns:

Type Description
int

Number of dimensions

Source code in TM1py/Services/DimensionService.py
def get_number_of_dimensions(self, skip_control_dims: bool = False, **kwargs) -> int:
    """Ask TM1 Server for number of dimensions

    :skip_control_dims: bool, True to exclude control dims from count
    :return: Number of dimensions
    """

    if skip_control_dims:
        response = self._rest.GET("/ModelDimensions()?$select=Name&$top=0&$count", **kwargs)
        return response.json()["@odata.count"]

    return int(self._rest.GET("/Dimensions/$count", **kwargs).text)

update(dimension, keep_existing_attributes=False, **kwargs)

Update an existing dimension

Parameters:

Name Type Description Default
dimension Dimension

instance of TM1py.Dimension

required
keep_existing_attributes

True to make sure existing attributes are not removed

False

Returns:

Type Description

None

Source code in TM1py/Services/DimensionService.py
def update(self, dimension: Dimension, keep_existing_attributes=False, **kwargs):
    """Update an existing dimension

    :param dimension: instance of TM1py.Dimension
    :param keep_existing_attributes: True to make sure existing attributes are not removed
    :return: None
    """
    # delete hierarchies that have been removed from the dimension object
    hierarchies_to_be_removed = CaseAndSpaceInsensitiveSet(
        *self.hierarchies.get_all_names(dimension.name, **kwargs)
    )
    for hierarchy in dimension.hierarchy_names:
        hierarchies_to_be_removed.discard(hierarchy)

    # update all Hierarchies except for the implicitly maintained 'Leaves' Hierarchy
    for hierarchy in dimension:
        if not case_and_space_insensitive_equals(hierarchy.name, "Leaves"):
            if self.hierarchies.exists(hierarchy.dimension_name, hierarchy.name, **kwargs):
                self.hierarchies.update(hierarchy, keep_existing_attributes=keep_existing_attributes, **kwargs)
            else:
                self.hierarchies.create(hierarchy, **kwargs)

    # Edge case: elements in leaves hierarchy that do not exist in other hierarchies
    if "Leaves" in dimension:
        existing_leaves = CaseAndSpaceInsensitiveSet(
            self.hierarchies.elements.get_leaf_element_names(dimension.name, "Leaves")
        )

        leaves_to_create = list()
        for leaf in dimension.get_hierarchy("Leaves"):
            if leaf.name not in existing_leaves:
                leaves_to_create.append(leaf)

        if leaves_to_create:
            self.hierarchies.elements.add_elements(
                dimension_name=dimension.name, hierarchy_name="Leaves", elements=leaves_to_create
            )

    for hierarchy_name in hierarchies_to_be_removed:
        if not case_and_space_insensitive_equals(hierarchy_name, "Leaves"):
            self.hierarchies.delete(dimension_name=dimension.name, hierarchy_name=hierarchy_name, **kwargs)

update_or_create(dimension, **kwargs)

update if exists else create

Parameters:

Name Type Description Default
dimension Dimension
required

Returns:

Type Description
Source code in TM1py/Services/DimensionService.py
def update_or_create(self, dimension: Dimension, **kwargs):
    """update if exists else create

    :param dimension:
    :return:
    """
    if self.exists(dimension_name=dimension.name, **kwargs):
        self.update(dimension=dimension, **kwargs)
    else:
        self.create(dimension=dimension, **kwargs)

uses_alternate_hierarchies(dimension_name, **kwargs)

Source code in TM1py/Services/DimensionService.py
def uses_alternate_hierarchies(self, dimension_name: str, **kwargs) -> bool:
    hierarchy_names = self.hierarchies.get_all_names(dimension_name, **kwargs)
    if len(hierarchy_names) > 1:
        return True

    return not case_and_space_insensitive_equals(dimension_name, hierarchy_names[0])