forked from Zhubei_iXD/report_skill_expm
8.5 KiB
8.5 KiB
Project Hierarchy Skill
A Python skill for fetching and managing project hierarchies from OpenProject, with local caching and search capabilities.
Overview
The project_hierarchy skill provides tools to:
- Fetch projects from OpenProject API with error handling and recovery
- Build and visualize project hierarchies from parent-child relationships
- Search and validate projects by identifier or name
- Generate breadcrumb paths showing full project lineage
- Cache project data locally with configurable TTL
Use Cases
- Project Validation: Validate that project names/IDs in cost reports match OpenProject
- Hierarchy Understanding: View parent-child relationships between projects
- Breadcrumb Generation: Show full project path (e.g., "Controller → Amphimove → Submodule")
- Project Search: Find projects by partial name or identifier match
- Report Enhancement: Add project context to weekly or cost reports
Architecture
project_hierarchy/
├── project_manager.py # Core ProjectManager class (11 methods)
├── references/
│ └── projects.json # Cache file (auto-populated)
├── test_project_manager.py # Test script
└── SKILL.md # This file
Core Module: project_manager.py
Main class: ProjectManager
Initialization
from project_manager import ProjectManager
# Uses default OpenProject URL and local cache
manager = ProjectManager()
# Or with custom config
manager = ProjectManager(
api_url="http://ixd.openproject.techmation.com.tw/api/v3/projects",
cache_file="references/projects.json",
cache_timeout=3600 # 1 hour
)
Key Methods
| Method | Purpose | Returns |
|---|---|---|
fetch_projects(force_refresh=False) |
Fetch from API and cache | dict of projects or None if failed |
get_hierarchy_tree() |
Get tree structure of all projects | dict with tree nodes |
get_project(project_id) |
Get single project data | dict project info or None |
get_children(project_id) |
Get child projects | list of child project identifiers |
get_parent(project_id) |
Get parent project | str parent identifier or None |
get_full_path(project_id) |
Get breadcrumb path | list of project identifiers from root |
get_breadcrumb(project_id) |
Get readable breadcrumb | str like "root → child → project" |
search_projects(query) |
Find projects by name/id | list of matching project identifiers |
validate_project(project_id) |
Check if project exists | bool |
print_hierarchy() |
Print ASCII tree view | None (prints to stdout) |
Usage Examples
1. Fetch Projects and Show Hierarchy
from project_hierarchy.project_manager import ProjectManager
manager = ProjectManager()
# Fetch fresh data from OpenProject API
projects = manager.fetch_projects(force_refresh=True)
print(f"Loaded {len(projects)} projects")
# Display hierarchy
manager.print_hierarchy()
Output:
Projects by Hierarchy:
Root projects:
└─ controller (ID: controller)
├─ amphimove (ID: amphimove)
│ └─ submodule (ID: submodule_1)
└─ interface (ID: interface)
2. Validate Project Names in Reports
# When processing a cost report
project_name = "amphimove"
if manager.validate_project(project_name):
print(f"✓ Project '{project_name}' found in OpenProject")
else:
print(f"✗ Project '{project_name}' not found - check spelling")
3. Generate Breadcrumbs for Display
# Show full path in report header
breadcrumb = manager.get_breadcrumb("submodule_1")
print(f"Project: {breadcrumb}")
# Output: "Project: controller > amphimove > submodule_1"
4. Search for Projects
# Find all projects matching "master"
results = manager.search_projects("master")
for project_id in results:
print(f" - {project_id}")
5. Get Project Metadata
# Access raw project data
project = manager.get_project("amphimove")
if project:
print(f"Name: {project['name']}")
print(f"Parent: {project.get('parent')}")
print(f"ID: {project['id']}")
Integration with Other Skills
With week_report_gen
Validate projects before generating reports:
from project_hierarchy.project_manager import ProjectManager
from week_report_gen.generate_report import format_for_weekly_report
manager = ProjectManager()
# Validate project
if not manager.validate_project(cost_data['project']):
print(f"Warning: Project '{cost_data['project']}' not found")
# Generate report
report = format_for_weekly_report(cost_data, ...)
Data Format
projects.json Structure
{
"projects": {
"controller": {
"id": "123456",
"identifier": "controller",
"name": "Controller Project",
"parent": null
},
"amphimove": {
"id": "234567",
"identifier": "amphimove",
"name": "Amphimove Module",
"parent": {"identifier": "controller"}
}
},
"hierarchy": {
"controller": ["amphimove"],
"amphimove": ["submodule_1"]
},
"last_update": "2024-01-15T10:30:00Z"
}
Configuration
Environment Variables
# Override OpenProject API URL
export OPENPROJECT_API_URL="http://ixd.openproject.techmation.com.tw/api/v3/projects"
# Cache location (relative to skill directory)
export PROJECT_CACHE_FILE="references/projects.json"
# Cache timeout in seconds (default: 3600 = 1 hour)
export PROJECT_CACHE_TIMEOUT="3600"
Programmatic Configuration
manager = ProjectManager(
api_url="http://custom-url/api/v3/projects",
cache_file="custom/cache/projects.json",
cache_timeout=7200 # 2 hours
)
Testing
Run the test script to verify functionality:
cd d:\bensung\report_skill_expm\.claude\skills\project_hierarchy
python test_project_manager.py
Expected output:
============================================================
PROJECT MANAGER - TEST
============================================================
✓ ProjectManager initialized
Testing: Fetch projects from OpenProject
------------------------------------------------------------
✓ Successfully fetched X projects
First 5 projects:
1. controller → Controller Project (parent: None)
2. amphimove → Amphimove Module (parent: controller)
...
Testing: Get project hierarchy
------------------------------------------------------------
Projects by Hierarchy:
Root projects:
└─ controller (ID: controller)
├─ amphimove (ID: amphimove)
...
============================================================
✓ All tests completed!
============================================================
Error Handling
The ProjectManager handles common failures gracefully:
- API Unreachable: Uses cached data if available, returns empty dict if no cache
- Invalid Project ID: Returns
Nonewith no exception - Malformed Cache: Rebuilds from API on next fetch
- Network Errors: Logged and cached data used as fallback
Example:
try:
projects = manager.fetch_projects(force_refresh=True)
except Exception as e:
# Use cached data instead
projects = manager.get_cached_projects()
if not projects:
print("No data available - check OpenProject connection")
Performance Characteristics
- First fetch: ~500ms - 2s (depends on API and project count)
- Cached lookups: <1ms
- Hierarchy search: O(n) where n = project count
- Breadcrumb generation: O(depth of tree)
- Cache file size: ~50KB for typical project set (100+ projects)
Limitations & Notes
- Read-Only: ProjectManager only reads from OpenProject (no create/update)
- Parent Relationships: Only immediate parent is tracked (not full ancestry via hierarchy graph)
- Update Frequency: Default 1-hour cache - adjust
cache_timeoutfor real-time needs - Project Identifier: Searches are case-sensitive for identifiers, case-insensitive for names
- API Access: Requires network access to OpenProject instance
Future Enhancements
- Add filtering by project status/type
- Support hierarchical queries (all descendants, not just children)
- Export hierarchy as JSON/CSV/SVG
- Add project metadata (status, budget, owner)
- MCP server for sharing with other AI systems
- Real-time sync mode for active monitoring
Related Skills
- list_user: User name mapping and team membership
- week_report_gen: Weekly project reporting with project validation