626 lines
24 KiB
Python
626 lines
24 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from datetime import date, timedelta
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
from .config import GeneratorConfig
|
||
|
|
from .models import DatasetBundle, stable_id
|
||
|
|
from .registry import TABLE_SPECS
|
||
|
|
|
||
|
|
_PROJECT_TABLES = frozenset(
|
||
|
|
{
|
||
|
|
"contracts",
|
||
|
|
"ship-projects",
|
||
|
|
"milestones",
|
||
|
|
"wbs",
|
||
|
|
"blocks",
|
||
|
|
"zones",
|
||
|
|
"work-packages",
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
_PROJECT_SPECS = (
|
||
|
|
{
|
||
|
|
"code": "BH-SYN-2601",
|
||
|
|
"shipType": "210000 DWT 散货船",
|
||
|
|
"startOffset": 0,
|
||
|
|
"deliveryOffset": 348,
|
||
|
|
"priority": 8,
|
||
|
|
"shipOwner": "船东-SYN-A",
|
||
|
|
"status": "IN_PROGRESS",
|
||
|
|
"feature": "大型散货船并行建造模拟场景",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"code": "BH-SYN-2602",
|
||
|
|
"shipType": "210000 DWT 散货船",
|
||
|
|
"startOffset": 44,
|
||
|
|
"deliveryOffset": 424,
|
||
|
|
"priority": 7,
|
||
|
|
"shipOwner": "船东-SYN-A",
|
||
|
|
"status": "PLANNED",
|
||
|
|
"feature": "BH-SYN-2601 姊妹船,允许复用工时、工艺和学习数据",
|
||
|
|
"sisterProjectCode": "BH-SYN-2601",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"code": "BH-SYN-2603",
|
||
|
|
"shipType": "大型集装箱船",
|
||
|
|
"startOffset": 19,
|
||
|
|
"deliveryOffset": 465,
|
||
|
|
"priority": 9,
|
||
|
|
"shipOwner": "船东-SYN-B",
|
||
|
|
"status": "IN_PROGRESS",
|
||
|
|
"feature": "舾装、电气和管系密集型工作包",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"code": "BH-SYN-2604",
|
||
|
|
"shipType": "汽车运输船 PCTC",
|
||
|
|
"startOffset": 61,
|
||
|
|
"deliveryOffset": 483,
|
||
|
|
"priority": 6,
|
||
|
|
"shipOwner": "船东-SYN-C",
|
||
|
|
"status": "PLANNED",
|
||
|
|
"feature": "多层车辆甲板和复杂通风系统",
|
||
|
|
},
|
||
|
|
)
|
||
|
|
|
||
|
|
_MILESTONES = (
|
||
|
|
("CONTRACT_EFFECTIVE", "合同生效", -120, "商务管理部", 10),
|
||
|
|
("BASIC_DESIGN", "基本设计完成", -75, "设计部", 7),
|
||
|
|
("DETAIL_DESIGN", "详细设计完成", -45, "设计部", 7),
|
||
|
|
("PRODUCTION_DESIGN", "生产设计完成", -20, "生产设计部", 5),
|
||
|
|
("FIRST_STEEL", "首批钢材到厂", -7, "采购供应部", 3),
|
||
|
|
("STEEL_CUT", "开工/钢板切割", 0, "钢材预处理车间", 5),
|
||
|
|
("KEEL_LAY", "龙骨铺设", 60, "船体结构部", 5),
|
||
|
|
("MAIN_ENGINE_ARRIVAL", "主机到厂", "DELIVERY-190", "采购供应部", 7),
|
||
|
|
("SECTION_COMPLETE", "分段制造完成", "DELIVERY-165", "分段制造部", 7),
|
||
|
|
("DOCK_ERECTION_START", "船坞搭载开始", "DELIVERY-155", "船坞搭载部", 5),
|
||
|
|
("HULL_FORMED", "船体成型", "DELIVERY-110", "船坞搭载部", 7),
|
||
|
|
("LAUNCH", "下水", "DELIVERY-87", "船坞搭载部", 7),
|
||
|
|
("QUAY_OUTFITTING", "码头舾装", "DELIVERY-80", "舾装部", 5),
|
||
|
|
("MOORING_TRIAL", "系泊试验", "DELIVERY-48", "系统调试部", 5),
|
||
|
|
("SEA_TRIAL", "海上试航", "DELIVERY-31", "试航部", 5),
|
||
|
|
("CLASS_ACCEPTANCE", "船级社验收", "DELIVERY-14", "质量管理部", 5),
|
||
|
|
("OWNER_ACCEPTANCE", "船东验收", "DELIVERY-7", "项目管理部", 3),
|
||
|
|
("DELIVERY", "交付", "DELIVERY", "项目管理部", 0),
|
||
|
|
)
|
||
|
|
|
||
|
|
_DISCIPLINES = (
|
||
|
|
("HULL", "船体结构", "船体结构部"),
|
||
|
|
("MACHINERY", "机装", "机装部"),
|
||
|
|
("PIPE", "管系", "管系部"),
|
||
|
|
("ELECTRICAL", "电气", "电气部"),
|
||
|
|
("HVAC", "通风", "通风部"),
|
||
|
|
("INTERIOR", "内装", "内装部"),
|
||
|
|
("PAINT", "涂装", "涂装部"),
|
||
|
|
("DECK_MACHINERY", "甲板机械", "甲板机械部"),
|
||
|
|
("POWER", "动力系统", "动力系统部"),
|
||
|
|
("AUTOMATION", "自动化系统", "自动化部"),
|
||
|
|
("SAFETY", "安全系统", "安全管理部"),
|
||
|
|
("COMMISSION", "调试与试验", "系统调试部"),
|
||
|
|
)
|
||
|
|
|
||
|
|
_ZONE_TYPES = (
|
||
|
|
("BOW", "艏部区域"),
|
||
|
|
("STERN", "艉部区域"),
|
||
|
|
("CARGO", "货舱区域"),
|
||
|
|
("ENGINE_ROOM", "机舱区域"),
|
||
|
|
("SUPERSTRUCTURE", "上层建筑"),
|
||
|
|
("DECK", "甲板区域"),
|
||
|
|
("DOUBLE_BOTTOM", "双层底区域"),
|
||
|
|
("SIDE", "舷侧区域"),
|
||
|
|
("STEERING_GEAR", "舵机舱区域"),
|
||
|
|
("BRIDGE", "驾驶室区域"),
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def _set_owned_rows(
|
||
|
|
bundle: DatasetBundle,
|
||
|
|
table_name: str,
|
||
|
|
rows: list[dict[str, Any]],
|
||
|
|
*,
|
||
|
|
order_by: tuple[str, ...],
|
||
|
|
) -> None:
|
||
|
|
if table_name not in _PROJECT_TABLES or table_name not in TABLE_SPECS:
|
||
|
|
raise ValueError(f"table is outside W69-PROJECT ownership: {table_name}")
|
||
|
|
spec = TABLE_SPECS[table_name]
|
||
|
|
for index, row in enumerate(rows):
|
||
|
|
for field in spec.required:
|
||
|
|
if field not in row:
|
||
|
|
raise ValueError(
|
||
|
|
f"{table_name}[{index}] missing required field {field}"
|
||
|
|
)
|
||
|
|
primary_keys = [row[spec.primary_key] for row in rows]
|
||
|
|
if len(primary_keys) != len(set(primary_keys)):
|
||
|
|
raise ValueError(f"duplicate primary key in {table_name}")
|
||
|
|
bundle.set_rows(
|
||
|
|
table_name,
|
||
|
|
sorted(rows, key=lambda row: tuple(str(row.get(key, "")) for key in order_by)),
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def _distribute(total: int, groups: int) -> list[int]:
|
||
|
|
quotient, remainder = divmod(total, groups)
|
||
|
|
return [quotient + (1 if index < remainder else 0) for index in range(groups)]
|
||
|
|
|
||
|
|
|
||
|
|
def _project_id(project_code: str) -> str:
|
||
|
|
return stable_id("project", project_code, prefix="PRJ")
|
||
|
|
|
||
|
|
|
||
|
|
def _wbs_id(wbs_code: str) -> str:
|
||
|
|
return stable_id("wbs", wbs_code, prefix="WBS")
|
||
|
|
|
||
|
|
|
||
|
|
def _planned_window(
|
||
|
|
project_start: date,
|
||
|
|
delivery_date: date,
|
||
|
|
ordinal: int,
|
||
|
|
total: int,
|
||
|
|
*,
|
||
|
|
duration_days: int,
|
||
|
|
) -> tuple[str, str]:
|
||
|
|
span = max(1, (delivery_date - project_start).days)
|
||
|
|
usable = max(1, span - duration_days)
|
||
|
|
offset = min(usable, (max(0, ordinal) * usable) // max(1, total))
|
||
|
|
start = project_start + timedelta(days=offset)
|
||
|
|
end = min(delivery_date, start + timedelta(days=duration_days))
|
||
|
|
return start.isoformat(), end.isoformat()
|
||
|
|
|
||
|
|
|
||
|
|
def _wbs_row(
|
||
|
|
*,
|
||
|
|
wbs_code: str,
|
||
|
|
project_id: str,
|
||
|
|
wbs_type: str,
|
||
|
|
name: str,
|
||
|
|
parent_wbs_id: str | None,
|
||
|
|
planned_start: str,
|
||
|
|
planned_end: str,
|
||
|
|
department: str,
|
||
|
|
discipline: str | None,
|
||
|
|
zone: str | None,
|
||
|
|
weight_t: float,
|
||
|
|
status: str = "PLANNED",
|
||
|
|
) -> dict[str, Any]:
|
||
|
|
return {
|
||
|
|
"wbsId": _wbs_id(wbs_code),
|
||
|
|
"wbsCode": wbs_code,
|
||
|
|
"projectId": project_id,
|
||
|
|
"parentWbsId": parent_wbs_id,
|
||
|
|
"wbsType": wbs_type,
|
||
|
|
"name": name,
|
||
|
|
"plannedStart": planned_start,
|
||
|
|
"plannedEnd": planned_end,
|
||
|
|
"responsibilityDepartment": department,
|
||
|
|
"discipline": discipline,
|
||
|
|
"zone": zone,
|
||
|
|
"weightT": round(weight_t, 3),
|
||
|
|
"progressPct": 0.0,
|
||
|
|
"status": status,
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def generate_projects(bundle: DatasetBundle, config: GeneratorConfig) -> DatasetBundle:
|
||
|
|
"""Populate deterministic contracts, projects, milestones and complete WBS hierarchy."""
|
||
|
|
selected_specs = _PROJECT_SPECS[: config.profile.project_count]
|
||
|
|
base_date = config.planning_base_date
|
||
|
|
ship_projects: list[dict[str, Any]] = []
|
||
|
|
contracts: list[dict[str, Any]] = []
|
||
|
|
milestones: list[dict[str, Any]] = []
|
||
|
|
wbs_rows: list[dict[str, Any]] = []
|
||
|
|
zones: list[dict[str, Any]] = []
|
||
|
|
blocks: list[dict[str, Any]] = []
|
||
|
|
work_packages: list[dict[str, Any]] = []
|
||
|
|
|
||
|
|
project_contexts: list[dict[str, Any]] = []
|
||
|
|
for spec in selected_specs:
|
||
|
|
project_code = str(spec["code"])
|
||
|
|
project_id = _project_id(project_code)
|
||
|
|
project_start = base_date + timedelta(days=int(spec["startOffset"]))
|
||
|
|
delivery_date = base_date + timedelta(days=int(spec["deliveryOffset"]))
|
||
|
|
launch_date = delivery_date - timedelta(days=87)
|
||
|
|
sea_trial_date = delivery_date - timedelta(days=31)
|
||
|
|
sister_code = spec.get("sisterProjectCode")
|
||
|
|
ship_projects.append(
|
||
|
|
{
|
||
|
|
"projectId": project_id,
|
||
|
|
"projectCode": project_code,
|
||
|
|
"name": f"{project_code} 船舶项目-SYN",
|
||
|
|
"shipType": spec["shipType"],
|
||
|
|
"status": spec["status"],
|
||
|
|
"plannedStart": project_start.isoformat(),
|
||
|
|
"launchTarget": launch_date.isoformat(),
|
||
|
|
"seaTrialTarget": sea_trial_date.isoformat(),
|
||
|
|
"deliveryTarget": delivery_date.isoformat(),
|
||
|
|
"priority": spec["priority"],
|
||
|
|
"shipOwner": spec["shipOwner"],
|
||
|
|
"sisterProjectId": _project_id(str(sister_code))
|
||
|
|
if sister_code
|
||
|
|
else None,
|
||
|
|
"sisterProjectCode": sister_code,
|
||
|
|
"reuseLearningAllowed": bool(sister_code),
|
||
|
|
"scenarioFeature": spec["feature"],
|
||
|
|
"dateSource": "ATTACHMENT"
|
||
|
|
if project_code == "BH-SYN-2601"
|
||
|
|
else "DERIVED_SYNTHETIC",
|
||
|
|
"datasetType": "SYNTHETIC",
|
||
|
|
}
|
||
|
|
)
|
||
|
|
contract_code = f"CTR-{project_code}"
|
||
|
|
contracts.append(
|
||
|
|
{
|
||
|
|
"contractId": stable_id("contract", contract_code, prefix="CTR"),
|
||
|
|
"contractCode": contract_code,
|
||
|
|
"projectId": project_id,
|
||
|
|
"shipOwner": spec["shipOwner"],
|
||
|
|
"effectiveDate": (project_start - timedelta(days=120)).isoformat(),
|
||
|
|
"deliveryDate": delivery_date.isoformat(),
|
||
|
|
"currency": "CNY",
|
||
|
|
"contractStatus": "EFFECTIVE",
|
||
|
|
"synthetic": True,
|
||
|
|
}
|
||
|
|
)
|
||
|
|
root_code = f"{project_code}-WBS-ROOT"
|
||
|
|
root_wbs_id = _wbs_id(root_code)
|
||
|
|
wbs_rows.append(
|
||
|
|
_wbs_row(
|
||
|
|
wbs_code=root_code,
|
||
|
|
project_id=project_id,
|
||
|
|
wbs_type="PROJECT",
|
||
|
|
name=f"{project_code} 船舶项目-SYN",
|
||
|
|
parent_wbs_id=None,
|
||
|
|
planned_start=project_start.isoformat(),
|
||
|
|
planned_end=delivery_date.isoformat(),
|
||
|
|
department="项目管理部",
|
||
|
|
discipline=None,
|
||
|
|
zone=None,
|
||
|
|
weight_t=0.0,
|
||
|
|
status=str(spec["status"]),
|
||
|
|
)
|
||
|
|
)
|
||
|
|
discipline_wbs_ids: list[str] = []
|
||
|
|
for discipline_index, (
|
||
|
|
discipline_code,
|
||
|
|
discipline_name,
|
||
|
|
department,
|
||
|
|
) in enumerate(_DISCIPLINES, start=1):
|
||
|
|
wbs_code = f"{project_code}-DISC-{discipline_index:02d}"
|
||
|
|
discipline_wbs_ids.append(_wbs_id(wbs_code))
|
||
|
|
wbs_rows.append(
|
||
|
|
_wbs_row(
|
||
|
|
wbs_code=wbs_code,
|
||
|
|
project_id=project_id,
|
||
|
|
wbs_type="DISCIPLINE",
|
||
|
|
name=f"{discipline_name}-SYN",
|
||
|
|
parent_wbs_id=root_wbs_id,
|
||
|
|
planned_start=project_start.isoformat(),
|
||
|
|
planned_end=delivery_date.isoformat(),
|
||
|
|
department=department,
|
||
|
|
discipline=discipline_code,
|
||
|
|
zone=None,
|
||
|
|
weight_t=0.0,
|
||
|
|
)
|
||
|
|
)
|
||
|
|
zone_contexts: list[dict[str, Any]] = []
|
||
|
|
for zone_index, (zone_type, zone_name) in enumerate(_ZONE_TYPES, start=1):
|
||
|
|
zone_code = f"{project_code}-ZONE-{zone_index:02d}"
|
||
|
|
zone_id = stable_id("zone", zone_code, prefix="ZONE")
|
||
|
|
discipline_index = (zone_index - 1) % len(_DISCIPLINES)
|
||
|
|
discipline_code, _, department = _DISCIPLINES[discipline_index]
|
||
|
|
zone_wbs_code = f"{project_code}-WBS-ZONE-{zone_index:02d}"
|
||
|
|
zone_wbs_id = _wbs_id(zone_wbs_code)
|
||
|
|
zones.append(
|
||
|
|
{
|
||
|
|
"zoneId": zone_id,
|
||
|
|
"zoneCode": zone_code,
|
||
|
|
"projectId": project_id,
|
||
|
|
"zoneType": zone_type,
|
||
|
|
"name": f"{zone_name}-SYN",
|
||
|
|
"wbsId": zone_wbs_id,
|
||
|
|
"status": "ACTIVE",
|
||
|
|
}
|
||
|
|
)
|
||
|
|
wbs_rows.append(
|
||
|
|
_wbs_row(
|
||
|
|
wbs_code=zone_wbs_code,
|
||
|
|
project_id=project_id,
|
||
|
|
wbs_type="ZONE",
|
||
|
|
name=f"{zone_name}-SYN",
|
||
|
|
parent_wbs_id=discipline_wbs_ids[discipline_index],
|
||
|
|
planned_start=project_start.isoformat(),
|
||
|
|
planned_end=delivery_date.isoformat(),
|
||
|
|
department=department,
|
||
|
|
discipline=discipline_code,
|
||
|
|
zone=zone_type,
|
||
|
|
weight_t=0.0,
|
||
|
|
)
|
||
|
|
)
|
||
|
|
zone_contexts.append(
|
||
|
|
{
|
||
|
|
"zoneId": zone_id,
|
||
|
|
"zoneCode": zone_code,
|
||
|
|
"zoneType": zone_type,
|
||
|
|
"zoneName": zone_name,
|
||
|
|
"wbsId": zone_wbs_id,
|
||
|
|
"discipline": discipline_code,
|
||
|
|
"department": department,
|
||
|
|
}
|
||
|
|
)
|
||
|
|
project_contexts.append(
|
||
|
|
{
|
||
|
|
"projectCode": project_code,
|
||
|
|
"projectId": project_id,
|
||
|
|
"projectStart": project_start,
|
||
|
|
"deliveryDate": delivery_date,
|
||
|
|
"rootWbsId": root_wbs_id,
|
||
|
|
"zones": zone_contexts,
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
previous_milestone_id: str | None = None
|
||
|
|
previous_date: date | None = None
|
||
|
|
for milestone_index, (code, name, timing, department, buffer_days) in enumerate(
|
||
|
|
_MILESTONES, start=1
|
||
|
|
):
|
||
|
|
if timing == "DELIVERY":
|
||
|
|
planned_date = delivery_date
|
||
|
|
elif isinstance(timing, str) and timing.startswith("DELIVERY-"):
|
||
|
|
planned_date = delivery_date - timedelta(days=int(timing.split("-")[1]))
|
||
|
|
else:
|
||
|
|
planned_date = project_start + timedelta(days=int(timing))
|
||
|
|
milestone_code = f"{project_code}-MS-{milestone_index:02d}-{code}"
|
||
|
|
milestone_id = stable_id("milestone", milestone_code, prefix="MS")
|
||
|
|
lag_days = (
|
||
|
|
0 if previous_date is None else (planned_date - previous_date).days
|
||
|
|
)
|
||
|
|
milestones.append(
|
||
|
|
{
|
||
|
|
"milestoneId": milestone_id,
|
||
|
|
"projectId": project_id,
|
||
|
|
"milestoneCode": milestone_code,
|
||
|
|
"milestoneType": code,
|
||
|
|
"name": f"{name}-SYN",
|
||
|
|
"plannedDate": planned_date.isoformat(),
|
||
|
|
"predecessorMilestoneId": previous_milestone_id,
|
||
|
|
"relationType": "FS",
|
||
|
|
"lagDays": lag_days,
|
||
|
|
"leadDays": 0,
|
||
|
|
"bufferDays": buffer_days,
|
||
|
|
"responsibilityDepartment": department,
|
||
|
|
"sequence": milestone_index,
|
||
|
|
"status": "PLANNED",
|
||
|
|
}
|
||
|
|
)
|
||
|
|
previous_milestone_id = milestone_id
|
||
|
|
previous_date = planned_date
|
||
|
|
|
||
|
|
grand_counts = _distribute(config.profile.grand_block_count, len(project_contexts))
|
||
|
|
section_counts = _distribute(config.profile.section_count, len(project_contexts))
|
||
|
|
package_counts = _distribute(
|
||
|
|
config.profile.work_package_count, len(project_contexts)
|
||
|
|
)
|
||
|
|
task_counts = _distribute(config.profile.wbs_task_count, len(project_contexts))
|
||
|
|
|
||
|
|
for project_index, context in enumerate(project_contexts):
|
||
|
|
project_code = context["projectCode"]
|
||
|
|
project_id = context["projectId"]
|
||
|
|
project_start = context["projectStart"]
|
||
|
|
delivery_date = context["deliveryDate"]
|
||
|
|
zone_contexts = context["zones"]
|
||
|
|
grand_contexts: list[dict[str, Any]] = []
|
||
|
|
for local_index in range(1, grand_counts[project_index] + 1):
|
||
|
|
zone = zone_contexts[(local_index - 1) % len(zone_contexts)]
|
||
|
|
block_code = f"{project_code}-GB-{local_index:03d}"
|
||
|
|
block_id = stable_id("block", block_code, prefix="BLK")
|
||
|
|
wbs_code = f"{project_code}-WBS-GB-{local_index:03d}"
|
||
|
|
wbs_id = _wbs_id(wbs_code)
|
||
|
|
planned_start, planned_end = _planned_window(
|
||
|
|
project_start,
|
||
|
|
delivery_date,
|
||
|
|
local_index - 1,
|
||
|
|
grand_counts[project_index],
|
||
|
|
duration_days=100,
|
||
|
|
)
|
||
|
|
weight = 720.0 + (local_index % 11) * 42.5
|
||
|
|
blocks.append(
|
||
|
|
{
|
||
|
|
"blockId": block_id,
|
||
|
|
"blockCode": block_code,
|
||
|
|
"projectId": project_id,
|
||
|
|
"parentBlockId": None,
|
||
|
|
"blockType": "GRAND_BLOCK",
|
||
|
|
"name": f"总段-SYN-{local_index:03d}",
|
||
|
|
"zoneId": zone["zoneId"],
|
||
|
|
"wbsId": wbs_id,
|
||
|
|
"weightT": weight,
|
||
|
|
"lengthM": 32.0 + local_index % 9,
|
||
|
|
"widthM": 18.0 + local_index % 6,
|
||
|
|
"heightM": 14.0 + local_index % 5,
|
||
|
|
"status": "PLANNED",
|
||
|
|
}
|
||
|
|
)
|
||
|
|
wbs_rows.append(
|
||
|
|
_wbs_row(
|
||
|
|
wbs_code=wbs_code,
|
||
|
|
project_id=project_id,
|
||
|
|
wbs_type="GRAND_BLOCK",
|
||
|
|
name=f"总段-SYN-{local_index:03d}",
|
||
|
|
parent_wbs_id=zone["wbsId"],
|
||
|
|
planned_start=planned_start,
|
||
|
|
planned_end=planned_end,
|
||
|
|
department=zone["department"],
|
||
|
|
discipline=zone["discipline"],
|
||
|
|
zone=zone["zoneType"],
|
||
|
|
weight_t=weight,
|
||
|
|
)
|
||
|
|
)
|
||
|
|
grand_contexts.append(
|
||
|
|
{
|
||
|
|
"blockId": block_id,
|
||
|
|
"wbsId": wbs_id,
|
||
|
|
"zone": zone,
|
||
|
|
"weightT": weight,
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
section_contexts: list[dict[str, Any]] = []
|
||
|
|
for local_index in range(1, section_counts[project_index] + 1):
|
||
|
|
parent = grand_contexts[(local_index - 1) % len(grand_contexts)]
|
||
|
|
zone = parent["zone"]
|
||
|
|
block_code = f"{project_code}-SEC-{local_index:03d}"
|
||
|
|
block_id = stable_id("block", block_code, prefix="BLK")
|
||
|
|
wbs_code = f"{project_code}-WBS-SEC-{local_index:03d}"
|
||
|
|
wbs_id = _wbs_id(wbs_code)
|
||
|
|
planned_start, planned_end = _planned_window(
|
||
|
|
project_start,
|
||
|
|
delivery_date,
|
||
|
|
local_index - 1,
|
||
|
|
section_counts[project_index],
|
||
|
|
duration_days=55,
|
||
|
|
)
|
||
|
|
weight = 95.0 + (local_index % 17) * 8.5
|
||
|
|
blocks.append(
|
||
|
|
{
|
||
|
|
"blockId": block_id,
|
||
|
|
"blockCode": block_code,
|
||
|
|
"projectId": project_id,
|
||
|
|
"parentBlockId": parent["blockId"],
|
||
|
|
"blockType": "SECTION",
|
||
|
|
"name": f"分段-SYN-{local_index:03d}",
|
||
|
|
"zoneId": zone["zoneId"],
|
||
|
|
"wbsId": wbs_id,
|
||
|
|
"weightT": weight,
|
||
|
|
"lengthM": 12.0 + local_index % 11,
|
||
|
|
"widthM": 8.0 + local_index % 7,
|
||
|
|
"heightM": 6.0 + local_index % 5,
|
||
|
|
"status": "PLANNED",
|
||
|
|
}
|
||
|
|
)
|
||
|
|
wbs_rows.append(
|
||
|
|
_wbs_row(
|
||
|
|
wbs_code=wbs_code,
|
||
|
|
project_id=project_id,
|
||
|
|
wbs_type="SECTION",
|
||
|
|
name=f"分段-SYN-{local_index:03d}",
|
||
|
|
parent_wbs_id=parent["wbsId"],
|
||
|
|
planned_start=planned_start,
|
||
|
|
planned_end=planned_end,
|
||
|
|
department=zone["department"],
|
||
|
|
discipline=zone["discipline"],
|
||
|
|
zone=zone["zoneType"],
|
||
|
|
weight_t=weight,
|
||
|
|
)
|
||
|
|
)
|
||
|
|
section_contexts.append(
|
||
|
|
{
|
||
|
|
"blockId": block_id,
|
||
|
|
"wbsId": wbs_id,
|
||
|
|
"zone": zone,
|
||
|
|
"weightT": weight,
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
package_contexts: list[dict[str, Any]] = []
|
||
|
|
for local_index in range(1, package_counts[project_index] + 1):
|
||
|
|
section = section_contexts[(local_index - 1) % len(section_contexts)]
|
||
|
|
zone = section["zone"]
|
||
|
|
package_code = f"{project_code}-WP-{local_index:04d}"
|
||
|
|
package_id = stable_id("work-package", package_code, prefix="WP")
|
||
|
|
wbs_code = f"{project_code}-WBS-WP-{local_index:04d}"
|
||
|
|
wbs_id = _wbs_id(wbs_code)
|
||
|
|
planned_start, planned_end = _planned_window(
|
||
|
|
project_start,
|
||
|
|
delivery_date,
|
||
|
|
local_index - 1,
|
||
|
|
package_counts[project_index],
|
||
|
|
duration_days=24,
|
||
|
|
)
|
||
|
|
wbs_rows.append(
|
||
|
|
_wbs_row(
|
||
|
|
wbs_code=wbs_code,
|
||
|
|
project_id=project_id,
|
||
|
|
wbs_type="WORK_PACKAGE",
|
||
|
|
name=f"区域托盘工作包-SYN-{local_index:04d}",
|
||
|
|
parent_wbs_id=section["wbsId"],
|
||
|
|
planned_start=planned_start,
|
||
|
|
planned_end=planned_end,
|
||
|
|
department=zone["department"],
|
||
|
|
discipline=zone["discipline"],
|
||
|
|
zone=zone["zoneType"],
|
||
|
|
weight_t=round(section["weightT"] / 4.0, 3),
|
||
|
|
)
|
||
|
|
)
|
||
|
|
work_packages.append(
|
||
|
|
{
|
||
|
|
"workPackageId": package_id,
|
||
|
|
"workPackageCode": package_code,
|
||
|
|
"projectId": project_id,
|
||
|
|
"wbsId": wbs_id,
|
||
|
|
"blockId": section["blockId"],
|
||
|
|
"zoneId": zone["zoneId"],
|
||
|
|
"packageType": "PALLET" if local_index % 2 else "AREA",
|
||
|
|
"discipline": zone["discipline"],
|
||
|
|
"plannedStart": planned_start,
|
||
|
|
"plannedEnd": planned_end,
|
||
|
|
"needDate": planned_end,
|
||
|
|
"status": "PLANNED",
|
||
|
|
}
|
||
|
|
)
|
||
|
|
package_contexts.append(
|
||
|
|
{
|
||
|
|
"workPackageId": package_id,
|
||
|
|
"wbsId": wbs_id,
|
||
|
|
"zone": zone,
|
||
|
|
"blockId": section["blockId"],
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
for local_index in range(1, task_counts[project_index] + 1):
|
||
|
|
package = package_contexts[(local_index - 1) % len(package_contexts)]
|
||
|
|
zone = package["zone"]
|
||
|
|
task_code = f"{project_code}-TASK-{local_index:04d}"
|
||
|
|
planned_start, planned_end = _planned_window(
|
||
|
|
project_start,
|
||
|
|
delivery_date,
|
||
|
|
local_index - 1,
|
||
|
|
task_counts[project_index],
|
||
|
|
duration_days=8,
|
||
|
|
)
|
||
|
|
wbs_rows.append(
|
||
|
|
_wbs_row(
|
||
|
|
wbs_code=task_code,
|
||
|
|
project_id=project_id,
|
||
|
|
wbs_type="TASK",
|
||
|
|
name=f"生产任务-SYN-{local_index:04d}",
|
||
|
|
parent_wbs_id=package["wbsId"],
|
||
|
|
planned_start=planned_start,
|
||
|
|
planned_end=planned_end,
|
||
|
|
department=zone["department"],
|
||
|
|
discipline=zone["discipline"],
|
||
|
|
zone=zone["zoneType"],
|
||
|
|
weight_t=round(0.5 + (local_index % 25) * 0.18, 3),
|
||
|
|
)
|
||
|
|
)
|
||
|
|
|
||
|
|
table_rows = {
|
||
|
|
"contracts": contracts,
|
||
|
|
"ship-projects": ship_projects,
|
||
|
|
"milestones": milestones,
|
||
|
|
"wbs": wbs_rows,
|
||
|
|
"blocks": blocks,
|
||
|
|
"zones": zones,
|
||
|
|
"work-packages": work_packages,
|
||
|
|
}
|
||
|
|
order_fields = {
|
||
|
|
"contracts": ("contractCode",),
|
||
|
|
"ship-projects": ("projectCode",),
|
||
|
|
"milestones": ("projectId", "sequence"),
|
||
|
|
"wbs": ("wbsCode",),
|
||
|
|
"blocks": ("blockCode",),
|
||
|
|
"zones": ("zoneCode",),
|
||
|
|
"work-packages": ("workPackageCode",),
|
||
|
|
}
|
||
|
|
for table_name, rows in table_rows.items():
|
||
|
|
_set_owned_rows(bundle, table_name, rows, order_by=order_fields[table_name])
|
||
|
|
return bundle
|