fix: include finish milestones (TT_FinMile) in milestone queries

The list_milestones tool was only returning start milestones (TT_Mile)
and missing all finish milestones. P6 uses two task types for milestones:
- TT_Mile = Start Milestone
- TT_FinMile = Finish Milestone

Changes:
- Update query_milestones() to include both TT_Mile and TT_FinMile
- Derive milestone_type from task_type when not explicitly set:
  - TT_Mile -> 'start'
  - TT_FinMile -> 'finish'
- Add unit tests for milestone_type derivation from task_type

This fixes the E-J Electric schedule returning 5 milestones instead of 62.
This commit is contained in:
2026-01-08 11:55:43 -05:00
parent af8cdc1d31
commit bf2f85813e
3 changed files with 165 additions and 6 deletions

View File

@@ -157,6 +157,147 @@ class TestTaskHandler:
assert result["early_start_date"] is None
assert result["early_end_date"] is None
def test_parse_milestone_type_start(self) -> None:
"""Handler should parse milestone_type='start' for start milestones."""
from xer_mcp.parser.table_handlers.task import TaskHandler
handler = TaskHandler()
fields = [
"task_id",
"proj_id",
"task_code",
"task_name",
"task_type",
"milestone_type",
]
values = [
"2001",
"1001",
"MS-START",
"Project Start",
"TT_Mile",
"MS_Start",
]
result = handler.parse_row(fields, values)
assert result is not None
assert result["task_type"] == "TT_Mile"
assert result["milestone_type"] == "start"
def test_parse_milestone_type_finish(self) -> None:
"""Handler should parse milestone_type='finish' for finish milestones."""
from xer_mcp.parser.table_handlers.task import TaskHandler
handler = TaskHandler()
fields = [
"task_id",
"proj_id",
"task_code",
"task_name",
"task_type",
"milestone_type",
]
values = [
"2002",
"1001",
"MS-END",
"Project Complete",
"TT_Mile",
"MS_Finish",
]
result = handler.parse_row(fields, values)
assert result is not None
assert result["task_type"] == "TT_Mile"
assert result["milestone_type"] == "finish"
def test_parse_milestone_type_null_for_non_milestones(self) -> None:
"""Handler should return None for milestone_type on non-milestone activities."""
from xer_mcp.parser.table_handlers.task import TaskHandler
handler = TaskHandler()
fields = [
"task_id",
"proj_id",
"task_code",
"task_name",
"task_type",
]
values = [
"2003",
"1001",
"A1000",
"Regular Task",
"TT_Task",
]
result = handler.parse_row(fields, values)
assert result is not None
assert result["task_type"] == "TT_Task"
assert result["milestone_type"] is None
def test_parse_milestone_type_derived_from_tt_mile(self) -> None:
"""Handler should derive milestone_type='start' from task_type TT_Mile."""
from xer_mcp.parser.table_handlers.task import TaskHandler
handler = TaskHandler()
# No explicit milestone_type field - derive from task_type
fields = [
"task_id",
"proj_id",
"task_code",
"task_name",
"task_type",
]
values = [
"2001",
"1001",
"MS-START",
"Project Start",
"TT_Mile",
]
result = handler.parse_row(fields, values)
assert result is not None
assert result["task_type"] == "TT_Mile"
assert result["milestone_type"] == "start"
def test_parse_milestone_type_derived_from_tt_finmile(self) -> None:
"""Handler should derive milestone_type='finish' from task_type TT_FinMile."""
from xer_mcp.parser.table_handlers.task import TaskHandler
handler = TaskHandler()
# TT_FinMile is a finish milestone
fields = [
"task_id",
"proj_id",
"task_code",
"task_name",
"task_type",
]
values = [
"2002",
"1001",
"MS-END",
"Project Complete",
"TT_FinMile",
]
result = handler.parse_row(fields, values)
assert result is not None
assert result["task_type"] == "TT_FinMile"
assert result["milestone_type"] == "finish"
class TestTaskpredHandler:
"""Tests for TASKPRED table handler."""