Skip to content

Commit

Permalink
Merge pull request #77 from kayjan/v0.10.3
Browse files Browse the repository at this point in the history
V0.10.3
  • Loading branch information
Kay Jan authored Aug 11, 2023
2 parents 317a4cf + 5ea7602 commit 0d2081e
Show file tree
Hide file tree
Showing 9 changed files with 330 additions and 9 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.10.3] - 2023-08-12
### Added
- Tree Constructor: `add_path_to_tree`, `dataframe_to_tree`, `dataframe_to_tree_by_relation` to allow custom node types that takes in constructor arguments.
### Changed
- Binary Tree: Able to accept node val of `str` type besides `int` type.

## [0.10.2] - 2023-08-11
### Fixed
- Tree Constructor: `nested_dict_to_tree` to throw TypeError if child_key is not list type.
Expand Down Expand Up @@ -297,6 +303,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Utility Iterator: Tree traversal methods.
- Workflow To Do App: Tree use case with to-do list implementation.

[0.10.3]: https://github.com/kayjan/bigtree/compare/0.10.2...0.10.3
[0.10.2]: https://github.com/kayjan/bigtree/compare/0.10.1...0.10.2
[0.10.1]: https://github.com/kayjan/bigtree/compare/0.10.0...0.10.1
[0.10.0]: https://github.com/kayjan/bigtree/compare/0.9.5...0.10.0
Expand Down
2 changes: 1 addition & 1 deletion bigtree/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "0.10.2"
__version__ = "0.10.3"

from bigtree.binarytree.construct import list_to_binarytree
from bigtree.dag.construct import dataframe_to_dag, dict_to_dag, list_to_dag
Expand Down
2 changes: 1 addition & 1 deletion bigtree/node/basenode.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ def get_attr(self, attr_name: str) -> Any:
(Any)
"""
try:
return self.__getattribute__(attr_name)
return getattr(self, attr_name)
except AttributeError:
return None

Expand Down
5 changes: 4 additions & 1 deletion bigtree/node/binarynode.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ def __init__(
children: Optional[List[Optional[T]]] = None,
**kwargs: Any,
):
self.val = int(name)
try:
self.val: Union[str, int] = int(name)
except ValueError:
self.val = str(name)
self.name = str(name)
self._sep = "/"
self.__parent: Optional[T] = None
Expand Down
2 changes: 1 addition & 1 deletion bigtree/node/dagnode.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ def get_attr(self, attr_name: str) -> Any:
(Any)
"""
try:
return self.__getattribute__(attr_name)
return getattr(self, attr_name)
except AttributeError:
return None

Expand Down
26 changes: 23 additions & 3 deletions bigtree/tree/construct.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,11 @@ def add_path_to_tree(
else:
node = find_child_by_name(parent_node, node_name)
if not node:
node = node_type(branch[idx])
if idx == len(branch) - 1:
node_name = node_attrs.pop("name", branch[idx])
node = node_type(node_name, **node_attrs)
else:
node = node_type(branch[idx])
node.parent = parent_node
parent_node = node
node.set_attrs(node_attrs)
Expand Down Expand Up @@ -803,7 +807,15 @@ def dataframe_to_tree(
)

root_name = data[path_col].values[0].split(sep)[0]
root_node = node_type(root_name)
root_node_data = data[data[path_col] == root_name]
if len(root_node_data):
root_node_kwargs = list(
root_node_data[attribute_cols].to_dict(orient="index").values()
)[0]
root_name = root_node_kwargs.pop("name", root_name)
root_node = node_type(root_name, **root_node_kwargs)
else:
root_node = node_type(root_name)
add_dataframe_to_tree_by_path(
root_node,
data,
Expand Down Expand Up @@ -920,7 +932,15 @@ def dataframe_to_tree_by_relation(
f"Unable to determine root node\nPossible root nodes: {root_names}"
)
root_name = root_names[0]
root_node = node_type(root_name)
root_node_data = data[data[child_col] == root_name]
if len(root_node_data):
root_node_kwargs = list(
root_node_data[attribute_cols].to_dict(orient="index").values()
)[0]
root_name = root_node_kwargs.pop("name", root_name)
root_node = node_type(root_name, **root_node_kwargs)
else:
root_node = node_type(root_name)

def retrieve_attr(_row: Dict[str, Any]) -> Dict[str, Any]:
node_attrs = _row.copy()
Expand Down
1 change: 1 addition & 0 deletions tests/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ class Constants:
ERROR_DUPLICATE_NAME = "There exists duplicate name with different attributes"
ERROR_DUPLICATE_PARENT = "There exists duplicate child with different parent where the child is also a parent node"
ERROR_NODE_TYPE = "Node type is not `NodeA`"
ERROR_CUSTOM_NODE_TYPE = "Node type is not `CustomNode`"
ERROR_PREFIX = "Invalid prefix, prefix should be unicode character or whitespace, otherwise specify one or more prefixes"
ERROR_PREFIX_LENGTH = "Tree string have different prefix length, check branch"
ERROR_JOIN_TYPE = "`join_type` must be one of 'inner' or 'left'"
Expand Down
32 changes: 30 additions & 2 deletions tests/node/test_basenode.py
Original file line number Diff line number Diff line change
Expand Up @@ -745,8 +745,8 @@ def assert_tree_structure_basenode_root_attr(
):
"""Test tree structure with age attributes"""
# Test describe()
expected = [("age", 90), ("name", "a")]
actual = root.describe(exclude_prefix="_")
expected = {("age", 90), ("name", "a")}
actual = set(root.describe(exclude_prefix="_"))
assert (
actual == expected
), f"Node description should be {expected}, but it is {actual}"
Expand All @@ -760,6 +760,34 @@ def assert_tree_structure_basenode_root_attr(
), f"Node name and age should be {expected}, but it is {actual}"


def assert_tree_structure_customnode_root_attr(
root,
a=("a", 90),
b=("b", 65),
c=("c", 60),
d=("d", 40),
e=("e", 35),
f=("f", 38),
g=("g", 10),
h=("h", 6),
):
"""Test tree structure with age attributes"""
# Test describe()
expected = {("custom_field", 90), ("custom_field_str", "a"), ("name", "a")}
actual = set(root.describe(exclude_prefix="_"))
assert (
actual == expected
), f"Node description should be {expected}, but it is {actual}"

# Test age attribute
expected_attrs = [a, b, d, e, g, h, c, f]
for node, expected in zip(preorder_iter(root), expected_attrs):
actual = node.get_attr("custom_field_str"), node.get_attr("custom_field")
assert (
actual == expected
), f"Node custom_field_str and custom_field should be {expected}, but it is {actual}"


def assert_tree_structure_basenode_self(self):
"""Test tree structure with self object"""
nodes = [self.a, self.b, self.c, self.d, self.e, self.f, self.g, self.h]
Expand Down
Loading

0 comments on commit 0d2081e

Please sign in to comment.