diff --git a/.claude/skills/week_work_package_log/work_package_logger.py b/.claude/skills/week_work_package_log/work_package_logger.py index 695502f..0ec4da2 100644 --- a/.claude/skills/week_work_package_log/work_package_logger.py +++ b/.claude/skills/week_work_package_log/work_package_logger.py @@ -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 層級結構