PlanExe / src /wbs /wbs_populate.py
Simon Strandgaard
snapshot of PlanExe repo
6369972
import os
import json
import logging
from src.wbs.wbs_task import WBSTask, WBSProject
from src.uuid_util.is_valid_uuid import is_valid_uuid
logger = logging.getLogger(__name__)
class WBSPopulate:
@staticmethod
def project_from_level1_json(path_level1_json: str) -> WBSProject:
"""
Create a WBSProject from a level 1 JSON file.
"""
if not isinstance(path_level1_json, str):
raise ValueError("Invalid path_level1_json.")
with open(path_level1_json) as f:
d = json.load(f)
if not isinstance(d, dict):
raise ValueError("Expected a dictionary in the JSON file.")
root_task_id = d.get('id', None)
project_title = d.get('project_title', None)
final_deliverable = d.get('final_deliverable', None)
if not is_valid_uuid(root_task_id):
logger.error(f"Expected valid UUID, but got root_task_id: {root_task_id}")
root_task = WBSTask(
id = root_task_id,
description=project_title,
)
root_task.set_field('final_deliverable', final_deliverable)
return WBSProject(root_task)
@staticmethod
def extend_project_with_level2_json(wbs_project: WBSProject, path_level2_json: str):
"""
Grow the tree hierarchy with tasks from a WBS Level 2 JSON file.
"""
if not isinstance(wbs_project, WBSProject):
raise ValueError("Expected a WBSProject object.")
if not isinstance(path_level2_json, str):
raise ValueError("Invalid path_level2_json.")
with open(path_level2_json) as f:
task_item_list = json.load(f)
if not isinstance(task_item_list, list):
raise ValueError("Expected a list of tasks in the JSON file.")
for task_index, task_json in enumerate(task_item_list):
task_id = task_json.get('id', None)
if not is_valid_uuid(task_id):
logger.error(f"Expected valid UUID, but got task_id: {task_id} for task_index: {task_index}")
task_id = "MISSING_TASK_ID"
major_phase_title = task_json.get('major_phase_title', None)
if not major_phase_title:
logger.error(f"Missing 'major_phase_title' for task with id: {task_id} for task_index: {task_index}")
major_phase_title = "MISSING_MAJOR_PHASE_TITLE"
task = WBSTask(
id=task_id,
description=major_phase_title
)
wbs_project.root_task.task_children.append(task)
subtask_list = task_json.get('subtasks', None)
if not subtask_list:
logger.error(f"Missing 'subtasks' for task with id: {task_id}")
subtask_list = []
for subtask_json in subtask_list:
subtask_id = subtask_json.get('id', None)
if not is_valid_uuid(subtask_id):
logger.error(f"Expected valid UUID, but got subtask_id: {subtask_id} for task with id: {task_id}")
subtask_description = subtask_json.get('description', None)
if not subtask_description:
logger.error(f"Missing 'description' for subtask with id: {subtask_id}")
subtask_description = "MISSING_SUBTASK_DESCRIPTION"
subtask = WBSTask(
id=subtask_id,
description=subtask_description
)
task.task_children.append(subtask)
@staticmethod
def extend_project_with_dependencies_json(wbs_project: WBSProject, path_dependencies_json: str):
"""
Enrich the tree hierarchy with tasks from a dependencies JSON file.
Establish dependencies between tasks, with explanations why these dependencies exists.
"""
if not isinstance(wbs_project, WBSProject):
raise ValueError("Expected a WBSProject object.")
if not isinstance(path_dependencies_json, str):
raise ValueError("Invalid path_dependencies_json.")
with open(path_dependencies_json) as f:
d = json.load(f)
if not isinstance(d, dict):
raise ValueError("Expected a dictionary in the JSON file.")
task_item_list = d.get('task_dependency_details', [])
for task_json in task_item_list:
dependent_task_id = task_json['dependent_task_id']
dependent_task = wbs_project.root_task.find_task_by_id(dependent_task_id)
if not dependent_task:
logger.debug(f"ERROR: Task with id {dependent_task_id} not found. Cannot set dependencies.")
continue
depends_on_task_id_list = task_json['depends_on_task_id_list']
# Check if all uuid's in the depends_on_task_id_list are valid
for depends_on_task_id in depends_on_task_id_list:
depends_on_task = wbs_project.root_task.find_task_by_id(depends_on_task_id)
if not depends_on_task:
logger.debug(f"ERROR: Task with id {depends_on_task_id} not found. Cannot set dependency for task {dependent_task_id}.")
continue
dependent_task.set_field('depends_on_task_ids', depends_on_task_id_list)
depends_on_task_explanation_list = task_json['depends_on_task_explanation_list']
dependent_task.set_field('depends_on_task_explanations', depends_on_task_explanation_list)
@staticmethod
def extend_project_with_durations_json(wbs_project: WBSProject, path_durations_json: str):
"""
Enrich the task hierarchy with time estimates from a task_durations JSON file.
"""
if not isinstance(wbs_project, WBSProject):
raise ValueError("Expected a WBSProject object.")
if not isinstance(path_durations_json, str):
raise ValueError("Invalid path_dependencies_json.")
with open(path_durations_json) as f:
task_duration_list = json.load(f)
if not isinstance(task_duration_list, list):
raise ValueError("Expected a list in the JSON file.")
# logger.debug(f"task_duration_list length: {len(task_duration_list)}")
for task_duration_index, task_duration_json in enumerate(task_duration_list):
task_id = task_duration_json.get('task_id', None)
if not is_valid_uuid(task_id):
logger.error(f"Expected valid UUID, but got task_id: {task_id} for task_duration_index: {task_duration_index}")
continue
task = wbs_project.root_task.find_task_by_id(task_id)
if not task:
logger.error(f"Task with id {task_id} not found in WBSProject. Cannot set duration fields.")
continue
delay_risks = task_duration_json.get('delay_risks', None)
if delay_risks:
task.set_field('delay_risks', delay_risks)
mitigation_strategy = task_duration_json.get('mitigation_strategy', None)
if mitigation_strategy:
task.set_field('mitigation_strategy', mitigation_strategy)
days_min = task_duration_json.get('days_min', None)
if days_min:
task.set_field('days_min', days_min)
days_max = task_duration_json.get('days_max', None)
if days_max:
task.set_field('days_max', days_max)
days_realistic = task_duration_json.get('days_realistic', None)
if days_realistic:
task.set_field('days_realistic', days_realistic)
@staticmethod
def extend_project_with_decomposed_tasks_json(wbs_project: WBSProject, path_decomposed_tasks_json: str):
"""
Enrich the task hierarchy with more subtasks from a decomposed_tasks JSON file.
"""
if not isinstance(wbs_project, WBSProject):
raise ValueError("Expected a WBSProject object.")
if not isinstance(path_decomposed_tasks_json, str):
raise ValueError("Invalid path_decomposed_tasks_json.")
with open(path_decomposed_tasks_json) as f:
task_list = json.load(f)
if not isinstance(task_list, list):
raise ValueError("Expected a list in the JSON file.")
# logger.debug(f"Number of subtasks to be added. count: {len(task_list)}")
for task_index, task_json in enumerate(task_list):
task_id = task_json.get('id', None)
if not is_valid_uuid(task_id):
logger.error(f"Expected valid UUID, but got task_id: {task_id} for task_index: {task_index}. Cannot create subtask.")
continue
task_parent_id = task_json.get('parent_id', None)
if not is_valid_uuid(task_parent_id):
logger.error(f"Expected valid UUID, but got task_parent_id: {task_parent_id} for task_index: {task_index}. Cannot create subtask.")
continue
subtask_name = task_json.get('name', None)
if not subtask_name:
logger.error(f"Missing 'name' for task with id: {task_id}.")
subtask_name = "MISSING_SUBTASK_NAME"
parent_task = wbs_project.root_task.find_task_by_id(task_parent_id)
if not parent_task:
logger.error(f"Task with id {task_parent_id} not found. Cannot create subtask for child task {task_id}.")
continue
task = WBSTask(
id=task_id,
description=subtask_name
)
subtask_detailed_description = task_json.get('description', None)
if subtask_detailed_description:
task.set_field('detailed_description', subtask_detailed_description)
subtask_resources_needed = task_json.get('resources_needed', None)
if subtask_resources_needed:
task.set_field('resources_needed', subtask_resources_needed)
parent_task.task_children.append(task)
# logger.debug(f"Added task {task_id} to parent task {task_parent_id}")