import pandas as pd
from typing import Optional
from typing import List
from typing import Dict
class CategoryNode:
def __init__(self, name : str, level : int, parentNode : Optional["CategoryNode"] = None):
self.name : str = name
self.level : int = level
self.parentNode : Optional[CategoryNode] = parentNode
self.childNodeList : List[CategoryNode] = []
self.costCodeList : List[str] = []
self.nodeCount : int = 0
self.nodePathList : List[CategoryNode] = self._initializeNodePathList()
def _initializeNodePathList(self) -> List["CategoryNode"]:
nodePathList = []
currentParentNode = self.parentNode
while currentParentNode is not None:
nodePathList.insert(0, currentParentNode)
currentParentNode = currentParentNode.parentNode
nodePathList.append(self)
return nodePathList
def addChild(self, childNode : "CategoryNode") -> None:
self.childNodeList.append(childNode)
def addCostCode(self, costCode : str) -> None:
if costCode and costCode not in self.costCodeList:
self.costCodeList.append(costCode)
def getAncestorNodeList(self) -> List["CategoryNode"]:
return self.nodePathList
class Category:
def __init__(self):
self.rootNodeList : List[CategoryNode] = []
self.costCodeDictionary : Dict[str, CategoryNode] = {}
def addRootNode(self, rootNode : CategoryNode) -> None:
self.rootNodeList.append(rootNode)
def addCodeCode(self, costCode : str, node : CategoryNode) -> None:
self.costCodeDictionary[costCode] = node
def setNodeCount(self) -> None:
def calculateNodeCount(node : CategoryNode) -> int:
count = 1
for child in node.childNodeList:
count += calculateNodeCount(child)
node.nodeCount = count
return count
for rootNode in self.rootNodeList:
calculateNodeCount(rootNode)
def createCategory(sourceFilePath : str) -> Category:
sourceDataFrame = pd.read_excel(sourceFilePath)
category = Category()
nodeDictionary = {}
for _, rowSeries in sourceDataFrame.iterrows():
currentParentNode = None
currentPathList = []
for level in range(1, 6): # 구분1 ~ 구분5
columnName = f"구분{level}"
value = rowSeries[columnName]
if pd.isna(value):
break
currentPathList.append(value)
pathKeyTuple = tuple(currentPathList)
if pathKeyTuple not in nodeDictionary:
newNode = CategoryNode(value, level - 1, currentParentNode)
nodeDictionary[pathKeyTuple] = newNode
if currentParentNode:
currentParentNode.addChild(newNode)
else:
if newNode.name not in [rootNode.name for rootNode in category.rootNodeList]:
category.addRootNode(newNode)
currentParentNode = nodeDictionary[pathKeyTuple]
costCode = rowSeries["비용코드"]
if pd.notna(costCode) and currentParentNode:
currentParentNode.addCostCode(costCode)
category.addCodeCode(costCode, currentParentNode)
category.setNodeCount()
return category
def printCategoryNode(node : CategoryNode, indent : str = "") -> None:
print(f"{indent}{node.name} (Level : {node.level}, Node Count : {node.nodeCount})", end = "")
if node.costCodeList:
print(f", Cost Code List : {node.costCodeList}")
else:
print()
for child in node.childNodeList:
printCategoryNode(child, indent + " ")
def printNodePathList(node : CategoryNode) -> str:
nodePathString = ""
for node in node.nodePathList:
if len(nodePathString) == 0:
nodePathString += node.name
else:
nodePathString += f",{node.name}"
return nodePathString
def printCostCodeDictionary(category : Category) -> None:
for costCode, node in category.costCodeDictionary.items():
print(f"Cost Code : {costCode} -> Node : {node.name} (Level : {node.level}, Node Path List : {printNodePathList(node)})")
sourceFilePath = "source.xlsx"
category = createCategory(sourceFilePath)
for rootNode in category.rootNodeList:
printCategoryNode(rootNode)
print()
printCostCodeDictionary(category)
"""
A (Level : 0, Node Count : 7)
A1 (Level : 1, Node Count : 4)
A2 (Level : 2, Node Count : 3)
A3 (Level : 3, Node Count : 2)
A4 (Level : 4, Node Count : 1), Cost Code List : ['CFA01', 'CFA02', 'CFA03']
A5 (Level : 1, Node Count : 2)
A6 (Level : 2, Node Count : 1), Cost Code List : ['CFA04', 'CFA05']
B (Level : 0, Node Count : 4)
B1 (Level : 1, Node Count : 2)
B2 (Level : 2, Node Count : 1), Cost Code List : ['CFA06', 'CFA07']
B3 (Level : 1, Node Count : 1), Cost Code List : ['CFA08']
C (Level : 0, Node Count : 7)
C1 (Level : 1, Node Count : 3)
C2 (Level : 2, Node Count : 2)
C3 (Level : 3, Node Count : 1), Cost Code List : ['CFA09', 'CFA10']
C4 (Level : 1, Node Count : 3)
C5 (Level : 2, Node Count : 2)
C6 (Level : 3, Node Count : 1), Cost Code List : ['CFA11', 'CFA12']
Cost Code : CFA01 -> Node : A4 (Level : 4, Node Path List : A,A1,A2,A3,A4)
Cost Code : CFA02 -> Node : A4 (Level : 4, Node Path List : A,A1,A2,A3,A4)
Cost Code : CFA03 -> Node : A4 (Level : 4, Node Path List : A,A1,A2,A3,A4)
Cost Code : CFA04 -> Node : A6 (Level : 2, Node Path List : A,A5,A6)
Cost Code : CFA05 -> Node : A6 (Level : 2, Node Path List : A,A5,A6)
Cost Code : CFA06 -> Node : B2 (Level : 2, Node Path List : B,B1,B2)
Cost Code : CFA07 -> Node : B2 (Level : 2, Node Path List : B,B1,B2)
Cost Code : CFA08 -> Node : B3 (Level : 1, Node Path List : B,B3)
Cost Code : CFA09 -> Node : C3 (Level : 3, Node Path List : C,C1,C2,C3)
Cost Code : CFA10 -> Node : C3 (Level : 3, Node Path List : C,C1,C2,C3)
Cost Code : CFA11 -> Node : C6 (Level : 3, Node Path List : C,C4,C5,C6)
Cost Code : CFA12 -> Node : C6 (Level : 3, Node Path List : C,C4,C5,C6)
"""