Allocation Categories¶
AllocationCategories(baze)
¶
Class used for handling allocation categories.
Source code in echo_baze/baze_root.py
def __init__(self, baze: e_bz.Baze) -> None:
"""Base class that all subclasses should inherit from.
Parameters
----------
baze : Baze
Top level object carrying all functionality and the connection handler.
"""
# check inputs
if not isinstance(baze, e_bz.Baze):
raise ValueError(f"baze must be of type Baze, not {type(baze)}")
self.baze: e_bz.Baze = baze
get(allocation_types, output_type='dict')
¶
Gets all allocation categories with detailed information.
The most useful keys/columns returned are:
- id
- available
- enabled
- excluded
- performance
- priority
- sortIndex
- uncategorized
- color
- parentCategoryId
- parentCategoryName
- parentAllocationTypeName
- childrenCategoryIds: dict[str, list[int]] like {children_availability_type: [child_category_id, ...], ...}
- childrenCategoryNames: dict[str, list[str]] like {children_availability_type: [child_category_name, ...], ...}
Parameters:
-
(allocation_types¶list[str]) –Desired allocation types to get the allocation categories from.
-
(output_type¶Literal['dict', 'DataFrame'], default:'dict') –Output type of the data. Can be one of ["dict", "DataFrame"] By default "dict"
Returns:
-
dict[str, dict[str, dict[str, Any]]]–In case output_type == "dict" it will return a dict with the following format: {allocation_type: {name: {attribute: value, ...}, ...}, ...}
-
DataFrame–In case output_type == "DataFrame" it will return a DataFrame with the following format: index = MultiIndex with levels ["allocation_type", "category"], columns = [attribute, ...]
Source code in echo_baze/allocation_categories.py
@validate_call
def get(
self,
allocation_types: list[str],
output_type: Literal["dict", "DataFrame"] = "dict",
) -> dict[str, dict[str, dict[str, Any]]] | DataFrame:
"""Gets all allocation categories with detailed information.
The most useful keys/columns returned are:
- id
- available
- enabled
- excluded
- performance
- priority
- sortIndex
- uncategorized
- color
- parentCategoryId
- parentCategoryName
- parentAllocationTypeName
- childrenCategoryIds: dict[str, list[int]] like {children_availability_type: [child_category_id, ...], ...}
- childrenCategoryNames: dict[str, list[str]] like {children_availability_type: [child_category_name, ...], ...}
Parameters
----------
allocation_types: list[str]
Desired allocation types to get the allocation categories from.
output_type : Literal["dict", "DataFrame"], optional
Output type of the data. Can be one of ["dict", "DataFrame"]
By default "dict"
Returns
-------
dict[str, dict[str, dict[str, Any]]]
In case output_type == "dict" it will return a dict with the following format: {allocation_type: {name: {attribute: value, ...}, ...}, ...}
DataFrame
In case output_type == "DataFrame" it will return a DataFrame with the following format: index = MultiIndex with levels ["allocation_type", "category"], columns = [attribute, ...]
"""
# getting the allocation types
allocation_types_def = self.baze.allocations.types.get()
allocation_type_ids = {k: v["id"] for k, v in allocation_types_def.items()}
if wrong_types := set(allocation_types) - set(allocation_type_ids):
raise ValueError(f"Invalid allocation types: {wrong_types}")
# getting a list of allocation types including the parents of the wanted ones
complete_allocation_types = []
for allocation_type in list(allocation_types_def.keys()):
# getting the parent type
parent_allocation_type = allocation_types_def[allocation_type].get("linkedTypeName")
if parent_allocation_type is not None:
complete_allocation_types.append(parent_allocation_type)
complete_allocation_types.append(allocation_type)
complete_allocation_types = list(set(complete_allocation_types))
# getting the allocation categories
results = {}
for allocation_type in complete_allocation_types:
endpoint = f"allocationCategories?Id={allocation_type_ids[allocation_type]}"
# getting the data
request_result = self.baze.conn.get(endpoint)
self._handle_http_errors(request_result)
# converting to dict
result: list[dict[str, Any]] = request_result.json()
bool_keys = ["uncategorized", "available", "enabled", "performance", "excluded"]
for entry, key in itertools.product(result, bool_keys):
entry[key] = bool(entry[key])
# converting to desired format
result_dict = {entry["name"]: entry for entry in result}
# adding to the results
results[allocation_type] = result_dict
# getting parentCategoryName
for allocation_type, categories in results.items():
# checking if the allocation type has a parent type
parent_allocation_type = allocation_types_def[allocation_type].get("linkedTypeName")
if parent_allocation_type is None:
continue
for attributes in categories.values():
parent_category_id = attributes.get("parentCategoryId")
if parent_category_id is None:
continue
for parent_category_name, parent_category_vals in results[parent_allocation_type].items():
if parent_category_vals["id"] == parent_category_id:
attributes["parentCategoryName"] = parent_category_name
attributes["parentAllocationTypeName"] = parent_allocation_type
break
# getting childrenCategoryIds and childrenCategoryNames
for allocation_type, categories in results.items():
# checking if the allocation type has a parent type
parent_allocation_type = allocation_types_def[allocation_type].get("linkedTypeName")
if parent_allocation_type is None:
continue
# iterating over the categories
for category, attributes in categories.items():
# adding this category to the children of the parent
parent_category_name = attributes.get("parentCategoryName")
if parent_category_name is None:
continue
if "childrenCategoryIds" not in results[parent_allocation_type][parent_category_name]:
results[parent_allocation_type][parent_category_name]["childrenCategoryIds"] = {}
if allocation_type not in results[parent_allocation_type][parent_category_name]["childrenCategoryIds"]:
results[parent_allocation_type][parent_category_name]["childrenCategoryIds"][allocation_type] = []
if "childrenCategoryNames" not in results[parent_allocation_type][parent_category_name]:
results[parent_allocation_type][parent_category_name]["childrenCategoryNames"] = {}
if allocation_type not in results[parent_allocation_type][parent_category_name]["childrenCategoryNames"]:
results[parent_allocation_type][parent_category_name]["childrenCategoryNames"][allocation_type] = []
# adding the current category to the children of the parent
results[parent_allocation_type][parent_category_name]["childrenCategoryIds"][allocation_type].append(attributes["id"])
results[parent_allocation_type][parent_category_name]["childrenCategoryNames"][allocation_type].append(category)
# filtering only the wanted allocation types
results = {allocation_type: categories for allocation_type, categories in results.items() if allocation_type in allocation_types}
# returning on the desired format
match output_type:
case "dict":
return results
case "DataFrame":
df = DataFrame.from_dict(
{
(alloc_type, category): attributes
for alloc_type, categories in results.items()
for category, attributes in categories.items()
},
orient="index",
)
df.index.names = ["allocation_type", "category"]
df = df.reset_index(drop=False).convert_dtypes(dtype_backend="pyarrow").set_index(["allocation_type", "category"])
return df
case _:
raise ValueError(f"Invalid output_type: {output_type}")
get_default_children(allocation_types)
¶
Gets the default child categories for the given allocation categories.
The most useful keys/columns returned are: - id - name
Parameters:
-
(allocation_types¶list[str]) –Desired allocation types to get the allocation categories from.
Returns:
-
dict[str, dict[str, dict[str, dict[str, str | int]]]]–Dictionary with the default child categories in the format {allocation_type: {category_name: {children_allocation_type: {id: id, name: name}, ...}, ...}
Source code in echo_baze/allocation_categories.py
@validate_call
def get_default_children(
self,
allocation_types: list[str],
) -> dict[str, dict[str, dict[str, dict[str, str | int]]]]:
"""Gets the default child categories for the given allocation categories.
The most useful keys/columns returned are:
- id
- name
Parameters
----------
allocation_types: list[str]
Desired allocation types to get the allocation categories from.
Returns
-------
dict[str, dict[str, dict[str, dict[str, str | int]]]]
Dictionary with the default child categories in the format {allocation_type: {category_name: {children_allocation_type: {id: id, name: name}, ...}, ...}
"""
# getting the allocation categories for the wanted types
all_alloc_types = self.baze.allocations.types.get_ids()
allocation_categories = self.get(allocation_types=list(all_alloc_types.keys()))
missing_allocation_types = set(allocation_types) - set(allocation_categories)
if missing_allocation_types:
raise ValueError(f"Invalid allocation types: {missing_allocation_types}")
# getting the default child categories
default_child_categories = {}
for allocation_type, categories in allocation_categories.items():
if allocation_type not in allocation_types:
continue
# adding allocation_type to the default child categories
default_child_categories[allocation_type] = {}
for category, attributes in categories.items():
# checking if the category has children
if "childrenCategoryIds" not in attributes:
continue
if category not in default_child_categories[allocation_type]:
default_child_categories[allocation_type][category] = {}
for child_allocation_type in attributes["childrenCategoryIds"]:
if child_allocation_type not in default_child_categories[allocation_type][category]:
default_child_categories[allocation_type][category][child_allocation_type] = {}
# finding the default child categories
if category in ALLOCATION_CHILD_DEFAULT_CATEGORIES:
# checking if the default child category exists in the children
if ALLOCATION_CHILD_DEFAULT_CATEGORIES[category] not in attributes["childrenCategoryNames"][child_allocation_type]:
raise ValueError(
f"Default child category {ALLOCATION_CHILD_DEFAULT_CATEGORIES[category]} not found in {category} children for {allocation_type}",
)
default_child = ALLOCATION_CHILD_DEFAULT_CATEGORIES[category]
# trying to find a category that matches the parent .UND
elif f"{category}.UND" in attributes["childrenCategoryNames"][child_allocation_type]:
default_child = f"{category}.UND"
else:
default_child = attributes["childrenCategoryNames"][child_allocation_type][0]
# getting the id of the default child category
default_child_category_id = attributes["childrenCategoryIds"][child_allocation_type][
attributes["childrenCategoryNames"][child_allocation_type].index(default_child)
]
# adding the default child category to the default child categories
default_child_categories[allocation_type][category][child_allocation_type] = {
"id": default_child_category_id,
"name": default_child,
}
return default_child_categories
get_ids(allocation_types)
¶
Gets a dictionary with all allocation category names and ids.
Returns:
-
dict[str, dict[str, int]]:–Dictionary with all allocation categories in the format {allocation_type: {name: id, ...}, ...}
Source code in echo_baze/allocation_categories.py
@validate_call
def get_ids(self, allocation_types: list[str]) -> dict[str, dict[str, int]]:
"""Gets a dictionary with all allocation category names and ids.
Returns
-------
dict[str, dict[str, int]]:
Dictionary with all allocation categories in the format {allocation_type: {name: id, ...}, ...}
"""
# getting the allocation types
allocation_type_ids = self.baze.allocations.types.get_ids()
if wrong_types := set(allocation_types) - set(allocation_type_ids):
raise ValueError(f"Invalid allocation types: {wrong_types}")
# getting the allocation categories
results = {}
for allocation_type in allocation_types:
endpoint = f"allocationCategories?Id={allocation_type_ids[allocation_type]}"
# getting the data
request_result = self.baze.conn.get(endpoint)
self._handle_http_errors(request_result)
# converting to dict
result: list[dict[str, Any]] = request_result.json()
# converting to desired format
result_dict = {entry["name"]: entry["id"] for entry in result}
# adding to the results
results[allocation_type] = result_dict
return results