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:
assignments (Array of Assignments r/o) • Assignments of this resource to tasks.
completedCost (Number r/o) • Expenditure on this resource that has already happened for work performed.
costPerHour (Number) • Cost per hour of work performed.
costPerUse (Number) • Cost per task that this resource is assigned to.
efficiency (Number) • Amount of work actually performed per time. Defaults to 1.0 (100%).
email (String or null) • Email address of this resource.
members (Array of Resource) • Member resources of this group.
name (String) • Name of this resource.
note (String) • Note of this resource.
schedule (Schedule read-only) • Schedule for this resource.
totalCost (Number r/o) • Total planned expenditure on this resource.
totalHours (Number r/o) • Total assigned hours.
type (ResourceType) • Resource type for this resource.
uniqueID (String r/o) • The unique identifier for the resource. Used in creating URL links to resources in OmniPlan: omniplan:///resource/<uniqueID>
unitsAvailable (Number) • Number of units of this resource available for this project.
Instance Functions
Here are the functions that can be called on an instance of a Resource.
addMember() → (Resource) • Create new resource member and makes this resource into a group.
descendents() → (Array of Resource) • All descendents from this group. Use the members property to retrieve references to the resources within the immediate resource group, but use the descendents() function to retrieve references of all resources within the group hierarchy.
remove() → Delete this resource and remove it from the project, along with any associated assignments.
Resource Types
Here are the various types of OmniPlan resources:
all → (Array of ResourceType r/o) • An array of all items of this enumeration. Often used in the creation of action forms.
equipment → (ResourceType r/o) • A resource that is shared amongst tasks like staff but normally contributes no work itself (efficiency defaults to zero).
group → (ResourceType r/o) • A group of other resources.
material → (ResourceType r/o) • A material - instead of having limited units to be shared amongst tasks, materials count up their total uses.
staff → (ResourceType r/o) • A normal resource that performs work.
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.
adjustAssignedUnits (ResourceAssignmentType r/o) • When changing resource assignments, adjust other assignments’ units.
adjustDuration (ResourceAssignmentType r/o) • When changing resource assignments, adjust the task duration.
adjustEffort (ResourceAssignmentType r/o) • When changing resource assignments, adjust the task effort.
all (Array of ResourceAssignmentType r/o) • An array of all items of this enumeration. Often used in the creation of action forms.
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:
resourceNamed(named:String) → (Resource or null) • Get a given resource by name. If there is more than one with the same name, only returns the first.
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:
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
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;
})();
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:
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"]
})
})
})
})
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.
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()
}
})();
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.”
- DOWNLOAD PLUG-IN • This plug-in will download a JSON file containing data for staffing groups, and then parse the file to import the data into OmniPlan as resource groups.
- DOWNLOAD SHORTCUT • This Shortcut uses one of OmniPlan’s built-in Shortcuts actions to execute the installed plug-in. Installed Shortcut workflows can be run by speaking their titles as Siri commands.
Vision Pro: Import Remote Data |
Import staffing resources from remote server. |
|