×

Resources

An OmniPlan project includes the team members, equipment and materials needed to bring the project to fruition. Every person, piece of infrastructure, and raw ingredient that contributes to reaching the project’s goal is counted as a resource.

Creating resources works much like creating tasks. Like tasks, resources can also exist in hierarchical groups.

Instance Properties

Here are the properties of an OmmiPlan resource:

Instance Functions

Here are the functions that can be called on an instance of a Resource.

Resource Types

Here are the various types of OmniPlan resources:

omniplan://localhost/omnijs-run?script=var%20rsc%20%3D%20actual%2ErootResource%2EaddMember%28%29%0Arsc%2Etype%20%3D%20ResourceType%2Eequipment%0Arsc%2Ename%20%3D%20%22Backhoe%22%0Avar%20costValue%20%3D%20Decimal%2EfromString%28%22275%22%29%0Arsc%2EcostPerHour%20%3D%20costValue
Create an Equipment Resource
 

var rsc = actual.rootResource.addMember() rsc.type = ResourceType.equipment rsc.name = "Backhoe" var costValue = Decimal.fromString("275") rsc.costPerHour = costValue

Resource Assignment Types

The process of adjusting project resources can effect interaction with other project elements. Resource Assignment Types determine the method used in calculating any adjustments.

Resource Assignment Type


var rsc = actual.resourceNamed("Bob Jones") if(rsc){ var task = actual.rootTask.addSubtask() task.title = "Analyze Design" task.addAssignment(rsc) task.resourceAssignmentType = ResourceAssignmentType.adjustAssignedUnits }

Generating a Reference to a Resource

To generate a reference to a specific resource, use the resourceNamed("resourceName") function of the Scenario class. Pass-in the name of the resource to the function called on a scenario (actual or baseline) and the result will be either a reference to the first matching resource, or null — indicating that no matching resource was located:

In the following example, a reference to the specified resource is generated and used to assign the resource to a new task:

New Assigned Task


var rsc = actual.resourceNamed("Margret Jensen") if(rsc){ var task = actual.rootTask.addSubtask() task.title = "Prune Shrubbery" task.addAssignment(rsc) }

Here’s a script variation that uses the addMember() function to create a resource if one doesn’t already exist:

omniplan://localhost/omnijs-run?script=var%20resourceName%20%3D%20%22Bob%20Sykes%22%0Avar%20rsc%20%3D%20actual%2EresourceNamed%28resourceName%29%0Aif%28%21rsc%29%7B%0A%09rsc%20%20%3D%20actual%2ErootResource%2EaddMember%28%29%0A%09rsc%2Etype%20%3D%20ResourceType%2Estaff%0A%09rsc%2Ename%20%3D%20resourceName%0A%09rsc%2EcostPerHour%20%3D%20Decimal%2EfromString%28%2234%2E50%22%29%0A%09rsc%2Eemail%20%3D%20%22bob%2Esykes%40sykes%2Dconstruction%2Ecom%22%0A%09rsc%2Enote%20%3D%20%22Not%20available%20on%20Tuesdays%22%0A%7D%0Avar%20task%20%3D%20actual%2ErootTask%2EaddSubtask%28%29%0Atask%2Etitle%20%3D%20%22Dig%20Drainage%20Trench%22%0Atask%2EaddAssignment%28rsc%29%0Avar%20task%20%3D%20actual%2ErootTask%2EaddSubtask%28%29%0Atask%2Etitle%20%3D%20%22Connect%20City%20Services%22%0Atask%2EaddAssignment%28rsc%29
Reference|Create Resource
 

var resourceName = "Bob Sykes" var rsc = actual.resourceNamed(resourceName) if(!rsc){ rsc = actual.rootResource.addMember() rsc.type = ResourceType.staff rsc.name = resourceName rsc.costPerHour = Decimal.fromString("34.50") rsc.email = "bob.sykes@sykes-construction.com" rsc.note = "Not available on Tuesdays" } var task = actual.rootTask.addSubtask() task.title = "Dig Drainage Trench" task.addAssignment(rsc) var task = actual.rootTask.addSubtask() task.title = "Connect City Services" task.addAssignment(rsc)

Here's a script that uses the assignments property of the Resource class to display an alert showing an assignment summary for a resource:

assignment-summary-for-resource
omniplan://localhost/omnijs-run?script=var%20resourceName%20%3D%20%22Bob%20Sykes%22%0Avar%20rsc%20%3D%20actual%2EresourceNamed%28resourceName%29%0Aif%28rsc%29%7B%0A%09var%20reportTitle%20%3D%20%60Assignments%20for%20%24%7BresourceName%7D%60%0A%09var%20items%20%3D%20rsc%2Eassignments%0A%09var%20entry%20%3D%20%60%24%7Bitems%2Elength%7D%20Assignments%20%3D%20%24%7Brsc%2EtotalHours%2F3600%7D%20Hours%60%0A%09var%20reportBody%20%3D%20entry%20%2B%20%22%5Cn%22%0A%09items%2EforEach%28item%20%3D%3E%20%7B%0A%09%09entry%20%3D%20%60%24%7Bitem%2Etask%2Etitle%7D%20%C2%B7%20%24%7Bitem%2Etask%2EeffortRemaining%2F3600%7D%20Hours%60%0A%09%09reportBody%20%2B%3D%20%22%5Cn%22%20%2B%20entry%0A%09%7D%29%0A%09new%20Alert%28reportTitle%2C%20reportBody%29%2Eshow%28%29%0A%7D
Assignment Summary for Resource
 

var resourceName = "Bob Sykes" var rsc = actual.resourceNamed(resourceName) if(rsc){ var reportTitle = `Assignments for ${resourceName}` var items = rsc.assignments var entry = `${items.length} Assignments = ${rsc.totalHours/3600} Hours` var reportBody = entry + "\n" items.forEach(item => { entry = `${item.task.title} · ${item.task.effortRemaining/3600} Hours` reportBody += "\n" + entry }) new Alert(reportTitle, reportBody).show() }

Import People Data from File

This Omni Automation action plugIn will create new staff resources using the data read from either a chosen TSV (tabbed-delimited) or CSV (comma-separated) file.

Note that the action plugIn incorporates the use of the FilePicker class to enable the user to choose the file to import.

Also note that, for this example plugIn, the exported data has two columns: the staffer’s full name, and the staffer’s email address. (DOWNLOAD EXAMPLE FILES)

Import Data in CSV and TSV Formats


// COMMA-SEPARATED VALUES (CSV) "Alissa Archer","huygyaq2@postmaster.com" "Marcus Simon","tfmcf6703@icqmail.com" "Jay Freeman","ypmvyc006@mail2world.com" "Margret Jensen","eupt00@excite.com" // TAB-SEPARATED VALUES (TSV) Gordon Valencia    huygyaq2@postmaster.com Tyler Burton    tfmcf6703@icqmail.com Merle Montgomery    ypmvyc006@mail2world.com Lloyd Pacheco    eupt00@excite.com
New Staff Resources Importing Data from File
 

/*{ "type": "action", "targets": ["omniplan"], "author": "Otto Automator", "identifier": "com.omni-automation.op.import-people-from-file", "version": "1.2", "description": "Will create new resources using the person data read from a chosen comma- or tab-delimited file.", "label": "Import People from File", "shortLabel": "Import People" }*/ (() => { var action = new PlugIn.Action(function(selection, sender){ // action code // selection options: project, tasks, resources // CREATE INSTANCE OF FILEPICKER var picker = new FilePicker() picker.folders = false picker.multiple = false var TSVtype = new FileType("public.tab-separated-values-text") var CSVtype = new FileType("public.comma-separated-values-text") picker.types = [TSVtype, CSVtype] // DECLARE THE PROMISE VARIABLE var pickerPromise // ADJUST PROMPT FOR PLATFORM if(app.platformName === "iOS"){ msg = "Tap ”Continue” and choose the comma- or tab-delimited text file to import from the forthcoming file picker:" var alert = new Alert("Select File", msg) alert.addOption("Continue") alert.show(result => { pickerPromise = picker.show() }) } else if (app.platformName === "macOS"){ picker.message = "Choose the comma- or tab-delimited text file to import:" pickerPromise = picker.show() } // READ AND PROCESS CONTENTS OF THE CHOSEN TEXT FILE pickerPromise.then(urls => { var url = urls[0] var typeIndicator = (url.string.endsWith(".tsv")) ? 0:1 url.fetch(data => { data = data.toString().split(/\r?\n/) var items = new Array() for(var i = 0; i < data.length; i++){ if(typeIndicator === 0){ items.push(data[i].split(/\t/)) } else { items.push(data[i].split(",")) } } // GET THE CURRENT RESOURCE EMAIL ADDRESSES var resourceEmails = new Array() actual.rootResource.descendents().forEach((rsc)=>{ if (rsc.type === ResourceType.staff && rsc.email){ resourceEmails.push(rsc.email) } }) // CREATE NEW RESOURCE FOR EACH UNIQUE ITEM items.forEach((item)=>{ // TRIM ENCASING QUOTES IF SOURCE IS CSV var personName = (typeIndicator === 0) ? item[0]:item[0].replace(/^\"+|\"+$/gm,'') var personEmail = (typeIndicator === 0) ? item[1]:item[1].replace(/^\"+|\"+$/gm,'') // ADD PERSONS WITH UNIQUE EMAIL AS RESOURCES if (!resourceEmails.includes(personEmail)){ var newResource = actual.rootResource.addMember() newResource.type = ResourceType.staff newResource.name = personName newResource.email = personEmail resourceEmails.push(personEmail) } }) // end forEach() }) // end fetch() }) // end promise }); action.validate = function(selection, sender){ // validation code // selection options: project, tasks, resources return true }; return action; })();

 01-10  The action metadata determines which Omni applications are targeted by the action, the action’s Automation menu and toolbar titles, and the version and description of the action.

 11-89  The main function that contains the action code.

 12-80  Create a new instance of the Action class that includes a callback function that receives references to the selection and sender objects as its default parameters.

 17  Create and store an instance of the FilePicker class.

 18-19  Set the properties of the file picker to allow the selection of a single file.

 20-21  Create instances of the FileType class for files that contain either comma-separated-values or tab-separated-values.

 22  Assign the file types of the file picker instance to the created file types.

 25  Declare a variable that will contain the JavaScript promise object that is returned when the file picker is called.

 41-78  Calling the then() function on promise object will enable the processing of the chosen file, whose file URL is passed as an array into the callback function.

 42  The file URL for the chosen file is extracted from the passed URL array (the passed-in array contains a single-item)

 43  A JavaScript ternary statement returns a value of 0 if the file URL ends with “tsv” or 1 if the file URL ends with “csv.”

 44-77  The fetch() function is used to extract the data from the file, and passes the data object into the callback function.

 45  Convert the passed-in data to text and then delimit it by paragraphs or linefeeds into an array of text objects.

 46  Create an empty array to hold the split data objects.

 47-53  Iterate the array of text objects and split each one based upon whether the data of the source file was tab- or comma-delimited. The resulting JavaScript object will contain two text items, and will be appended to the previously created array.

 56-61  To ensure that duplicate resources are not created, generate a list of the existing resource emails, which will be used for comparison before creating a new resource.

 64-78  Use the JavaScript forEach() function to create new resources using the data from each of the retrieved text pairs.

 66-67  If the source data was comma-delimited, strip the encasing quotes from each text string of the object.

 69-75  If the potential email address is not in the list of existing resource emails, then create a new resource set its name and email address.

 74  Append the email address of the created resource to the list of current email addresses.

 75-79  The resulting boolean value of the validation function determines whether the action plugIn appears in the Automation menu.

Resource Groups

A Resource Group is a resource that contains other resources. It is often used to create “teams” assigned to specific tasks.

The following script example creates resource groups for the teams imported from a chosen JSON data file (DOWNLOAD) (example data generated by mockaroo.com)

Group Data
 

[ { "name": "Red Team", "members": [ { "email": "ldell4@xinhuanet.com", "phone": "800-604-1071", "name": "Lewes Dell 'Orto", "role": "Food Chemist" }, { "email": "mcovendon3@symantec.com", "phone": "694-301-1238", "name": "Markos Covendon", "role": "Programmer Analyst I" }, { "email": "swolton2@freewebs.com", "phone": "793-362-0138", "name": "Sylas Wolton", "role": "Developer IV" }, { "email": "sstockport1@squarespace.com", "phone": "215-981-3559", "name": "Shirlene Stockport", "role": "Product Engineer" }, { "email": "bgerant0@ucoz.ru", "phone": "373-419-0679", "name": "Biron Gerant", "role": "VP Sales" } ] }, { "name": "Blue Team", "members": [ { "email": "fbaugham9@people.com.cn", "phone": "486-110-7943", "name": "Farris Baugham", "role": "Nurse" }, { "email": "sbird8@gnu.org", "phone": "511-593-7065", "name": "Sonnnie Bird", "role": "Research Associate" }, { "email": "hstangroom7@howstuffworks.com", "phone": "324-905-1600", "name": "Hill Stangroom", "role": "Marketing Manager" }, { "email": "tchislett6@yelp.com", "phone": "929-853-7355", "name": "Thebault Chislett", "role": "Editor" }, { "email": "edurand5@imageshack.us", "phone": "515-924-8322", "name": "Eldredge Durand", "role": "Food Chemist" } ] }, { "name": "Green Team", "members": [ { "email": "ciaduccellie@time.com", "phone": "862-281-2490", "name": "Clarke Iaduccelli", "role": "Environmental Tech" }, { "email": "ajankiewiczd@symantec.com", "phone": "355-390-3438", "name": "Angelica Jankiewicz", "role": "Junior Executive" }, { "email": "dpackc@mediafire.com", "phone": "604-389-5099", "name": "Debee Pack", "role": "Account Executive" }, { "email": "kmaturab@opensource.org", "phone": "993-456-9106", "name": "Kala Matura", "role": "GIS Technical Architect" }, { "email": "jpadricka@istockphoto.com", "phone": "282-371-9596", "name": "Johnette Padrick", "role": "Internal Auditor" } ] }, { "name": "Orange Team", "members": [ { "email": "ejouhanj@springer.com", "phone": "280-668-5317", "name": "Ephrayim Jouhan", "role": "Chemical Engineer" }, { "email": "hblagbroughi@nydailynews.com", "phone": "299-880-0063", "name": "Hermione Blagbrough", "role": "VP Accounting" }, { "email": "kvanbrughh@nymag.com", "phone": "631-123-6111", "name": "Kris VanBrugh", "role": "VP Marketing" }, { "email": "ldoriang@friendfeed.com", "phone": "498-262-5636", "name": "Lowrance Dorian", "role": "Account Coordinator" }, { "email": "pmewsf@adobe.com", "phone": "614-164-0036", "name": "Pearl Mews", "role": "Web Developer IV" } ] } ]

Here’s the script for importing the example resource files:

omniplan://localhost/omnijs-run?script=var%20picker%20%3D%20new%20FilePicker%28%29%0Apicker%2Efolders%20%3D%20false%0Apicker%2Emultiple%20%3D%20false%0Apicker%2Etypes%20%3D%20%5BFileType%2Ejson%5D%0Apicker%2Eshow%28%29%2Ethen%28urlsArray%20%3D%3E%20%7B%0A%09var%20aURL%20%3D%20urlsArray%5B0%5D%0A%09aURL%2Efetch%28data%20%3D%3E%20%7B%0A%09%09var%20resourceData%20%3D%20JSON%2Eparse%28data%2EtoString%28%29%29%0A%09%09resourceData%2EforEach%28team%20%3D%3E%20%7B%0A%09%09%09var%20rsc%20%3D%20actual%2ErootResource%2EaddMember%28%29%0A%09%09%09rsc%2Etype%20%3D%20ResourceType%2Egroup%0A%09%09%09rsc%2Ename%20%3D%20team%5B%22name%22%5D%0A%09%09%09staff%20%3D%20team%5B%22members%22%5D%0A%09%09%09staff%2EforEach%28staffer%20%3D%3E%20%7B%0A%09%09%09%09teammate%20%3D%20rsc%2EaddMember%28%29%0A%09%09%09%09teammate%2Etype%20%3D%20ResourceType%2Estaff%0A%09%09%09%09teammate%2Ename%20%3D%20staffer%5B%22name%22%5D%0A%09%09%09%09teammate%2Eemail%20%3D%20staffer%5B%22email%22%5D%0A%09%09%09%09teammate%2Enote%20%3D%20staffer%5B%22role%22%5D%0A%09%09%09%7D%29%0A%09%09%7D%29%0A%09%7D%29%0A%7D%29%0A
Import Resource Groups from JSON File
 

var picker = new FilePicker() picker.folders = false picker.multiple = false picker.types = [FileType.json] picker.show().then(urlsArray => { var aURL = urlsArray[0] aURL.fetch(data => { var resourceData = JSON.parse(data.toString()) resourceData.forEach(team => { var rsc = actual.rootResource.addMember() rsc.type = ResourceType.group rsc.name = team["name"] staff = team["members"] staff.forEach(staffer => { teammate = rsc.addMember() teammate.type = ResourceType.staff teammate.name = staffer["name"] teammate.email = staffer["email"] teammate.note = staffer["role"] }) }) }) })

 01  Store an new instance of the FilePicker class in the variable: filepicker

 02-04  Set the properties of the file picker to accept a single JSON file as the selection.

 05  Use the show() method to display the file picker interface. The result of this command is a JavaScript promise.

 05-21  Append the then() function to the show() method to process the promise result of the file picker. The then() callback function will receive an array of URLs to the chosen items as its default parameter.

 06  The file URL to the chosen JSON file will be the only item in the passed array of URLs.

 07-20  Call the fetch() function on the file URL to extract its contents and pass the resulting data to the function.

 08  Use the JavaScript toString() function to convert the file data to text, and then call the parse() method of of the JSON class to convert the text into a JSON array of objects (in this case the “teams” of people).

 09-19  Iterate the JSON array of “team” objects.

 10  Create a new resource.

 11  Set the type of the new resource to group.

 12  Set the name of the new resource group to the name of the team.

 13  Extract the array of team member objects from the JSON team object

 14-18  Use the JavaScript forEach() function to process each of the team member JSON objects.

 15  Add a new resource to the group resource.

 16  Set the type of the new resource to be a staff resource.

 17  Assign the name of the new staff resource to the name of the team member.

 18  Assign the email of the new staff resource to the email of the team member.

 19  Assign the note of the new staff resource to the role of the team member.

imported-resource-groups

Importing Remote Resource Data

The following script, a variation of the previous script, uses the Fetch.Request and Fetch.Response classes of the URL class to retrieve the resource group data from a secure JSON file stored on this website. Detailed documentation about the HTTP Fetch API can be found here.

omniplan://localhost/omnijs-run?script=%28async%20%28%29%20%3D%3E%20%7B%0A%09try%20%7B%0A%09%09function%20createUtterance%28textToSpeak%29%7B%0A%09%09%09langCode%20%3D%20Speech%2EVoice%2EcurrentLanguageCode%0A%09%09%09voiceObj%20%3D%20Speech%2EVoice%2EwithLanguage%28langCode%29%0A%09%09%09utterance%20%3D%20new%20Speech%2EUtterance%28textToSpeak%29%0A%09%09%09utterance%2Evoice%20%3D%20voiceObj%0A%09%09%09utterance%2Erate%20%3D%20Speech%2EUtterance%2EdefaultSpeechRate%0A%09%09%09return%20utterance%0A%09%09%7D%0A%09%09var%20synthesizer%20%3D%20new%20Speech%2ESynthesizer%28%29%0A%0A%09%09targetURL%20%3D%20%22https%3A%2F%2Fomni%2Dautomation%2Ecom%2Fsecure%2Fresource%2Dgroups%2Ejson%22%0A%09%09request%20%3D%20URL%2EFetchRequest%2EfromString%28targetURL%29%0A%09%09request%2Emethod%20%3D%20%27GET%27%0A%09%09userAccount%20%3D%20%22otto%2Dautomator%22%0A%09%09userPassword%20%3D%20%22omni%2Dautomation%22%0A%09%09data%20%3D%20Data%2EfromString%28userAccount%20%2B%20%22%3A%22%20%2B%20userPassword%29%0A%09%09credentials%20%3D%20data%2EtoBase64%28%29%0A%09%09request%2Eheaders%20%3D%20%7B%22Authorization%22%3A%20%22Basic%22%20%2B%20%22%20%22%20%2B%20credentials%7D%0A%0A%09%09response%20%3D%20await%20request%2Efetch%28%29%0A%09%09%0A%09%09responseCode%20%3D%20response%2EstatusCode%0A%09%09if%20%28responseCode%20%3E%3D%20200%20%26%26%20responseCode%20%3C%20300%29%7B%0A%09%09%09responseJSON%20%3D%20JSON%2Eparse%28response%2EbodyString%29%0A%09%09%09responseJSON%2EforEach%28team%20%3D%3E%20%7B%0A%09%09%09%09rsc%20%3D%20actual%2ErootResource%2EaddMember%28%29%0A%09%09%09%09rsc%2Etype%20%3D%20ResourceType%2Egroup%0A%09%09%09%09rsc%2Ename%20%3D%20team%5B%22name%22%5D%0A%09%09%09%09staff%20%3D%20team%5B%22members%22%5D%0A%09%09%09%09staff%2EforEach%28staffer%20%3D%3E%20%7B%0A%09%09%09%09%09teammate%20%3D%20rsc%2EaddMember%28%29%0A%09%09%09%09%09teammate%2Etype%20%3D%20ResourceType%2Estaff%0A%09%09%09%09%09teammate%2Ename%20%3D%20staffer%5B%22name%22%5D%0A%09%09%09%09%09teammate%2Eemail%20%3D%20staffer%5B%22email%22%5D%0A%09%09%09%09%09teammate%2Enote%20%3D%20staffer%5B%22role%22%5D%0A%09%09%09%09%7D%29%0A%09%09%09%7D%29%09%0A%09%09%09utterance%20%3D%20createUtterance%28%22Done%21%22%29%0A%09%09%09synthesizer%2EspeakUtterance%28utterance%29%0A%0A%09%09%7D%20else%20%7B%0A%09%09%09console%2Eerror%28JSON%2Estringify%28response%2Eheaders%29%29%0A%09%09%09throw%20%7B%0A%09%09%09%09name%3A%20%22Fetch%20Error%22%2C%0A%09%09%09%09message%3A%20%60The%20server%20returned%20a%20status%20code%20of%3A%20%24%7BresponseCode%7D%60%0A%09%09%09%7D%0A%09%09%7D%0A%09%7D%0A%09catch%28err%29%7B%0A%09%09utterance%20%3D%20createUtterance%28err%2Emessage%29%0A%09%09synthesizer%2EspeakUtterance%28utterance%29%0A%09%09new%20Alert%28err%2Ename%2C%20err%2Emessage%29%2Eshow%28%29%0A%09%7D%0A%7D%29%28%29%3B
Create Resource Groups using Remote Data
 

(async () => { try { function createUtterance(textToSpeak){ langCode = Speech.Voice.currentLanguageCode voiceObj = Speech.Voice.withLanguage(langCode) utterance = new Speech.Utterance(textToSpeak) utterance.voice = voiceObj utterance.rate = Speech.Utterance.defaultSpeechRate return utterance } var synthesizer = new Speech.Synthesizer() targetURL = "https://omni-automation.com/secure/resource-groups.json" request = URL.FetchRequest.fromString(targetURL) request.method = 'GET' userAccount = "otto-automator" userPassword = "omni-automation" data = Data.fromString(userAccount + ":" + userPassword) credentials = data.toBase64() request.headers = {"Authorization": "Basic" + " " + credentials} response = await request.fetch() responseCode = response.statusCode if (responseCode >= 200 && responseCode < 300){ responseJSON = JSON.parse(response.bodyString) responseJSON.forEach(team => { rsc = actual.rootResource.addMember() rsc.type = ResourceType.group rsc.name = team["name"] staff = team["members"] staff.forEach(staffer => { teammate = rsc.addMember() teammate.type = ResourceType.staff teammate.name = staffer["name"] teammate.email = staffer["email"] teammate.note = staffer["role"] }) }) utterance = createUtterance("Done!") synthesizer.speakUtterance(utterance) } else { console.error(JSON.stringify(response.headers)) throw { name: "Fetch Error", message: `The server returned a status code of: ${responseCode}` } } } catch(err){ utterance = createUtterance(err.message) synthesizer.speakUtterance(utterance) new Alert(err.name, err.message).show() } })();
group-resource-import

DEMO: Import Staffing Groups from Server

The following plug-in and Shortcuts workflow can be installed for demonstration purposes.

Install both items by activating the provided links. Once installed, the plug-in can be executed via Siri by uttering this voice command:

“Hey Siri, import staff groups from server.”

Vision Pro: Import Remote Data
Import staffing resources from remote server.