fix: build complete work package tree with child item retrieval

This commit is contained in:
MarioYang 2026-01-27 16:27:22 +08:00
parent b57bc6127d
commit 8d1e1e7163
1 changed files with 76 additions and 31 deletions

View File

@ -453,6 +453,69 @@ class WeekWorkPackageLogger:
# 3. 若都沒有則填 no-info
return NO_INFO
def _get_work_package_children(self, work_package_id: str) -> List[Dict]:
"""獲取 work package 的所有直接子項目"""
try:
# 使用 OpenProject API 獲取子項目
params = {
'filters': json.dumps([{
'parent': {
'operator': '=',
'values': [work_package_id]
}
}])
}
response = requests.get(
self.work_packages_endpoint,
params=params,
auth=self._get_auth(),
timeout=30
)
if response.status_code == 200:
data = response.json()
return data.get('_embedded', {}).get('elements', [])
else:
return []
except Exception as e:
print(f"⚠ Warning: Could not fetch children for work package {work_package_id}: {e}")
return []
def _build_complete_work_package_tree(self, work_package_id: str, wp_nodes: Dict, time_entries_by_wp: Dict) -> TreeNode:
"""遞歸建構完整的 work package 樹狀結構(包含所有子項目)"""
if work_package_id in wp_nodes:
return wp_nodes[work_package_id]
# 獲取 work package 詳細資料
wp_data = self._get_work_package_details(work_package_id)
if not wp_data:
return None
wp_name = wp_data.get('subject', f'WP-{work_package_id}')
# 檢查是否有時間記錄
entries = time_entries_by_wp.get(work_package_id, [])
node_hours = sum(entry['hours'] for entry in entries)
# 獲取完成度
wp_completion = self._get_work_package_completion(work_package_id)
# 創建節點
node = TreeNode(work_package_id, wp_name, 'work_package', node_hours, entries, wp_completion)
wp_nodes[work_package_id] = node
# 獲取所有子項目
children_data = self._get_work_package_children(work_package_id)
for child_data in children_data:
child_id = str(child_data.get('id'))
child_node = self._build_complete_work_package_tree(child_id, wp_nodes, time_entries_by_wp)
if child_node:
node.add_child(child_node)
return node
def _get_project_completion(self, project_id: str) -> str:
"""Project不計算完成度"""
return None
@ -597,41 +660,23 @@ class WeekWorkPackageLogger:
project_trees[project_id] = TreeNode(project_id, f"Project-{project_id}", 'project')
current_project_node = project_trees[project_id]
# Build work package hierarchy for this project
# current_project_node now points to the actual project where time was logged
# 建構完整的 work package 層級結構
wp_nodes = {}
root_wp_nodes = set()
for wp_id, entries in wp_entries.items():
# 首先找出所有根 work package通過檢查父子關係
for wp_id in wp_entries.keys():
wp_path = self._get_work_package_path(wp_id)
# Calculate total hours for this work package
total_hours = sum(entry['hours'] for entry in entries)
# Build work package hierarchy
current_wp_node = None
for wp_id_in_path, wp_name in wp_path:
if wp_id_in_path not in wp_nodes:
# Only attach entries to the actual work package (not parent work packages)
node_entries = entries if wp_id_in_path == wp_id else []
node_hours = total_hours if wp_id_in_path == wp_id else 0
wp_completion = self._get_work_package_completion(wp_id_in_path)
wp_nodes[wp_id_in_path] = TreeNode(wp_id_in_path, wp_name, 'work_package', node_hours, node_entries, wp_completion)
if current_wp_node is None:
current_wp_node = wp_nodes[wp_id_in_path]
else:
if wp_nodes[wp_id_in_path] not in current_wp_node.children:
current_wp_node.add_child(wp_nodes[wp_id_in_path])
current_wp_node = wp_nodes[wp_id_in_path]
# Attach work package hierarchy to the project where time was actually logged
if wp_path and current_project_node:
if wp_path:
root_wp_id = wp_path[0][0]
if root_wp_id in wp_nodes:
root_wp_node = wp_nodes[root_wp_id]
if root_wp_node not in current_project_node.children:
current_project_node.add_child(root_wp_node)
root_wp_nodes.add(root_wp_id)
# 為每個根 work package 建構完整樹狀結構
for root_wp_id in root_wp_nodes:
if current_project_node:
root_wp_node = self._build_complete_work_package_tree(root_wp_id, wp_nodes, wp_entries)
if root_wp_node and root_wp_node not in current_project_node.children:
current_project_node.add_child(root_wp_node)
return project_trees