Skip Headers
Agile Product Lifecycle Management SDK Developer Guide - Using APIs
Release 9.3.3
E39307-02
  Go To Table Of Contents
Contents

Previous
Previous
 
Next
Next
 

17 Creating and Managing Projects

This chapter includes the following:


Note:

In Release 9.3, the name of the Program Base Class was changed to Projects and Projects to Sourcing Projects. However the interface for the Projects Base Class is still called IProgram in the SDK Guide and in Javadoc references.

17.1 About Projects and Project Objects

You can use the project management features of Agile Product Portfolio Management (PPM) to define a project and its associated elements such as activity schedules, deliverables, and discussions. These capabilities enable you to determine the availability of the required resources, assigning resources to tasks, identifying bottlenecks, and responding to over- and under-allocated resource conditions. You can also create and reuse project templates.

The Project object is used to schedule and execute projects. Each project, in addition to schedule information, contains attachments, discussions and actions items, resources and roles, and history and content of related activities. For management visibility, data is rolled up to higher levels by rules and parent-child relationships.

The Agile API provides support for creating, loading, and working with Projects. The IProgram interface represents all Project objects, including programs, phases, tasks, and gates.

Similar to other Agile PLM business objects, the IProgram interface implements IRoutable, which means it uses the same IRouteable.changeStatus() method to change a Projects' Workflow status and to route it to other users. For more information, "Changing the Workflow Status of an Object."

17.2 Differences in the Behavior of Project Objects

The IProgram interface implements several interfaces commonly used by other Agile PLM objects. However, it also provides the following distinct functionality that separates Project objects from other objects.

  • The Project object is a container of other underlying Project objects, such as Phases, Tasks, and Gates. The underlying Project objects are related to the parent object, usually the Projects, through the Schedule table.

  • Projects have baselines that allow you to track changes in the schedule. Therefore, the IProgram interface provides methods that let you create, get, or remove a baseline.

  • Projects can be archived. If you archive the root Projects, the entire Projects tree is soft-deleted from the system.

  • Projects can be locked or unlocked.

17.3 Creating Projects

Use the IAgileSession.createObject() method to create Projects. When you specify the Project's parameters, you must specify the Project subclass (for example, Program, phase, task, or gate). For Programs, phases, and tasks, you must also specify following required Project attributes:

  • General Info.Name

  • General Info.Schedule Start Date

  • General Info.Schedule End Date

  • General Info.Duration Type

For gates, only two attributes are required, General Info.Name and General Info.Schedule End Date.

The following example shows how to create new Projects and specify the required attributes.

Example 17-1 Creating Projects

try {// Create a Map object to store parameters   Map params = new HashMap();

// Set Projects name   String name = "APOLLO PROJECTS";

// Set Projects start date   Date start = new Date();   start.setTime(1);

// Set Projects end date   Date end = new Date();   end.setTime(1 + 2*24*60*60*1000);

// Set Projects duration type   IAttribute attr = m_admin.getAgileClass(ProgramConstants.CLASS_PROGRAM).
   getAttribute(ProgramConstants.ATT_GENERAL_INFO_DURATION_TYPE);   IAgileList avail = attr.getAvailableValues();   avail.setSelection(new Object[] {"Fixed"});

// Initialize the params object   params.put(ProgramConstants.ATT_GENERAL_INFO_NAME, name);   params.put(ProgramConstants.ATT_GENERAL_INFO_SCHEDULE_START_DATE, start);   params.put(ProgramConstants.ATT_GENERAL_INFO_SCHEDULE_END_DATE, end);   params.put(ProgramConstants.ATT_GENERAL_INFO_DURATION_TYPE, avail);

// Create Projects   IProgram program = 
      (IProgram)m_session.createObject(ProgramConstants.CLASSPROGRAM, params);} catch (APIException ex) {      System.out.println(ex);}

Projects contain other types of activities, such as phases, tasks, and gates. A gate is a special milestone-a task with an end date but no duration-that denotes the completion of a set of related phases, tasks, or Projects. The following figure shows the hierarchy of Project objects.

Figure 17-1 Program hierarchy

Surrounding text describes Figure 17-1 .

You can use the IAgileSession.createObject() method to create phases, tasks, and gates in the same way that you create other Project objects. Once you create these different types of activities, you can add them to the Schedule table of a Projects object. For more information, see "Scheduling Projects."

17.4 Adding Rules for PPM Objects

In PLM, any object that is assigned a lifecycle phase or a workflow can be added as a deliverable. The only exceptions are Discussions, Users, and User groups.

Rules in PPM ensure an activity will not complete before the completion of the preceding activity as set in the workflow, or lifecycle phase. For example, if you want to ensure the completion of an activity before a Gate is opened, you can add that activity as a deliverable for the Gate to open. You can even restrict one Gate from opening before another by adding the prior Gate as a deliverable for the subsequent Gate to open. For more information, refer to the Agile PLM Product Portfolio Management User Guide.

The SDK supports this function with IProgram interface as shown in the following example.

Example: Setting rules for PPM objects

Example 17-2 Setting rules for PPM objects

try{//Get Program   IProgram pgm =      (IProgram)session.getObject   (ProgramConstants.CLASS_PROGRAM,"PGM00239");

//Get Object and add as relationship under Content tab   IChange eco = (IChange)session.getObject(ChangeConstants.CLASS_ECO,"C00060");   ITable table = pgm.getTable(ProgramConstants.TABLE_RELATIONSHIPS);   IRow row = table.createRow(eco);

//Get the Control object status   IStateful state = (IStateful)pgm;   IStatus[] statuses = state.getStates();   IStatus ctl_status = null;   for(int i=0; i<statuses.length; i++){   if(statuses[i].getName().equals("In Process")){      ctl_status = statuses[i];   break;   }}

//Get the Affected object status   state = (IStateful)eco;   statuses = state.getStates();   IStatus aff_status = null;   for(int i=0; i<statuses.length; i++){   if(statuses[i].getName().equals("Submitted")){      aff_status = statuses[i];      break;   }}

//Add Rule   HashMap map = new HashMap();   map.put(CommonConstants.ATT_RELATIONSHIPS_RULE_CONTROLOBJECT, pgm);   map.put(CommonConstants.ATT_RELATIONSHIPS_RULE_AFFECTEDOBJECT, eco);   map.put(CommonConstants.ATT_RELATIONSHIPS_RULE_CONTROLOBJECTSTATUS, 
      ctl_status); map.put(CommonConstants.
         ATT_RELATIONSHIPS_RULE_AFFECTEDOBJECTSTATUS, aff_status);   row.setValue(CommonConstants.ATT_RELATIONSHIPS_RULE, map);    System.out.println(row.getCell   (CommonConstants.ATT_RELATIONSHIPS_RULE));
   }catch (APIException ex) {      System.out.println(ex);   }

17.5 Loading Projects

To load Projects, use the IAgileSession.getObject() method. To uniquely identify a Project object, specify the value for the General Info.Number attribute. You can also load a Project object by searching for it by name, and then selecting it from the search results.


Note:

The IProgram.getName() method actually returns the value of the General Info.Number attribute, and not that of General Info.Name.

Example 17-3 Loading Projects

public IProgram loadProgram(String number) throws APIException {   IProgram program = 
      (IProgram)m_session.getObject(IProgram.OBJECT_TYPE, number);   return program;}

Note:

The News table for Projects is disabled by default. To enable it, log in to the Java Client as an Administrator and make the News tab visible.

17.6 Adding ”FileFolder” to Project's Content Tab

You can add FileFolders to the Content tab of a Project using the IProgram API. The following example shows how to perform this task.

Example 17-4 Adding a FileFolder to the Content tab of a Project

import java.io.File;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.agile.api.*;

public class Sample {
   public static void main(String[] args) {
      try{
            Sample sample = new Sample();
            String url = "http://localhost:8888/web";
            String userName = "admin";
            String password = "agile";

   AgileSessionFactory instance = AgileSessionFactory.getInstance(url);   HashMap params = new HashMap();   params.put(AgileSessionFactory.USERNAME, userName);   params.put(AgileSessionFactory.PASSWORD, password);   IAgileSession session = instance.createSession(params);   IFileFolder ff = createFileFolder(session);   System.out.println(ff.getName());

// Add file to Files table   addFile2FileFolder(session,ff);

// Add filefolder to contents tab   sample.uploadFile(session,ff.getName());   }catch(Throwable th){      th.printStackTrace();   }}

/*** Upload attachment to Contents tab of a Program* @param session* @throws APIException*/private void uploadFile(IAgileSession session,String foldname) 
   throws APIException {      IProgram program = 
         (IProgram) session.getObject(IProgram.OBJECT_TYPE, "PGM00041");      IFileFolder ff = 
         (IFileFolder) session.getObject(IFileFolder.OBJECT_TYPE, foldname);

//Upload filefolder to Conents table   ITable table = program.getTable(ProgramConstants.TABLE_RELATIONSHIPS);   table.createRow(ff);}/*** Upload attachment to FileFolder* @param session* @param foldername* @throws APIException*/private static void addFile2FileFolder
      (IAgileSession session, IFileFolder ff) throws APIException {
   ff.checkOut();   ITable table = ff.getTable(FileFolderConstants.TABLE_FILES);   String path = "C:\\temp\\out3.txt";   File file = new File(path);   table.createRow(file);   ff.checkIn();   System.out.println("Finish");}

private static IFileFolder createFileFolder
      (IAgileSession session) throws APIException {   IAgileClass objClass = session.getAdminInstance().getAgileClass(   FileFolderConstants.CLASS_FILE_FOLDER);   IAutoNumber autoNumber = objClass.getAutoNumberSources()[0];   IFileFolder obj = (IFileFolder)session.createObject(   FileFolderConstants.CLASS_FILE_FOLDER, autoNumber);   return obj;   }}

17.7 Using Project Templates

Project templates make it easy to define a new Project object, activity, or task. A template is a Project with the General Info.Template attribute set to ”Template”. You can use a template to create a new Project by loading it and then using the IProgram.saveAs() method.

This special version of the saveAs() method enables to use the SDK to:

  • Create a new Project from a template and specify the tables that you want copied over

  • Change the owner of the Project and the owner of the children

  • Create a new Project template by saving a Project as a template

17.7.1 Creating New Projects Using Templates

You can use this special version of the saveAs() method to specify the Project tables that you want to copy from the original Project to the new Project. You don't need to specify all tables. The General Info, Schedule, Dependencies - Dependent Upon, Dependencies - Required For, and Workflow tables are copied automatically. The Discussion, News, and History tables cannot be copied. Generally, you should copy Page Two, Page Three (if it's used), and the Team table, as shown in the example below.

Example 17-5 Creating a new Project from a template

try
{
// Get the Project template whose number is PGM00004   IProgram template =          (IProgram)m_session.getObject(IProgram.OBJECT_TYPE, "PGM00004");   if (template != null) {   // Create a hash map of the program attributes to use for the new program      HashMap map = new HashMap();      String name = "Scorpio Program";      IAttribute att =         m_admin.getAgileClass(ProgramConstants.CLASS_PROGRAM).getAttribute(      ProgramConstants.ATT_GENERAL_INFO_TEMPLATE);      IAgileList templateList = att.getAvailableValues();

// Available values for Template attribute are Active, Proposed, and Template   templateList.setSelection(new Object[] {"Active"});   map.put(ProgramConstants.ATT_GENERAL_INFO_NAME, name);   map.put(ProgramConstants.ATT_GENERAL_INFO_SCHEDULE_START_DATE, new Date());   map.put(ProgramConstants.ATT_GENERAL_INFO_TEMPLATE, templateList);

// Define the tables to copy to the new program from the template   Integer pagetwo = ProgramConstants.TABLE_PAGETWO;   Integer pagethree = ProgramConstants.TABLE_PAGETHREE;Integer team = ProgramConstants.TABLE_TEAM;   Object[] tables = new Object[]{pagetwo, pagethree, team};

// Save the template as a new program   IProgram program =      (IProgram)template.saveAs(ProgramConstants.CLASS_PROGRAM,tables, map);      }   } catch (APIException ex) {   System.out.println(ex);
}

17.7.2 Creating Projects and Changing Ownerships

When you create a Project from a template using the saveAs() API call, you can change the ownership of the Project and propagate the change to the children of the Project. In SDK, the exposed API is used to accomplish this:

public IAgileObject saveAs(Object type, Object[] tablesToCopy,Object params, boolean applyToChildren)

This is done by specifying a value for both the ProgramConstants and the OWNER attribute. The value for the OWNER attribute is required in order to change the Project's ownership. Set the Boolean applyToChildren to true if you want to apply the OWNER value to all children.

In the UI, when you create a Project from a template, you have the option to change ownership of the Project and apply the change to the children. In this situation, the SDK mirrors the UI. However, the original Project must be a Template to create a Project from a template via SDK's saveAs() API.


Note:

In the SDK, a Project is a template when the value of the General Info.Template attribute in the original program is set to Template.

Example 17-6 Creating a Project from a template, change owner, and propagate change

public IProgram saveTemplateAndSetOwner (IProgram template, String userID, boolean       applyToChildren) throws APIException {

/* "template" is a program template*   userID -- The "userID" of the user that*   is specified as the owner of the Saved program object*   applyToChildren -- true or false.*   If "true" the "specified owner" is the owner of the entire program tree*   If "false", the specified owner is the owner of the Root Parent object only*/

HashMap map = new HashMap () ;String newPgmName =   "PROG" + System.currentTimeMillis() ; 

// Generate a random name for the Saved Program object   IUser user =      session.getObject(UserConstants.CLASS_USER, userID) ;      map.put(ProgramConstants.ATT_GENERAL_INFO_NAME, newPgmName);       map.put(ProgramConstants.ATT_GENERAL_INFO_OWNER, user);      map.put(ProgramConstants.ATT_GENERAL_INFO_SCHEDULE_START_DATE, new Date());

// Define the tables to copy from the template and if 
// you do not want to copy any tables, specify "null" 
// for the "tables" param.   Integer pageTwo = ProgramConstants.TABLE_PAGETWO ;   Integer pageThree = ProgramConstants.TABLE_PAGETHREE ;   Integer team = ProgramConstants.TABLE_TEAM ;   Object[] tables =         { pageTwo, pageThree, team } ;   IProgram pgm =         (IProgram) root.saveAs
            (ProgramConstants.CLASS_PROGRAM, tables, map, applyToChildren);   System.out.println      ("New Program Number = " + pgm.getName()) ;   System.out.println      ("Owner Value = " + pgm.getValue
         (ProgramConstants.ATT_GENERAL_INFO_OWNER).toString())   return pgm ;}

17.7.3 Saving Projects as Templates

When you create a Project, you can specify that it's a template by setting the value of the Template attribute (ProgramConstants.ATT_GENERAL_INFO_TEMPLATE) to ”Template”. You can only do this when you create a Project or when you save it as a new Project. Existing Projects cannot be changed from the ”Active” or ”Proposed” state to ”Template”.

The following example shows how to open a Project object and save it as a template.

Example 17-7 Saving a Projects object as a template

try {// Get the program whose number is PGM00005   IProgram program =      (IProgram)m_session.getObject(IProgram.OBJECT_TYPE, "PGM00005");

   if (program != null) {   // Create a hash map of program attributes for the new program      HashMap map = new HashMap();      String name = "Rapid Development");   IAttribute att =      m_admin.getAgileClass(ProgramConstants.CLASPROGRAM).getAttribute
         (ProgramConstants.ATT_GENERAL_INFO_TEMPLATE);   IAgileList templateList =      att.getAvailableValues();

// Available values for Template attribute are Active, Proposed, and Template   templateList.setSelection(new Object[] {"Template"});   map.put(ProgramConstants.ATT_GENERAL_INFO_NAME, name);   map.put(ProgramConstants.ATT_GENERAL_INFO_SCHEDULE_START_DATE, new Date());   map.put(ProgramConstants.ATT_GENERAL_INFO_TEMPLATE, templateList);

//Define tables to copy to the template   Integer pagetwo = ProgramConstants.TABLE_PAGETWO;   Integer pagethree = ProgramConstants.TABLE_PAGETHREE;   Integer team = ProgramConstants.TABLE_TEAM;   Object[] tables = new Object[]{pagetwo, pagethree, team};

// Save the program as a template   IProgram program = (IProgram)template.saveAs
      (ProgramConstants.CLASS_PROGRAM, tables, map);   }} catch (APIException ex) {   System.out.println(ex);}

17.8 Scheduling Projects

To schedule Projects, edit the Schedule table, which lets you add, edit, and remove schedule items. To add a new row to the Schedule table, use the ITable.createRow() method and specify an IProgram object for the parameter.

Example 17-8 Modifying the Schedule table

try {// Define a row variable   IRow row = null;

// Set the date format   DateFormat df = new SimpleDateFormat("MM/dd/yy");

// Get a Project   IProgram program =      (IProgram)m_session.getObject(ProgramConstants.CLASS_PROGRAM, "PGM00012");   if (program != null) {// Get the Schedule table      ITable schedule = program.getTable(ProgramConstants.TABLE_SCHEDULE);      Iterator i = schedule.iterator();

// Find task T000452 and remove it   while (i.hasNext()) {      row = (IRow)i.next();      String num =      (String)row.getValue(ProgramConstants.ATT_GENERAL_INFO_NUMBER);   if (num.equals("T000452")) {      schedule.removeRow(row);break;   }}

// Add a phase   HashMap info = new HashMap();   info.put(ProgramConstants.ATT_GENERAL_INFO_NAME, "Specifications phase");   info.put(ProgramConstants.ATT_GENERAL_INFO_SCHEDULE_START_DATE,    df.parse("06/01/05"));   info.put(ProgramConstants.ATT_GENERAL_INFO_SCHEDULE_END_DATE,    df.parse("06/10/05"));   IAttribute attr = m_admin.getAgileClass(ProgramConstants.CLASS_PHASE).   getAttribute(ProgramConstants.ATT_GENERAL_INFO_DURATION_TYPE);   IAgileList list = attr.getAvailableValues();   list.setSelection(new Object[] {"Fixed"});   info.put(ProgramConstants.ATT_GENERAL_INFO_DURATION_TYPE, list);   IProgram phase = 
      (IProgram)m_session.createObject(ProgramConstants.CLASS_PHASE, info);   row = schedule.createRow(phase);

// Add a task   info = null;   list = null;   info.put(ProgramConstants.ATT_GENERAL_INFO_NAME, "Write specifications");   info.put(ProgramConstants.ATT_GENERAL_INFO_NUMBER, "T000533");   info.put(ProgramConstants.ATT_GENERAL_INFO_SCHEDULE_START_DATE,    df.parse("06/01/05"));   info.put(ProgramConstants.ATT_GENERAL_INFO_SCHEDULE_END_DATE,    df.parse("06/05/05"));
   attr = m_admin.getAgileClass(ProgramConstants.CLASS_TASK).   getAttribute(ProgramConstants.ATT_GENERAL_INFO_DURATION_TYPE);   list = attr.getAvailableValues();   list.setSelection(new Object[] {"Fixed"});   info.put(ProgramConstants.ATT_GENERAL_INFO_DURATION_TYPE, list);   IProgram task = 
      (IProgram)m_session.createObject(ProgramConstants.CLASS_TASK, info);   row = schedule.createRow(task);

// Add a gate   info = null;   info.put(ProgramConstants.ATT_GENERAL_INFO_NAME, "Specifications complete");   info.put(ProgramConstants.ATT_GENERAL_INFO_SCHEDULE_END_DATE,    df.parse("06/10/05"));   IProgram gate = 
      (IProgram)m_session.createObject(ProgramConstants.CLASS_GATE, info);   row = schedule.createRow(gate);   }} catch (APIException ex) {      System.out.println(ex);}

Once a Project's schedule is defined, you can reschedule the project with the IProgram.reschedule() method. The reschedule() method takes a couple of parameters, the IProgram.RESCHEDULE constant and the new value for that schedule option. Here are the list of IProgram.RESCHEDULE constants you can use:

  • STARTDATE - This moves the scheduled start date to the specified date.

  • ENDDATE - This moves the scheduled end date to the specified date.

  • BACKWARDDAYS - This moves the schedule backward by the specified number of days.

  • FORWARDDAYS - This moves the schedule forward by the specified number of days.

Example 17-9 Rescheduling Projects

try {// Get a Project   IProgram program =      (IProgram)m_session.getObject(IProgram.OBJECT_TYPE, "PGM00012");   if (program != null) {

// Define new start and end dates   String startDate = "02/01/2005 GMT";   String endDate = "06/01/2005 GMT";   SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy z");   Date start = df.parse(startDate);   Date end = df.parse(endDate);

// Change the schedule start date   program.reschedule(IProgram.RESCHEDULE.STARTDATE, start);

// Change the schedule end date   program.reschedule(IProgram.RESCHEDULE.ENDDATE, end);

// Move the schedule backward three days   program.reschedule(IProgram.RESCHEDULE.BACKWARDDAYS, new Integer(3));

// Move the schedule forward two days   program.reschedule(IProgram.RESCHEDULE.FORWARDDAYS, new Integer(2));   }   } catch (Exception ex) {      System.out.println(ex);}

17.9 Setting Start and End Timestamps for PPM Date Attributes

Start and End timestamps are automatically set for PPM Date attributes when the end user creates or edits a scheduled PPM task. You can schedule PPM tasks in Working Time, which is configurable in the agile.properties file. When creating or editing PPM objects, the end user must specify a valid date for Working Time within the Start and End values set in agile.properties file for the following PPM Date attributes:

  • Schedule

  • Estimated

  • Actual


Note:

If the specified time for the above Date attributes is not within the Start and End values set in agile.properties, PPM will not complete the end operation of the user. For example, if the Start and End values in agile.properties are 8:00 AM and 6:00 PM and those specified by the user are different, PPM will not complete the applicable operation.

The environment variable (flag) called ppm.date.appendtime automatically sets the appropriate time for POM's Schedule, Estimated, or Actual dates before sending these values to the Agile server to update the Date attribute. When the flag is turned on, the existing time in the date value for Schedule, Estimated, or Actual dates is ignored and is automatically set according to the following rules:

  • If the attribute is Schedule Start Date, Estimated Start Date or Actual Start Date, then the start working time of the day set in agile.properties is appended. For example, if the working time inagile.properties is configured as 8:00:00-12:00:00, 13:00:00-17:00:00, then the start working time for the day is 8 AM. The time portion in the date value for start date attributes is set to 8 AM.

  • If the attribute is Schedule End Date, Estimated End Date or Actual End Date, then the end working time of the day set in agile.properties is appended. For example, if the working time in agile.propertieses is configured as 8:00:00-12:00:00, 13:00:00-17:00:00, then the end working time for the day is 5 PM. The time portion in the date value for end date attributes is set to 5 PM.

By default, the value of the ppm.date.appendtime flag is set to True. This is to ensure backward compatibility of PPM SDK so that SDK Clients compiled in earlier releases can execute without re compilation.

To set timestamp for PPM date attributes in SDK Client

You have the following options:

  • Set the ppm.date.appendtime flag to False using syntax such as java -Dppm.date.appendtime=false <SDK Program Name>

OR,

  • Set an environment variable called ppm.date.appendtime and execute the SDK program.


Note:

This is a global setting and the setting will apply to all SDK programs that are running on the given platform.

17.10 Working with Project Baselines

Project baselines allow you to compare actual progress with your original plans. When you create a baseline, a snapshot of your Project's schedule is preserved. The original estimates contained in the baseline are permanent reference points against which you can compare the updated task structure, schedule, and actual dates.

Baselines can be created only for the root Project object. You can save multiple baselines, and retrieve them later for comparison. The IProgram interface provides the following methods for creating, retrieving, and removing baselines:

  • createBaseline(java.lang.Object)

  • getBaseline()

  • getBaselines()

  • removeBaseline(java.lang.Object)

  • selectBaseline(java.lang.Object)

Example 17-10 Creating and retrieving baselines

try {// Get a Project   IProgram program = 
      (IProgram)m_session.getObject(IProgram.OBJECT_TYPE, "PGM00012");   if (program != null) {

// Create a baseline   Object baseline = program.createBaseline("august 8 baseline");

// Get all baselines   Map map = program.getBaselines();

// Get the first baseline   Set keys = map.keySet();   Object[] objs = keys.toArray();   baseline = map.get(objs[0]);

// Remove the first baseline   program.removeBaseline(baseline);

// Get all baselines again   map = program.getBaselines();

// Select the first baseline   If (map.size() > 0) {   keys = map.keySet();   objs = keys.toArray();
   baseline = map.get(objs[0]);   program.selectBaseline(baseline);      }   }} catch (APIException ex) {      System.out.println(ex);}

17.11 Delegating Ownership of a Project to Another User

The owner or manager of a Project object can assign the ownership of the Project to other users by delegating it. The delegated user receives a request that he can accept or decline. If he accepts, the delegated user becomes owner of the task. A delegated owner is automatically given the Project Manager role for the delegated Project object.

To delegate ownership of a Project, use the IProgram.delegateOwnership() method. When you delegate ownership of a Project, you automatically update the Delegated Owner field, which is read-only. The delegateOwnership() method lets you specify whether delegated ownership also applies to the Project's children.

Example 17-11 Delegating ownership of a Project object

try {// Get the task whose number is T00012   IProgram task = 
      (IProgram)m_session.getObject(IProgram.OBJECT_TYPE, "T00012");   if (task != null) {

// Get a user   IUser user1 = 
      (IUser)m_session.getObject(UserConstants.CLASS_USER, "kkieslowski");   if (user1 != null) {   // Delegate the task to the user      task.delegateOwnership(user1, false);      }   }}    catch (APIException ex) {      System.out.println(ex);}

17.12 Adding Resources to a Project Team

The Team table lets you manage the team member list for a Project object. You can add or remove team members, change team members' roles, and change their allocation. You must have the appropriate privileges to modify a Project's Team table.

When you add a resource to the Team table, you specify what roles the user or user group has for that Project object. The roles available are not the complete set of Agile PLM roles; they are roles specifically related to Project functionality. Here is the list of roles you can assign to team members:

  • Executive

  • Change Analyst

  • Program Team Member

  • Program Manager

  • Resource Pool Owner

  • Program Administrator

For a description of each of these roles, refer to the Agile PLM Administrator Guide.

The Team table has two attributes that require special mention:

  • ProgramConstants.ATT_TEAM_NAME

  • ProgramConstants.ATT_TEAM_ROLES.

These are SingleList and MultiList attributes, respectively. To get the available values for these attributes, use ITable.getAvailableValues() instead of IAttribute.getAvailableValues(). Otherwise, the IAgileList object returned from the method may contain invalid list values.

Example 17-12 Adding resources to a Project team

try {// Get users   IUser user1 = (IUser)session.getObject(UserConstants.CLASS_USER, "daveo");   IUser user2 = (IUser)session.getObject(UserConstants.CLASS_USER, "yvonnec");   IUser user3 = (IUser)session.getObject(UserConstants.CLASS_USER, "albertl");   IUser user4 = (IUser)session.getObject(UserConstants.CLASS_USER, "brians");

// Get a resource pool (user group)   IUserGroup pool = 
      (IUserGroup)session.getObject(IUserGroup.OBJECT_TYPE, "Development");

// Add all four users to the resource pool   ITable usersTable = pool.getTable(UserGroupConstants.TABLE_USERS);      usersTable.createRow(user1);      usersTable.createRow(user2);      usersTable.createRow(user3);      usersTable.createRow(user4);

// Get a Project   IProgram program = 
      (IProgram)session.getObject(IProgram.OBJECT_TYPE, "PGM02423");   if (program != null) {   // Get the Team table of the program   ITable teamTable = program.getTable(ProgramConstants.TABLE_TEAM);   // Get Roles attribute values (use ITable.getAvailableValues)   IAgileList attrRolesValues = 
      teamTable.getAvailableValues(ProgramConstants.ATT_TEAM_ROLES);

// Create a hash map to hold values for row attributes   Map map = new HashMap();

// Add the first user to the team   attrRolesValues.setSelection(new Object[]
      {"Change Analyst","Projects Manager"});
   map.put(ProgramConstants.ATT_TEAM_NAME, user1); 
   map.put(ProgramConstants.ATT_TEAM_ROLES, attrRolesValues);   IRow row1 = teamTable.createRow(map);

// Add the second user to the team   attrRolesValues.setSelection(new Object[]{"Projects Administrator"});   map.put(ProgramConstants.ATT_TEAM_NAME, user2);   IRow row2 = teamTable.createRow(map);

// Add the resource pool to the team   attrRolesValues.setSelection(new Object[]{"Projects Team Member"});   map.put(ProgramConstants.ATT_TEAM_NAME, pool);   IRow row3 = teamTable.createRow(map);}

In Agile Web Client, when you add a resource pool to the Team table, you can replace the pool with one or more resources contained within it. In other words, instead of assigning the entire resource pool, you can assign select users from the pool. The IProgram.assignUsersFromPool() method reproduces this functionality. To use assignUsersFromPool(), you must specify a user group that has already been added to the Project's Team table.

Example 17-13 Assigning users from a resource pool

public void replaceUserGroupWithUser(IProgram program) throws Exception {

// Get the Team table   ITable teamTable = program.getTable(ProgramConstants.TABLE_TEAM);

// Get a table iterator   Iterator it = teamTable.iterator();

// Find a user group and replace it with one of its members, kwong   while(it.hasNext()){      IRow row = (IRow)it.next();      IDataObject object = row.getReferent();      if(object instanceof IUserGroup){      IUserGroup ug = (IUserGroup)object;      ITable users = ug.getTable(UserGroupConstants.TABLE_USERS);      Iterator ref_it = users.getReferentIterator();      while(ref_it.hasNext()){         IUser user = (IUser)ref_it.next();         if(user.getName().equals("kwong")) {            program.assignUsersFromPool(new IUser[]{user}, ug, true); break;            }         }
      }
   }}

17.13 Substituting Project Resources

A resource's availability can frequently change due to overloading, reassignments, vacation, and illness. You can substitute an existing resource for another resource. The current resource's role is assigned to the substituted resource, but only for that Project. To substitute Project resources, use the IProgram.substituteResource() method.

When you substitute resources, you can specify users as well as user groups. You can also specify whether the resource assignment applies to the Project's children.

Example 17-14 Substituting Project resources

try {// Get a Project   IProgram program =      (IProgram)m_session.getObject(IProgram.OBJECT_TYPE, "PGM00012");   if (program != null) {   // Get users      IUser u1 =         (IUser)m_session.getObject(UserConstants.CLASS_USER, "akurosawa");      IUser u2 = 
         (IUser)m_session.getObject(UserConstants.CLASS_USER, "creed");      IUser u3 =         (IUser)m_session.getObject(UserConstants.CLASS_USER, "dlean");      IUser u4 =         (IUser)m_session.getObject(UserConstants.CLASS_USER, "jford");

// Get a user group   IUserGroup ug =      (IUserGroup)m_session.getObject(IUserGroup.OBJECT_TYPE, "Directors");

// Substitute u1 with u3 and do not apply to children   program.substituteResource(u1, u3, false);

// Substitute u2 with u4 and apply to children   program.substituteResource(u2, u4, true);

// Substituete u4 with a user group, and apply to children   program.substituteResource(u4, ug, true);   }} catch (APIException ex) {    System.out.println(ex);}

17.14 Locking or Unlocking Projects

The owner of Project can lock or unlock the Project object. When a Projects is locked, its schedule cannot be modified. To lock or unlock a Project, use the IProgram.setLock() method.


Note:

Projects are automatically locked when you use the Gantt Chart or the Microsoft Project integration functionality in Agile Web Client.

Example 17-15 Locking Projects

try {// Get a Program   IProgram program =      (IProgram)m_session.getObject(IProgram.OBJECT_TYPE, "PGM00012");   if (program != null) {   // Lock it      program.setLock(true);      }} catch (APIException ex) {   System.out.println(ex);
}

17.15 Working with Discussions

During the course of a project, issues arise that require users to collaborate and exchange information. Agile PLM provides threaded discussion functionality that allows team members to reply with their feedback, providing a record of their thoughts and ideas. Discussions are asynchronous; that is, they do not require a simultaneous connection from all discussion participants. People can reply to any thread of the discussion independently. To close issues, action items can be assigned to team resources. The Discussion object is used to manage both threaded discussions and the action items related to them.

Discussion objects, unlike Projects, are not routable objects. Therefore, discussions do not have workflows associated with them.


Note:

The Action Items, Cover Page, and Replies tables appear on the Discussion tab in Agile PLM clients. The Page Two table appears on the Details tab in Agile PLM clients. The Where Used table is not supported, its functionality is replaced by General Info.Related To field.

17.15.1 Creating a Discussion

To create a discussion, use the IAgileSession.createObject() method. When you specify discussion parameters, you must specify the discussion subclass and the following required discussion attributes:

  • Cover Page.NumberCover Page.Subject

In addition, you must also specify data for the Cover Page.Notify List and Cover Page.Message attributes. Otherwise, the discussion does not have a Notification list, or a message that users can respond to.

The following example shows how to create a new discussion and add it to the Discussion table of a Project.

Example: Creating a discussion

Example 17-16 Creating a discussion

try {// Create a hash map variable   Map map = new HashMap();

// Set the Number field   IAgileClass discussionClass = 
      m_session.getAdminInstance().getAgileClass
         ( DiscussionConstants.CLASS_DISCUSSION);   String number = 
      discussionClass.getAutoNumberSources()[0].getNextNumber();

// Set the Subject field   String subject = "Packaging issues";

// Make the Message field visible   IAttribute attr = 
      discussionClass.getAttribute
         (DiscussionConstants.ATT_COVER_PAGE_MESSAGE);
   IProperty propVisible = 
      attr.getProperty(PropertyConstants.PROP_VISIBLE);   IAgileList list = 
      propVisible.getAvailableValues();
   list.setSelection(new Object[] { "Yes" });

// Set the Message field   String message = 
      "We still have problems with the sleeves and inserts." +         "Let's resolve these things at the team meeting on Friday.";

// Set the Notify List field   IUser user1 = m_session.getCurrentUser();   IUser user2 = 
      (IUser)m_session.getObject(UserConstants.CLASS_USER, "jdassin");   attr = discussionClass.getAttribute
      (DiscussionConstants.ATT_COVER_PAGE_NOTIFY_LIST);   list = 
      attr.getAvailableValues(); list.setSelection(new Object[] {user1, user2});

// Put the values into the hash mapmap.put(DiscussionConstants.ATT_COVER_PAGE_NUMBER, number); map.put(DiscussionConstants.ATT_COVER_PAGE_SUBJECT, subject); map.put(DiscussionConstants.ATT_COVER_PAGE_MESSAGE, message); map.put(DiscussionConstants.ATT_COVER_PAGE_NOTIFY_LIST, list);

// Create a Discussion objectIDiscussion discussion = (IDiscussion)m_session.createObject(DiscussionConstants.CLASS_DISCUSSION, map);

// Get a Projects   IProgram program = 
      (IProgram)m_session.getObject(IProgram.OBJECT_TYPE, "PGM00012");   if (program != null) {

// Get the Discussion table   ITable discTable = program.getTable(ProgramConstants.TABLE_DISCUSSION);

// Add the new discussion to the table   discTable.createRow(discussion);
   }
} catch (APIException ex) {    System.out.println(ex);}

17.15.2 Replying to a Discussion

Team members or notified users-that is, users listed in the Cover Page.Notified List field of a discussion-can reply to discussions. When you reply to a discussion, you create another nested table in the Replies table.

Example 17-17 Replying to a discussion

private void replyToDiscussion() throws Exception {   Iterator it;      IDiscussion discussion;

// Get a Project   IProgram program =      (IProgram)m_session.getObject(IProgram.OBJECT_TYPE, "PGM00012");

// Get the Discussion table   ITable discTable =      program.getTable(ProgramConstants.TABLE_DISCUSSION);

// Get the first Discussion listed   if (discTable.size()!=0) {      it = discTable.iterator();      if (it.hasNext()) {      IRow row = (IRow)it.next();      discussion = (IDiscussion)row.getReferent();   }

// Get the Replies table   ITable repliesTable =      discussion.getTable(DiscussionConstants.TABLE_REPLIES);

// Iterate to the only row of the Replies table and send a reply   it = repliesTable.iterator();      if (it.hasNext()) {         IRow row = (IRow)it.next();         IMessage message = (IMessage)row;         HashMap response = new HashMap();

// Set the Subject field (use the same Subject as the parent)   response.put(MessageConstants.ATT_COVERPAGE_SUBJECT,row.getValue      (DiscussionConstants.ATT_REPLIES_SUBJECT));

// Make the Message field visible   IAgileClass discussionClass =      m_session.getAdminInstance().
         getAgileClass(DiscussionConstants.CLASS_DISCUSSION);   IAttribute attr =      discussionClass.getAttribute(DiscussionConstants.ATT_COVER_PAGE_MESSAGE);   IProperty propVisible =      attr.getProperty(PropertyConstants.PROP_VISIBLE);   IAgileList list =      propVisible.getAvailableValues();   list.setSelection(new Object[] { "Yes" });

// Set the Message field   response.put(MessageConstants.ATT_COVERPAGE_MESSAGE,      "The spec needs to be updated to reflect the latest decisions.");

// Send a reply      message.reply(response);      }   }}

The previous example showed how to reply to the root discussion. But what if a discussion has several replies and you want to reply to the latest one? That is a little more complicated, and requires further understanding of the Replies table.

The Replies table of a discussion is different from other Agile PLM tables. It contains only one row, even if there are multiple replies. If the discussion has multiple replies, they are contained within a series of nested tables. To select the latest reply, expand the Replies table to its last nested table. The following figure shows an expanded Replies table in Agile Web Client.

Figure 17-2 Expanded Replies table

Surrounding text describes Figure 17-2 .

You can use a recursive method (one that calls itself) to expand all levels of the Replies table, as shown in the following example. Subsequent levels of the Replies table are obtained by getting the value of the Child Table attribute (DiscussionConstants.ATT_REPLIES_CHILD_TABLE).

Example 17-18 How to expand the Replies table

// Read the Replies table   public void readRepliesTable(IDiscussion discussion) throws Exception {   ITable replies = 
      discussion.getTable(DiscussionConstants.TABLE_REPLIES);   browseReplies(0, replies);   }

// Recursively browse through all levels of the Replies table   void browseReplies(int indent, ITable replies) throws Exception {   Iterator i = replies.iterator();   while (i.hasNext()) {      IRow row = (IRow) i.next();      System.out.print(indent(indent*4));      readRow(row);      System.out.println();   ITable followup =      (ITable)row.getValue(DiscussionConstants.ATT_REPLIES_CHILD_TABLE);   browseReplies(indent + 1, followup);      }   }

// Read each cell in the row and print the attribute name and value   static protected void readRow(IRow row) throws Exception {      ICell[] cells = row.getCells();      for (int j = 0; j < cells.length; ++j) {      Object value = cells[j].getValue();      System.out.print( "\t" + cells[j].getAttribute().getName() + "="+ value);      }   }

// Indent text   private String indent(int level) {
   if (level <= 0) {   return "";   }   char c[] = new char[level*2];   Arrays.fill(c, ' ');   return new String(c);   } 

17.15.3 Joining a Discussion

Agile Web Client allows users to join a discussion by clicking the Discussion tab of a Project, and then clicking the Join button. When you join a discussion, your username is added to the Notify List field of the Discussion object. To join a discussion using the Agile API, simply add yourself to the Notify List field. You can join a discussion only if you are a team member of the Project.


Note:

If you are not on the Notify List of a Discussion object, you cannot read the replies. However, anyone listed on the Team table of a Projects can join a discussion associated with that Project.

Example 17-19 Joining a discussion

try {// Get a Project   IProgram program =      (IProgram)m_session.getObject(ProgramConstants.CLASS_PROGRAM, "PGM00012");   if (program != null) {   // Get the Discussion table      ITable discTable =         program.getTable(ProgramConstants.TABLE_DISCUSSION);

// Get the first discussion   IRow row =(IRow)discTable.iterator().next();   IDiscussion discussion =(IDiscussion)row.getReferent();

// Add yourself and another user to the Notify List field   IUser user1 =   m_session.getCurrentUser();   IUser user2 =      (IUser)m_session.getObject(UserConstants.CLASS_USER, "owelles");   ICell cell =      discussion.getCell(DiscussionConstants.ATT_COVER_PAGE_NOTIFY_LIST);   IAgileList list =(IAgileList)cell.getAvailableValues();   list.setSelection(new Object[] {user1, user2});   }} catch (APIException ex) {System.out.println(ex);} 

17.15.4 Creating an Action Item

Action items can be created as part of a Discussion object. If a discussion raises an issue that requires someone to perform an action, you can assign that action to another user. Action items have a subject, status, due date, and an assigned user. When you create an action item, it appears in the Notifications & Requests Inbox of the assigned user.

To create an action item, use the ITable.createRow() method to add a row to the Action Items table of a Project object. Make sure the map object used to initialize the row contains parameters for the Subject, Assigned To, and Due Date fields.

Example 17-20 Creating an action item

private void replyToDiscussion() throws Exception
{

// Get a Project   IProgram program = 
      (IProgram)m_session.getObject(IProgram.OBJECT_TYPE, "PGM00012");   if (program != null) {

   // Create a hash map for Action Item parameters      HashMap map = new HashMap();

   // Set the Subject field      String subj = "Update packaging requirements";      map.put(ProgramConstants.ATT_ACTION_ITEMS_SUBJECT, subj);

// Set the Assigned To field   IUser user1 = 
      (IUser)m_session.getObject(UserConstants.CLASS_USER, "akurosawa");   IAttribute attr = 
      m_session.getAdminInstance().getAgileClass(   ProgramConstants.CLASS_PROGRAM).getAttribute(      ProgramConstants.ATT_ACTION_ITEMS_ASSIGNED_TO);      IAgileList list = attr.getAvailableValues();      list.setSelection(new Object[] {user1});      map.put(ProgramConstants.ATT_ACTION_ITEMS_ASSIGNED_TO, list);

// Set the Due Date field   DateFormat df = new SimpleDateFormat("MM/dd/yy");   map.put(ProgramConstants.ATT_ACTION_ITEMS_DUE_DATE, df.parse("03/30/05"));

// Get the Action Items table   Table table = program.getTable(ProgramConstants.TABLE_ACTIONITEMS);

// Add the new Action Item to table   table.createRow(map);   }} catch (APIException ex) {   System.out.println(ex);}