Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unwind consolidations in hier update #1188

Merged
merged 2 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 50 additions & 12 deletions TM1py/Services/HierarchyService.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import json
import math
from typing import Dict, Tuple, List, Optional
from typing import Dict, Tuple, List, Optional, Iterable

import networkx as nx
from requests import Response
Expand All @@ -22,7 +22,7 @@
from TM1py.Services.SubsetService import SubsetService
from TM1py.Utils.Utils import case_and_space_insensitive_equals, format_url, CaseAndSpaceInsensitiveDict, \
CaseAndSpaceInsensitiveSet, CaseAndSpaceInsensitiveTuplesDict, require_pandas, require_data_admin, \
lower_and_drop_spaces, require_ops_admin, verify_version
require_ops_admin, verify_version


class HierarchyService(ObjectService):
Expand Down Expand Up @@ -353,8 +353,9 @@ def remove_edges_under_consolidation(self, dimension_name: str, hierarchy_name:
hierarchy = self.get(dimension_name, hierarchy_name)
from TM1py.Services import ElementService
element_service = ElementService(self._rest)
elements_under_consolidations = CaseAndSpaceInsensitiveSet(element_service.get_members_under_consolidation(dimension_name, hierarchy_name,
consolidation_element))
elements_under_consolidations = CaseAndSpaceInsensitiveSet(
element_service.get_members_under_consolidation(dimension_name, hierarchy_name,
consolidation_element))
elements_under_consolidations.add(consolidation_element)
remove_edges = []
for (parent, component) in hierarchy.edges:
Expand Down Expand Up @@ -427,8 +428,10 @@ def update_or_create_hierarchy_from_dataframe(
verify_unique_elements: bool = False,
verify_edges: bool = True,
element_type_column: str = 'ElementType',
unwind: bool = False,
update_attribute_types: bool = False):
unwind_all: bool = False,
unwind_consolidations: Iterable = None,
update_attribute_types: bool = False,
**kwargs):
""" Update or Create a hierarchy based on a dataframe, while never deleting existing elements.

:param dimension_name:
Expand All @@ -455,8 +458,11 @@ def update_or_create_hierarchy_from_dataframe(
Abort early if element names are not unique
:param verify_edges:
Abort early if edges contain a circular reference
:param unwind: bool
:param unwind_all: bool
Unwind hierarch before creating new edges
:param unwind_consolidations: list
Unwind a list of specific consolidations in the hierarchy before creating new edges,
if unwind_all is true, this list is ignored
:param update_attribute_types: bool
If True, function will delete and recreate attributes when a type change is requested.
By default, function will not delete attributes.
Expand Down Expand Up @@ -485,6 +491,17 @@ def update_or_create_hierarchy_from_dataframe(
if len(alias_columns) > 0:
self._validate_alias_uniqueness(df=df[[element_column, *alias_columns]])

# backward compatibility for unwind, the value for unwind would be assinged to unwind_all. expected type is bool
if "unwind" in kwargs:
unwind_all = kwargs["unwind"]

if unwind_consolidations:
if isinstance(unwind_consolidations, str) or not isinstance(unwind_consolidations, Iterable):
raise ValueError(
f"value for 'unwind_consolidations' must be an iterable (e.g., list), "
f"but received: '{unwind_consolidations}' of type {type(unwind_consolidations).__name__}"
)

# identify and sort level columns
level_columns = []
level_weight_columns = []
Expand Down Expand Up @@ -634,8 +651,29 @@ def update_or_create_hierarchy_from_dataframe(
sum_numeric_duplicates=False,
use_blob=True)

if unwind:
self.remove_all_edges(dimension_name, hierarchy_name)
if unwind_all:
self.remove_all_edges(dimension_name=dimension_name, hierarchy_name=hierarchy_name)
else:
if unwind_consolidations:
edges_to_delete = CaseAndSpaceInsensitiveTuplesDict()
for elem in unwind_consolidations:
if not self.elements.exists(
dimension_name=dimension_name,
hierarchy_name=hierarchy_name,
element_name=elem):
continue

edges_under_consolidation = self.elements.get_edges_under_consolidation(
dimension_name=dimension_name,
hierarchy_name=hierarchy_name,
consolidation=elem)
edges_to_delete.join(edges_under_consolidation)

self.elements.delete_edges(
dimension_name=dimension_name,
hierarchy_name=hierarchy_name,
edges=edges_to_delete,
use_blob=self.is_admin)

edges = CaseAndSpaceInsensitiveTuplesDict()
for element_name, *record in df[[element_column, *level_columns, *level_weight_columns]].itertuples(
Expand Down Expand Up @@ -666,16 +704,16 @@ def update_or_create_hierarchy_from_dataframe(
else:
raise ex

delete_edges = {
edges_to_delete = {
(k, v): w
for (k, v), w
in edges.items()
if w != current_edges.get((k, v), w)}
if delete_edges:
if edges_to_delete:
self.elements.delete_edges(
dimension_name=dimension_name,
hierarchy_name=hierarchy_name,
edges=delete_edges.keys(),
edges=edges_to_delete.keys(),
use_blob=self.is_admin)

new_edges = {
Expand Down
Loading