Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XER file generated by the python official example cannot be imported to P6 #797

Closed
tesla-cat opened this issue Jan 23, 2025 · 1 comment
Closed

Comments

@tesla-cat
Copy link

import jpype
import mpxj

jpype.startJVM()

from java.lang import Double
from java.time import LocalDate, LocalDateTime
from net.sf.mpxj import (
    Availability,
    Duration,
    ProjectFile,
    Relation,
    RelationType,
    TaskField,
    TimeUnit,
)
from net.sf.mpxj.common import LocalDateTimeHelper
from net.sf.mpxj.writer import FileFormat, UniversalProjectWriter

# The helper class we use later to actually write the file
# uses an enumeration to determine the output format.
file_name = "test.xer"
file_format = FileFormat.XER

# Create a ProjectFile instance
file = ProjectFile()

# Add a default calendar called "Standard"
calendar = file.addDefaultBaseCalendar()

# Add a holiday to the calendar to demonstrate calendar exceptions
calendar.addCalendarException(LocalDate.of(2006, 3, 13), LocalDate.of(2006, 3, 13))

# Retrieve the project properties and set the start date. Note Microsoft
# Project appears to reset all task dates relative to this date, so this
# date must match the start date of the earliest task for you to see
# the expected results. If this value is not set, it will default to
# today's date.
properties = file.getProjectProperties()
properties.setStartDate(LocalDateTime.of(2003, 1, 1, 8, 0))

# Set a couple more properties just for fun
properties.setProjectTitle("Created by MPXJ")
properties.setAuthor("Jon Iles")

# Let's create an alias for TEXT1
customFields = file.getCustomFields()
field = customFields.getOrCreate(TaskField.TEXT1)
field.setAlias("My Custom Field")

# Add resources
resource1 = file.addResource()
resource1.setName("Resource1")

resource2 = file.addResource()
resource2.setName("Resource2")
# This resource is only available 50% of the time
resource2.getAvailability().add(
    Availability(
        LocalDateTimeHelper.START_DATE_NA,
        LocalDateTimeHelper.END_DATE_NA,
        Double.valueOf(50.0),
    )
)


# Create a summary task
task1 = file.addTask()
task1.setName("Summary Task")

# Create the first sub task
task2 = task1.addTask()
task2.setName("First Sub Task")
task2.setDuration(Duration.getInstance(10.5, TimeUnit.DAYS))
task2.setStart(LocalDateTime.of(2003, 1, 1, 8, 0))
task2.setText(1, "My Custom Value 1")

# We'll set this task up as being 50% complete. If we have no resource
# assignments for this task, this is enough information for MS Project.
# If we do have resource assignments, the assignment record needs to
# contain the corresponding work and actual work fields set to the
# correct values in order for MS project to mark the task as complete
# or partially complete.
task2.setPercentageComplete(Double.valueOf(50.0))
task2.setActualStart(LocalDateTime.of(2003, 1, 1, 8, 0))

# Create the second sub task
task3 = task1.addTask()
task3.setName("Second Sub Task")
task3.setStart(LocalDateTime.of(2003, 1, 11, 8, 0))
task3.setDuration(Duration.getInstance(10, TimeUnit.DAYS))
task3.setText(1, "My Custom Value 2")


# Link these two tasks
task3.addPredecessor(Relation.Builder().targetTask(task2))

# Add a milestone
milestone1 = task1.addTask()
milestone1.setName("Milestone")
milestone1.setStart(LocalDateTime.of(2003, 1, 21, 8, 0))
milestone1.setDuration(Duration.getInstance(0, TimeUnit.DAYS))
milestone1.addPredecessor(Relation.Builder().targetTask(task3))

# This final task has a percent complete value, but no
# resource assignments. This is an interesting case it it requires
# special processing to generate the MSPDI file correctly.
task4 = file.addTask()
task4.setName("Next Task")
task4.setDuration(Duration.getInstance(8, TimeUnit.DAYS))
task4.setStart(LocalDateTime.of(2003, 1, 1, 8, 0))
task4.setPercentageComplete(Double.valueOf(70.0))
task4.setActualStart(LocalDateTime.of(2003, 1, 1, 8, 0))

# Assign resources to tasks
assignment1 = task2.addResourceAssignment(resource1)
assignment2 = task3.addResourceAssignment(resource2)

# As the first task is partially complete, and we are adding
# a resource assignment, we must set the work and actual work
# fields in the assignment to appropriate values, or MS Project
# won't recognise the task as being complete or partially complete
assignment1.setWork(Duration.getInstance(80, TimeUnit.HOURS))
assignment1.setActualWork(Duration.getInstance(40, TimeUnit.HOURS))

# If we were just generating an MPX file, we would already have enough
# attributes set to create the file correctly. If we want to generate
# an MSPDI file, we must also set the assignment start dates and
# the remaining work attribute. The assignment start dates will normally
# be the same as the task start dates.
assignment1.setRemainingWork(Duration.getInstance(40, TimeUnit.HOURS))
assignment2.setRemainingWork(Duration.getInstance(80, TimeUnit.HOURS))
assignment1.setStart(LocalDateTime.of(2003, 1, 1, 8, 0))
assignment2.setStart(LocalDateTime.of(2003, 1, 11, 8, 0))

# Write a 100% complete task
task5 = file.addTask()
task5.setName("Last Task")
task5.setDuration(Duration.getInstance(3, TimeUnit.DAYS))
task5.setStart(LocalDateTime.of(2003, 1, 1, 8, 0))
task5.setPercentageComplete(Double.valueOf(100.0))
task5.setActualStart(LocalDateTime.of(2003, 1, 1, 8, 0))

# Write a 100% complete milestone
task6 = file.addTask()
task6.setName("Last Milestone")
task6.setDuration(Duration.getInstance(0, TimeUnit.DAYS))
task6.setStart(LocalDateTime.of(2003, 1, 1, 8, 0))
task6.setPercentageComplete(Double.valueOf(100.0))
task6.setActualStart(LocalDateTime.of(2003, 1, 1, 8, 0))

# Write the file
writer = UniversalProjectWriter(file_format).write(file, file_name)

jpype.shutdownJVM()

the file generated by this cannot be imported to P6, all i have changed is from xml / MSPDI to xer / XER

@joniles
Copy link
Owner

joniles commented Jan 23, 2025

Hi, thanks for opening this issue.

Unfortunately this sample code has been set up for use with Microsoft Project, and provides the bare minimum necessary for the file to be opened successfully and show the desired schedule in Project. Sadly each application varies with respect to the minimum set of data required to create a valid schedule, and P6 is no exception to this.

In order to get this working you/I would need to do a little digging into what the minimum set of data required by P6 is. There may be some attributes which MPXJ can generate for you if they are not supplied (for example MPXJ already populates the Activity ID attribute if it has not been provided), and there may be other items of data which will just need to be supplied by your code.

MS Project is quite forgiving in this respect: the minimum amount of data required in an MSPDI file is relatively small, and from that Project can construct the rest of the schedule data for you. I have the feeling that P6 requires more data to create a valid schedule.

You may get different (better?!) results if you try generating a PMXML file and import that into P6. The reason for this is that XER is pretty much just a dump of the contents of the P6 database tables, and P6 assumes everything will be there when it reads in an XER file. PMXML is a more abstract file format modelling a schedule rather than a low level database dump, which P6 is transforming to populate the underlying database tables. So you may find that P6 is able to construct a schedule with a smaller subset of data when delivered as a PMXML file.

Hope that helps!

Jon

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants