diff --git a/TM1py/Services/HierarchyService.py b/TM1py/Services/HierarchyService.py index c638038c..1879f823 100644 --- a/TM1py/Services/HierarchyService.py +++ b/TM1py/Services/HierarchyService.py @@ -488,12 +488,20 @@ def update_or_create_hierarchy_from_dataframe( # identify level columns level_columns = [] level_weight_columns = [] + # sort to assure right order of levels - for column in sorted(df.columns, reverse=True): + sorted_level_columns = sorted( + [col for col in df.columns if any(char.isdigit() for char in col)], # Filter columns with digits + key=lambda x: int(''.join(filter(str.isdigit, x))), # Sort based on numeric part + reverse=True # Descending order + ) + + # Process sorted columns + for column in sorted_level_columns: if column.lower().startswith('level') and column[5:8].isdigit(): - if len(column) == 8: + if len(column) == 8: # "LevelXXX" level_columns.append(column) - elif len(column) == 15 and column.lower().endswith('_weight'): + elif len(column) == 15 and column.lower().endswith('_weight'): # "LevelXXX_weight" level_weight_columns.append(column) # case: no level weight columns. All weights are 1 @@ -503,9 +511,6 @@ def update_or_create_hierarchy_from_dataframe( level_weight_columns.append(level_weight_column) df[level_weight_column] = 1 - if not len(level_columns) == len(level_weight_columns): - raise ValueError("Number of level columns must be equal to number of level weight columns") - if verify_edges: self._validate_edges(df=df[[element_column, *level_columns]]) diff --git a/Tests/HierarchyService_test.py b/Tests/HierarchyService_test.py index d69aa5b5..26738918 100644 --- a/Tests/HierarchyService_test.py +++ b/Tests/HierarchyService_test.py @@ -3,6 +3,7 @@ from contextlib import suppress from pathlib import Path +import pandas as pd from mdxpy import MdxBuilder, MdxHierarchySet from pandas import DataFrame @@ -513,6 +514,33 @@ def test_update_or_create_hierarchy_from_dataframe(self): hierarchy_name=self.region_dimension_name) self._verify_region_dimension(hierarchy) + def test_update_or_create_hierarchy_from_dataframe_non_standard_level_order(self): + columns = [self.region_dimension_name, "ElementType", "Alias:a", "Currency:s", "population:n", "Level001", + "level000", "level001_weight", "level000_weight"] + data = [ + ['France', "Numeric", "Frankreich", "EUR", 60_000_000, "Europe", "World", 1, 1], + ['Switzerland', 'Numeric', "Schweiz", "CHF", 9_000_000, "Europe", "World", 1, 1], + ['Germany', 'Numeric', "Deutschland", "EUR", 84_000_000, "Europe", "World", 1, 1], + ] + df = DataFrame(data=data, columns=columns) + + df = pd.DataFrame(df[[self.region_dimension_name, "ElementType", "Alias:a", "Currency:s", "population:n", "level000", + "Level001", "level000_weight", "level001_weight"]]) + print(df.to_markdown()) + self.tm1.hierarchies.update_or_create_hierarchy_from_dataframe( + dimension_name=self.region_dimension_name, + hierarchy_name=self.region_dimension_name, + df=df, + element_column=self.region_dimension_name, + element_type_column="ElementType", + unwind=True + ) + + hierarchy = self.tm1.hierarchies.get( + dimension_name=self.region_dimension_name, + hierarchy_name=self.region_dimension_name) + self._verify_region_dimension(hierarchy) + def test_update_or_create_hierarchy_from_dataframe_existing_attributes(self): columns = [self.region_dimension_name, "ElementType", "Currency:s", "population:n", "level001", "level000", "level001_weight", "level000_weight"]