Skip to main content

PyP6Xer - Python Primavera P6 XER Parser

PyPI version Python Support License: LGPL v2.1 Documentation

PyP6Xer is a comprehensive Python library for parsing, analyzing, and manipulating Primavera P6 XER (eXchange ERport) files. It provides an intuitive, object-oriented interface to access all project data including activities, resources, calendars, and relationships.

🚀 Features

  • Complete XER Parsing: Parse all major XER file components — every model-backed table, validated against the Oracle XER schema
  • Intuitive API: Object-oriented design with clean, pythonic interfaces
  • Comprehensive Data Access: Activities, resources, calendars, WBS, relationships, and more
  • Any-Table Access: Every XER table (including ones without a dedicated model class) is available losslessly via xer.tables["TABLE_NAME"]
  • Data Export: Write modified data back to XER format — full, lossless round-trips
  • Relationship Editing: Add, remove, and re-route TASKPRED relationships programmatically
  • Schedule Analysis: Built-in DCMA 14-point analysis, critical path, float analysis, and schedule quality checks
  • Resource Management: Resource allocation, utilization, and cost analysis
  • Progress Tracking: Earned value analysis and performance metrics
  • Zero Dependencies: Uses only Python standard library
  • Well Tested: 280+ tests covering every table, query method, DCMA check, and edge case

📦 Installation

Install from PyPI using pip:

pip install PyP6XER

Or install from source:

git clone https://github.com/HassanEmam/PyP6Xer.git
cd PyP6Xer
pip install -e .

🏃‍♂️ Quick Start

Basic Usage

from xerparser.reader import Reader

# Load an XER file
xer = Reader("project.xer")

# Access projects
for project in xer.projects:
    print(f"Project: {project.proj_short_name}")
    print(f"Activities: {len(project.activities)}")
    
    # Access activities in the project
    for activity in project.activities:
        print(f"  {activity.task_code}: {activity.task_name}")
        print(f"  Duration: {activity.duration} days")
        print(f"  Status: {activity.status_code}")

Resource Analysis

# Analyze resource utilization
for resource in xer.resources:
    assignments = [a for a in xer.activityresources if a.rsrc_id == resource.rsrc_id]
    total_hours = sum(a.target_qty or 0 for a in assignments)
    
    print(f"Resource: {resource.rsrc_name}")
    print(f"Total Assigned Hours: {total_hours}")
    print(f"Assignments: {len(assignments)}")

Critical Path Analysis

# Find critical activities
critical_activities = []
for activity in xer.activities:
    if activity.total_float_hr_cnt is not None and activity.total_float_hr_cnt <= 0:
        critical_activities.append(activity)

print(f"Critical Path: {len(critical_activities)} activities")
for activity in critical_activities:
    print(f"  {activity.task_code}: {activity.task_name}")

Schedule Quality Check

# Check for schedule quality issues
issues = []

# Activities without predecessors (except start milestones)
no_predecessors = [a for a in xer.activities 
                  if not a.predecessors and a.task_type != "TT_Mile"]
if len(no_predecessors) > 1:
    issues.append(f"Multiple start activities: {len(no_predecessors)}")

# Long duration activities
long_activities = [a for a in xer.activities 
                  if a.duration and a.duration > 20]
if long_activities:
    issues.append(f"Long duration activities (>20 days): {len(long_activities)}")

# Report issues
if issues:
    print("Schedule Quality Issues:")
    for issue in issues:
        print(f"  • {issue}")

Data Export

# Modify data and export
for activity in xer.activities:
    if activity.status_code == "TK_Active":
        # Update progress for active activities
        activity.phys_complete_pct = 25.0

# Write to new XER file (preserves the source ERMHDR)
from xerparser.write import writeXER
writeXER(xer, "updated_project.xer")   # or: xer.write("updated_project.xer")

Any-Table Access

Tables without a dedicated model class are still fully available — parse, query and write them back losslessly:

# Generic access to any XER table
steps = xer.tables["WBSSTEP"]          # e.g. WBS steps
record = steps.records[0]
print(record.step_name)

# All 60+ tables from the file are present, keyed by their %T name
print(sorted(xer.tables.keys()))

🔗 Editing Activity Relationships

Relationships (the TASKPRED table) can be added, removed and re-routed in Python, then written back to a valid XER — no Excel round-trip needed (see issue #7):

from xerparser.reader import Reader
from xerparser.write import writeXER

xer = Reader("schedule.xer")

# Add a finish-to-start relationship (tasks validated, IDs auto-allocated)
rel = xer.relations.add_relationship(
    task_id=1003, pred_task_id=1002,
    pred_type="PR_FS",      # PR_FS / PR_SS / PR_FF / PR_SF
    lag_hr_cnt=8.0,         # hours; negative = lead
    comments="Resequence phase 2",
)

# Cross-project link: set the predecessor's project explicitly
xer.relations.add_relationship(task_id=1003, pred_task_id=5000, pred_proj_id=2)

# Relationships are mutable — edit in place
for rel in xer.activities.get_predecessors(1002):
    rel.lag_hr_cnt = 4.0

# Re-route (delete + create in one step; validation failures change nothing)
xer.relations.replace_relationship(rel, pred_task_id=1001, pred_type="PR_SS")

# Remove
xer.relations.remove_relationship(rel)          # by object
xer.relations.remove_relationship(12)           # or by task_pred_id

writeXER(xer, "rescheduled.xer")

Convenience queries on xer.activities (Tasks):

xer.activities.get_predecessors(task_id)   # relationships feeding this activity
xer.activities.get_successors(task_id)     # relationships leaving this activity

Note: on the xer.relations collection itself, get_successors(act_id) returns relationships in which act_id is the predecessor (i.e. the successors of act_id) — the Tasks passthroughs above are the unambiguous way to query.

📊 DCMA 14-Point Schedule Analysis

Run the full DCMA 14-point schedule quality check on a loaded programme:

from xerparser.dcma14.analysis import DCMA14

xer = Reader("schedule.xer")
results = DCMA14(xer).analysis()["analysis"]

# Every check reports a count (and, where applicable, a percentage)
print(f"Open ends (no predecessors): {results['predecessors']['cnt']}")
print(f"Open ends (no successors):   {results['successors']['cnt']}")
print(f"Lags > 0:                    {results['lags']['cnt']}")
print(f"Leads (negative lags):       {results['leads']['cnt']}")
print(f"Hard constraints:            {results['constraints']['cstr_cnt']}")
print(f"Large total float:           {results['totalfloat']['cnt']}")
print(f"Negative float:              {results['negativefloat']['cnt']}")
print(f"Invalid dates:               {results['invaliddates']['cnt']}")
print(f"Missing resource assignments:{results['resources']['cnt']}")
print(f"Critical path length test:   {results['critical_test']['passed']}")
print(f"CPLI:                        {results['cpli']['cpli']}")
print(f"BEI:                         {results['bei']['bei']}")

The analysis handles multi-project programmes (per-project data dates from last_recalc_date), calendar-aware hour/day conversions, and counts activities rather than relations where the DCMA standard requires it.

📖 Documentation

Comprehensive documentation is available with detailed API references and examples:

🎯 Use Cases

Project Management

  • Schedule analysis and optimization
  • Critical path identification
  • Resource leveling and optimization
  • Progress tracking and reporting
  • Earned value analysis

Data Integration

  • Import P6 data into other systems
  • Data migration between project management tools
  • Custom reporting and dashboards
  • Data validation and quality checks

Analysis and Reporting

  • Portfolio analysis across multiple projects
  • Resource utilization reports
  • Schedule compliance analysis
  • Cost variance analysis
  • Performance metrics calculation

🔧 Advanced Features

Schedule Analysis Tools

from xerparser.reader import Reader

def analyze_project_performance(xer_file):
    xer = Reader(xer_file)
    
    for project in xer.projects:
        activities = project.activities
        
        # Calculate performance metrics
        total_activities = len(activities)
        completed = len([a for a in activities if a.status_code == "TK_Complete"])
        in_progress = len([a for a in activities if a.status_code == "TK_Active"])
        
        completion_rate = (completed / total_activities) * 100
        
        print(f"Project: {project.proj_short_name}")
        print(f"Completion: {completion_rate:.1f}%")
        print(f"Active: {in_progress} activities")

Resource Optimization

def find_overallocated_resources(xer):
    """Find resources that are over-allocated."""
    
    overallocated = []
    
    for resource in xer.resources:
        if resource.rsrc_type != "RT_Labor":
            continue
            
        assignments = [a for a in xer.activityresources if a.rsrc_id == resource.rsrc_id]
        
        # Simple over-allocation check (assumes 40 hours/week)
        total_hours = sum(a.target_qty or 0 for a in assignments)
        max_available = 2080  # 40 hours * 52 weeks
        
        if total_hours > max_available:
            overallocated.append({
                'resource': resource,
                'allocated_hours': total_hours,
                'excess_hours': total_hours - max_available
            })
    
    return overallocated

Earned Value Management

def calculate_earned_value_metrics(project, xer):
    """Calculate EVM metrics for a project."""
    
    planned_value = 0
    earned_value = 0
    actual_cost = 0
    
    for activity in project.activities:
        assignments = [a for a in xer.activityresources if a.task_id == activity.task_id]
        
        activity_planned = sum(a.target_cost or 0 for a in assignments)
        activity_actual = sum((a.act_reg_cost or 0) + (a.act_ot_cost or 0) for a in assignments)
        
        if activity.phys_complete_pct:
            activity_earned = activity_planned * (activity.phys_complete_pct / 100)
        else:
            activity_earned = 0
        
        planned_value += activity_planned
        earned_value += activity_earned
        actual_cost += activity_actual
    
    # Calculate performance indices
    cpi = earned_value / actual_cost if actual_cost > 0 else 0
    spi = earned_value / planned_value if planned_value > 0 else 0
    
    return {
        'planned_value': planned_value,
        'earned_value': earned_value,
        'actual_cost': actual_cost,
        'cpi': cpi,
        'spi': spi
    }

🌟 Supported XER Elements

PyP6Xer supports parsing and manipulation of all major XER file elements — 39 model-backed collections plus lossless generic access to every other table in the file (60+ tables in a typical export, including RSRCCURV and RSRCCURVDATA for resource curve modelling):

Core Project Elements

  • Projects - Project information and settings
  • Activities (Tasks) - Project activities with full scheduling data
  • WBS - Work Breakdown Structure hierarchy
  • Relationships - Activity dependencies and logic links

Resources and Assignments

  • Resources - Labor, material, and equipment resources
  • Resource Assignments - Activity-resource assignments with costs and quantities
  • Resource Rates - Cost and pricing information
  • Resource Categories - Resource classification and organization

Scheduling and Time

  • Calendars - Working time definitions with exceptions
  • Schedule Options - Project scheduling parameters and settings

Classification and Organization

  • Activity Codes - Custom activity classification systems
  • Activity Types - Activity type definitions
  • OBS - Organizational Breakdown Structure
  • Roles - Resource role definitions

Financial

  • Cost Accounts - Project cost account structure
  • Currencies - Multi-currency support
  • Financial Templates - Cost management templates

🔄 Data Model

PyP6Xer uses an intuitive object model that mirrors P6's data structure:

Reader
├── projects (Projects collection)
│   └── Project
│       ├── activities (filtered by project)
│       └── wbss (filtered by project)
├── activities (Tasks collection)
│   └── Task
│       ├── resources (resource assignments)
│       ├── predecessors (predecessor relationships)
│       ├── successors (successor relationships)
│       └── activitycodes (activity code assignments)
├── resources (Resources collection)
│   └── Resource
├── relations (Predecessors collection)
│   └── Predecessor
├── calendars (Calendars collection)
│   └── Calendar
├── accounts, roles, expenses, activityresources, ... (one collection per table)
└── tables (dict of every XER table, modelled or not)

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details on:

  • Setting up the development environment
  • Running tests
  • Submitting pull requests
  • Code style guidelines

Running the Test Suite

# full suite (280+ tests: every table, query, DCMA check, edge case)
python -m pytest tests/ -q

# a single area
python -m pytest tests/test_all_model_tables.py -q   # table round-trips
python -m pytest tests/test_dcma14_checks.py -q      # DCMA 14-point

The suite builds its own XER fixtures from scratch (see tests/table_fixtures.py), so it needs no sample data files.

📄 License

PyP6Xer is licensed under the GNU Lesser General Public License v2.1. See the LICENSE file for details.

🆘 Support

🎉 Acknowledgments

PyP6Xer is developed and maintained by Hassan Emam and the open-source community. Special thanks to all contributors who help improve the library.


Keywords: Primavera P6, XER Parser, Project Management, Schedule Analysis, Python, Construction, Engineering, Resource Management

Metadata

Release files for PyP6XER 2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for PyP6XER 2.0
File Size Uploaded Provenance Actions
pyp6xer-2.0.tar.gz 130.6 kB Without provenance Details for pyp6xer-2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for PyP6XER 2.0
File Interpreter ABI Platform Actions
pyp6xer-2.0-py3-none-any.whl Python 3 none any Details for pyp6xer-2.0-py3-none-any.whl

Total release size: 244.5 kB

Release files / pyp6xer-2.0.tar.gz

Download URL pyp6xer-2.0.tar.gz
Size 130.6 kB
Tags Source
SHA-256 checksum
How to use checksums
c4526f00d1b0b53b24f182dbadf5f4ef3b9d7789849541413317d82d2d9abd2b
BLAKE2b-256 checksum
How to use checksums
d81418de15e5f1f5bc0acce67c83362e1ac4b40535d17fb5c137eacc1c7383e8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.25

Release files / pyp6xer-2.0-py3-none-any.whl

Download URL pyp6xer-2.0-py3-none-any.whl
Size 113.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8f877352004e8095b4a6703f95e8348722a15d9bd344b76ce1a962f85c8a711d
BLAKE2b-256 checksum
How to use checksums
c3891cc5b1c69039cdc85eb1f9393f5f1adec95b5d1dd37e85d918ac1670e4d2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.25

Security for PyP6XER 2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page