feat: add data_date to project summary response
Include the schedule data date (last_recalc_date from XER) in the get_project_summary tool response. This shows when the schedule was last calculated in P6. Changes: - Add last_recalc_date column to projects table schema - Parse last_recalc_date in PROJECT table handler - Include last_recalc_date in db loader - Return data_date field in get_project_summary query - Update contract test to verify data_date presence
This commit is contained in:
@@ -74,7 +74,7 @@ As an AI assistant user, I want to get a high-level summary of the project sched
|
|||||||
|
|
||||||
**Acceptance Scenarios**:
|
**Acceptance Scenarios**:
|
||||||
|
|
||||||
1. **Given** an XER file is loaded, **When** I request the project summary, **Then** I receive project name, start date, finish date, and total activity count
|
1. **Given** an XER file is loaded, **When** I request the project summary, **Then** I receive project name, data date, start date, finish date, and total activity count
|
||||||
2. **Given** an XER file with milestones, **When** I request milestones, **Then** I receive a list of milestone activities with their target dates
|
2. **Given** an XER file with milestones, **When** I request milestones, **Then** I receive a list of milestone activities with their target dates
|
||||||
3. **Given** an XER file is loaded, **When** I request the critical path, **Then** I receive the sequence of activities that determine the project end date
|
3. **Given** an XER file is loaded, **When** I request the critical path, **Then** I receive the sequence of activities that determine the project end date
|
||||||
4. **Given** no XER file is loaded, **When** I request project summary, milestones, or critical path, **Then** I receive a clear error message indicating no file is loaded
|
4. **Given** no XER file is loaded, **When** I request project summary, milestones, or critical path, **Then** I receive a clear error message indicating no file is loaded
|
||||||
@@ -98,7 +98,7 @@ As an AI assistant user, I want to get a high-level summary of the project sched
|
|||||||
- **FR-003**: System MUST expose an MCP tool to list all activities with filtering options (by date range, by WBS, by activity type)
|
- **FR-003**: System MUST expose an MCP tool to list all activities with filtering options (by date range, by WBS, by activity type)
|
||||||
- **FR-004**: System MUST expose an MCP tool to retrieve detailed information for a specific activity by ID
|
- **FR-004**: System MUST expose an MCP tool to retrieve detailed information for a specific activity by ID
|
||||||
- **FR-005**: System MUST expose an MCP tool to query predecessor and successor relationships for any activity
|
- **FR-005**: System MUST expose an MCP tool to query predecessor and successor relationships for any activity
|
||||||
- **FR-006**: System MUST expose an MCP tool to retrieve project summary information (name, dates, activity count)
|
- **FR-006**: System MUST expose an MCP tool to retrieve project summary information (name, data date, plan dates, activity count)
|
||||||
- **FR-007**: System MUST expose an MCP tool to list milestone activities
|
- **FR-007**: System MUST expose an MCP tool to list milestone activities
|
||||||
- **FR-008**: System MUST expose an MCP tool to identify the critical path
|
- **FR-008**: System MUST expose an MCP tool to identify the critical path
|
||||||
- **FR-009**: System MUST return structured data that AI assistants can process and present to users
|
- **FR-009**: System MUST return structured data that AI assistants can process and present to users
|
||||||
@@ -139,6 +139,7 @@ As an AI assistant user, I want to get a high-level summary of the project sched
|
|||||||
- Q: How should multi-project XER files be handled? → A: Require explicit project selection if multiple exist
|
- Q: How should multi-project XER files be handled? → A: Require explicit project selection if multiple exist
|
||||||
- Q: Should calendar data be exposed as queryable? → A: Internal use only (not exposed as queryable)
|
- Q: Should calendar data be exposed as queryable? → A: Internal use only (not exposed as queryable)
|
||||||
- Q: What happens when any query tool is called without a file loaded? → A: Return informative error indicating no XER file is loaded; applies to all tools except load_xer
|
- Q: What happens when any query tool is called without a file loaded? → A: Return informative error indicating no XER file is loaded; applies to all tools except load_xer
|
||||||
|
- Q: Should project summary include the data date? → A: Yes, include the data date (schedule "as-of" date) in project summary response
|
||||||
|
|
||||||
## Assumptions
|
## Assumptions
|
||||||
|
|
||||||
|
|||||||
@@ -25,14 +25,15 @@ def load_parsed_data(parsed: ParsedXer, project_id: str) -> None:
|
|||||||
# Insert project
|
# Insert project
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO projects (proj_id, proj_short_name, plan_start_date, plan_end_date)
|
INSERT INTO projects (proj_id, proj_short_name, plan_start_date, plan_end_date, last_recalc_date)
|
||||||
VALUES (?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?)
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
project["proj_id"],
|
project["proj_id"],
|
||||||
project["proj_short_name"],
|
project["proj_short_name"],
|
||||||
project["plan_start_date"],
|
project["plan_start_date"],
|
||||||
project["plan_end_date"],
|
project["plan_end_date"],
|
||||||
|
project.get("last_recalc_date"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ def get_project_summary(project_id: str) -> dict | None:
|
|||||||
with db.cursor() as cur:
|
with db.cursor() as cur:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
SELECT proj_id, proj_short_name, plan_start_date, plan_end_date
|
SELECT proj_id, proj_short_name, plan_start_date, plan_end_date, last_recalc_date
|
||||||
FROM projects
|
FROM projects
|
||||||
WHERE proj_id = ?
|
WHERE proj_id = ?
|
||||||
""",
|
""",
|
||||||
@@ -319,6 +319,7 @@ def get_project_summary(project_id: str) -> dict | None:
|
|||||||
return {
|
return {
|
||||||
"project_id": project_row[0],
|
"project_id": project_row[0],
|
||||||
"project_name": project_row[1],
|
"project_name": project_row[1],
|
||||||
|
"data_date": project_row[4],
|
||||||
"plan_start_date": project_row[2],
|
"plan_start_date": project_row[2],
|
||||||
"plan_end_date": project_row[3],
|
"plan_end_date": project_row[3],
|
||||||
"activity_count": activity_count,
|
"activity_count": activity_count,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ CREATE TABLE IF NOT EXISTS projects (
|
|||||||
proj_short_name TEXT NOT NULL,
|
proj_short_name TEXT NOT NULL,
|
||||||
plan_start_date TEXT,
|
plan_start_date TEXT,
|
||||||
plan_end_date TEXT,
|
plan_end_date TEXT,
|
||||||
|
last_recalc_date TEXT,
|
||||||
loaded_at TEXT NOT NULL DEFAULT (datetime('now'))
|
loaded_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -35,4 +35,5 @@ class ProjectHandler(TableHandler):
|
|||||||
"proj_short_name": data.get("proj_short_name", ""),
|
"proj_short_name": data.get("proj_short_name", ""),
|
||||||
"plan_start_date": convert_date(data.get("plan_start_date")),
|
"plan_start_date": convert_date(data.get("plan_start_date")),
|
||||||
"plan_end_date": convert_date(data.get("plan_end_date")),
|
"plan_end_date": convert_date(data.get("plan_end_date")),
|
||||||
|
"last_recalc_date": convert_date(data.get("last_recalc_date")),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class TestGetProjectSummaryContract:
|
|||||||
result = await get_project_summary()
|
result = await get_project_summary()
|
||||||
|
|
||||||
assert "project_name" in result
|
assert "project_name" in result
|
||||||
|
assert "data_date" in result
|
||||||
assert "plan_start_date" in result
|
assert "plan_start_date" in result
|
||||||
assert "plan_end_date" in result
|
assert "plan_end_date" in result
|
||||||
assert "activity_count" in result
|
assert "activity_count" in result
|
||||||
|
|||||||
Reference in New Issue
Block a user