report_skill_expm/.claude/skills/project_hierarchy/SKILL.md

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

  1. Project Validation: Validate that project names/IDs in cost reports match OpenProject
  2. Hierarchy Understanding: View parent-child relationships between projects
  3. Breadcrumb Generation: Show full project path (e.g., "Controller → Amphimove → Submodule")
  4. Project Search: Find projects by partial name or identifier match
  5. 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 None with 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

  1. Read-Only: ProjectManager only reads from OpenProject (no create/update)
  2. Parent Relationships: Only immediate parent is tracked (not full ancestry via hierarchy graph)
  3. Update Frequency: Default 1-hour cache - adjust cache_timeout for real-time needs
  4. Project Identifier: Searches are case-sensitive for identifiers, case-insensitive for names
  5. 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
  • list_user: User name mapping and team membership
  • week_report_gen: Weekly project reporting with project validation