forked from Zhubei_iXD/report_skill_expm
feat: enhance TreeNode and WeekWorkPackageLogger to support rejection status tracking
This commit is contained in:
parent
8d1e1e7163
commit
5a3824a85f
|
|
@ -65,7 +65,7 @@ class InvalidDateRangeError(WorkPackageLogError):
|
|||
|
||||
class TreeNode:
|
||||
"""Represents a node in the hierarchy tree"""
|
||||
def __init__(self, node_id: str, name: str, node_type: str, hours: float = 0.0, entries: List = None, completion: str = None):
|
||||
def __init__(self, node_id: str, name: str, node_type: str, hours: float = 0.0, entries: List = None, completion: str = None, logger = None):
|
||||
self.id = node_id
|
||||
self.name = name
|
||||
self.type = node_type # 'project' or 'work_package'
|
||||
|
|
@ -75,6 +75,7 @@ class TreeNode:
|
|||
self.parent = None
|
||||
self._raw_completion = completion # API原始完成度資料
|
||||
self._calculated_completion = None # 快取計算後的完成度
|
||||
self.logger = logger # 用於檢查駁回狀態
|
||||
|
||||
def add_child(self, child):
|
||||
child.parent = self
|
||||
|
|
@ -87,6 +88,13 @@ class TreeNode:
|
|||
total += child.get_total_hours()
|
||||
return total
|
||||
|
||||
def _is_rejected(self) -> bool:
|
||||
"""檢查此節點是否為駁回狀態"""
|
||||
if self.type != 'work_package' or not self.logger:
|
||||
return False
|
||||
|
||||
return self.logger._is_work_package_rejected(self.id)
|
||||
|
||||
def get_completion(self, visited_nodes: set = None) -> str:
|
||||
"""
|
||||
Recursively compute the completion percentage for this node.
|
||||
|
|
@ -138,20 +146,15 @@ class TreeNode:
|
|||
self._calculated_completion = "100%"
|
||||
return "100%"
|
||||
|
||||
# 2. 檢查是否有設定完成度
|
||||
if self._raw_completion and self._raw_completion != "no-info":
|
||||
self._calculated_completion = self._raw_completion
|
||||
return self._raw_completion
|
||||
|
||||
# 3. 如果是no-info,則根據子項目計算
|
||||
if not self.children:
|
||||
# 無子項目,最終層,給0%
|
||||
self._calculated_completion = "0%"
|
||||
return "0%"
|
||||
else:
|
||||
# 有子項目,計算所有子項目完成度的平均值
|
||||
# 2. 如果有子項目,需要重新計算(跳過駁回的子項目)
|
||||
if self.children:
|
||||
# 有子項目,計算所有子項目完成度的平均值(跳過駁回的子項目)
|
||||
child_completions = []
|
||||
for child in self.children:
|
||||
# 跳過駁回狀態的子項目
|
||||
if child._is_rejected():
|
||||
continue
|
||||
|
||||
child_completion_str = child.get_completion(visited_nodes)
|
||||
|
||||
# 將完成度字串轉換為數值
|
||||
|
|
@ -168,10 +171,18 @@ class TreeNode:
|
|||
if child_completions:
|
||||
avg_completion = sum(child_completions) / len(child_completions)
|
||||
self._calculated_completion = f"{avg_completion:.0f}%"
|
||||
else:
|
||||
self._calculated_completion = "0%"
|
||||
|
||||
return self._calculated_completion
|
||||
return self._calculated_completion
|
||||
# 如果所有子項目都被駁回,則使用原始完成度或0%
|
||||
|
||||
# 3. 檢查是否有設定完成度(沒有子項目或所有子項目都被駁回時使用)
|
||||
# 如果設定為 0%,視為未設定,仍會嘗試從子項目計算
|
||||
if self._raw_completion and self._raw_completion != "no-info" and self._raw_completion != "0%":
|
||||
self._calculated_completion = self._raw_completion
|
||||
return self._raw_completion
|
||||
|
||||
# 4. 沒有任何完成度資訊,給0%
|
||||
self._calculated_completion = "0%"
|
||||
return "0%"
|
||||
|
||||
def to_dict(self) -> Dict:
|
||||
"""Convert tree node to dictionary for JSON serialization"""
|
||||
|
|
@ -421,6 +432,24 @@ class WeekWorkPackageLogger:
|
|||
return {}
|
||||
|
||||
|
||||
def _is_work_package_rejected(self, work_package_id: str) -> bool:
|
||||
"""檢查 work package 是否為駁回狀態"""
|
||||
wp_data = self._get_work_package_details(work_package_id)
|
||||
if not wp_data:
|
||||
return False
|
||||
|
||||
# 檢查狀態是否包含駁回關鍵字
|
||||
status_link = wp_data.get('_links', {}).get('status')
|
||||
if status_link and status_link.get('href'):
|
||||
status_data = self._get_status_details(status_link['href'])
|
||||
if status_data:
|
||||
status_name = status_data.get('name', '').lower()
|
||||
if any(keyword in status_name for keyword in REJECTION_KEYWORDS):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _get_work_package_completion(self, work_package_id: str) -> str:
|
||||
"""獲取work package的原始完成度資訊"""
|
||||
wp_data = self._get_work_package_details(work_package_id)
|
||||
|
|
@ -503,7 +532,7 @@ class WeekWorkPackageLogger:
|
|||
wp_completion = self._get_work_package_completion(work_package_id)
|
||||
|
||||
# 創建節點
|
||||
node = TreeNode(work_package_id, wp_name, 'work_package', node_hours, entries, wp_completion)
|
||||
node = TreeNode(work_package_id, wp_name, 'work_package', node_hours, entries, wp_completion, self)
|
||||
wp_nodes[work_package_id] = node
|
||||
|
||||
# 獲取所有子項目
|
||||
|
|
@ -643,7 +672,7 @@ class WeekWorkPackageLogger:
|
|||
|
||||
for proj_id, proj_name in project_path:
|
||||
if proj_id not in project_trees:
|
||||
project_trees[proj_id] = TreeNode(proj_id, proj_name, 'project')
|
||||
project_trees[proj_id] = TreeNode(proj_id, proj_name, 'project', logger=self)
|
||||
|
||||
if current_project_node is None:
|
||||
# This is the root project in the path
|
||||
|
|
@ -657,7 +686,7 @@ class WeekWorkPackageLogger:
|
|||
# If no project path found (no hierarchy support), create a simple node
|
||||
if not project_path and project_id:
|
||||
if project_id not in project_trees:
|
||||
project_trees[project_id] = TreeNode(project_id, f"Project-{project_id}", 'project')
|
||||
project_trees[project_id] = TreeNode(project_id, f"Project-{project_id}", 'project', logger=self)
|
||||
current_project_node = project_trees[project_id]
|
||||
|
||||
# 建構完整的 work package 層級結構
|
||||
|
|
|
|||
Loading…
Reference in New Issue