Selections
This tutorial describes methods involved with handling selections and selection sets. This also includes functionality to match Person data downloaded from the IRB Exchange, and if needed create contacts if they don't exist.
There are four different pieces to this:
- Adding a method to get the JSON representation of a selection set for upload to the IRB Exchange.
- Adding a method to parse JSON into a selection set on download from the IRB Exchange.
- Adding a method to get a selection entity based on a key attribute or create it if it doesn't exist, based on data downloaded from the IRB Exchange.
- Adding a method to get or create a contact based on data downloaded from the IRB Exchange.
Get JSON for a Selection Set
The goal of this method is to return a JSON array of IDs or other key attribute that represents the selection set.
_IRBSubmission.getSelectionSetIds Method Example
/** Gets a JSON array of selection set IDs.
*
* @param entity (Entity) The entity to get the eSet from
* @param eSetPath (string) Attribute path to the eSet
* @param idPath (string) Attribute path to the ID of the eSet items
*
* @returns {{JSON}}
**/
function getSelectionSetIds(entity, eSetPath, idPath) {
var enrollmentStatus = entity.getQualifiedAttribute(eSetPath);
var enrollmentStatusesObj = [];
if (enrollmentStatus != null) {
var enrollmentStatusElements = enrollmentStatus.elements();
var enrollmentStatusCount = enrollmentStatusElements.count();
for (var i = 1; i <= enrollmentStatusCount; i++) {
enrollmentStatusesObj.push(enrollmentStatusElements.item(i).getQualifiedAttribute(idPath));
}
}
return enrollmentStatusesObj;
}
Parse JSON into Selection Set
The goal of this method is to parse the JSON representation of a selection set as downloaded from the IRB Exchange and store the appropriate data into the local store's selection set.
_IRBSubmission.setSelectionSetFromJson Method Example
/** Takes JSON from the IRB Exchange for a selection set and sets the data onto the appropriate entity.
*
* @param typeName (string) Name of the type of the selection set
* @param entity (Entity) The entity the selection set is stored on
* @param setAttributePath (string) Attribute path to the selection set on the entity
* @param jsonArray (JSON array) Data from Exchange to get selection set from
* @param keyAttributePath (string) Attribute path to the key attribute on the selection type to lookup on
* @param activity (Activity) The Update from IRB Exchange or Update from IRB Exchange (Admin) activity that triggered the call here, or null if initial download
**/
function setSelectionSetFromJson(typeName, entity, setAttributePath, jsonArray, keyAttributePath, activity) {
var eSet = null;
var hasChanged = false;
var localEntity;
if(jsonArray != null) {
eSet = entity.getQualifiedAttribute(setAttributePath);
var jsonArrayCount = jsonArray.length;
// If there's data to set, but the local set doesn't exist, then create it and set on submission
if (jsonArrayCount > 0 && eSet == null) {
var eSetType = ApplicationEntity.getTypeNamed(typeName);
eSet = eSetType.createEntitySet();
entity.setQualifiedAttribute(setAttributePath, eSet);
}
// Create a set of entities to keep for aiding on removed element handling
var entityType = ApplicationEntity.getTypeNamed("ApplicationEntity");
var entitiesToKeep = entityType.createEntitySet();
// Loop the set elements from the Exchange and set locally
var arrayMemberObj, matchingEntities, matchingEntitiesCount;
if(typeName == "Person") {
var ipOrg = entity.getQualifiedAttribute("customAttributes.mSSPSiteInstitutionalProfile.customAttributes.institution");
var isSubscribedToProfileData = ClickIntegrationUtils.IsStoreSubscribedToPublication("ProfileData");
}
for (var i = 0; i < jsonArrayCount; i++) {
arrayMemberObj = jsonArray[i];
// Check to see if the selection is already in the local set
if(typeName == "Person") {
matchingEntities = eSet.query(keyAttributePath + " = '" + arrayMemberObj.email + "'").elements();
} else {
matchingEntities = eSet.query(keyAttributePath + " = '" + arrayMemberObj + "'").elements();
}
// Selection is not in local set, so add it
if(matchingEntities.count() == 0) {
if(typeName == "Person") {
localEntity = getMatchedPersonFromExchangeData(arrayMemberObj.email, arrayMemberObj.firstName, arrayMemberObj.lastName, ipOrg, null, isSubscribedToProfileData, entity, setAttributePath);
} else {
matchingEntities = ApplicationEntity.getResultSet(typeName).query(keyAttributePath + " = '" + arrayMemberObj + "'").elements();
matchingEntitiesCount = matchingEntities.count();
if (matchingEntitiesCount > 1) {
wom.log("WARNING: More than one matching selection entity found for entity with ID " + entity.ID + ": JSON key value = " + arrayMemberObj);
localEntity = matchingEntities.item(1);
} else if (matchingEntitiesCount == 0) {
wom.log("WARNING: No matching selection entity found for entity with ID " + entity.ID + ": JSON key value = " + arrayMemberObj);
localEntity = null;
} else {
localEntity = matchingEntities.item(1);
}
}
if(localEntity != null) {
eSet.addElement(localEntity);
entitiesToKeep.addElement(localEntity);
hasChanged = true;
}
} else {
entitiesToKeep.addElement(matchingEntities.item(1));
}
}
}
// Remove entities that have been deleted on the Exchange
if(eSet != null) {
var eSetElements = eSet.elements();
var eSetCount = eSetElements.count();
for(var i = 1; i <= eSetCount; i++) {
localEntity = eSetElements.item(i);
if(!entitiesToKeep.contains(localEntity)) {
eSet.removeElement(localEntity);
hasChanged = true;
}
}
}
// If data changed, then set the flag on the activity
if(hasChanged == true && activity != null) {
activity.setQualifiedAttribute("customAttributes.hasDataChanged", true);
}
}
Technical note about this method example:
- When integrated using CPIP and IRB is not the profile data host, this method fires an event that instructs the profile data host to create organizations as needed.
- If more than one selection entity is matched on the one downloaded from the Exchange, then a warning is posted to the wom log and the first item is retrieved.
- If no selection entity is matched on the one downloaded from the Exchange, then a warning is posted to the wom log and no further action is taken.
- A flag is set on the activity after processing to denote whether data changed or not, which aids in activity history feedback to whether data changed.
Handling Persons
Since Person is a specially handled entity type in Portal, we handle it specially here too. When Person data is downloaded from the IRB Exchange, this method attempts to match the Person by preferred email address. If no match is made, the method creates a contact and sets what data it has for the contact.
_IRBSubmission.getMatchedPersonFromExchangeData Method Example
/** Gets a person matched by email or creates if doesn't exist. If multiple matches by email are made, then attempts to match on name.
*
* @param email (string) Email address of the PI
* @param firstName (string) First name of the PI
* @param lastName (string) Last name of the PI
* @param organization (Company) The person's employer organization
* @param organizationName (string) The person's employer organization name
* @param isSubscribedToProfileData (Boolean) Flag indicating whether this store is subscribed to ProfileData over CPIP
* @param submission (_IRBSubmission) The submission the data is being downloaded for
* @param attributePath (string) Attribute path the person will be stored at
*
* @returns {Person} The person matched or created
**/
function getMatchedPersonFromExchangeData(email, firstName, lastName, organization, organizationName, isSubscribedToProfileData, submission, attributePath) {
var person;
// Email is set, so match on it
if(email != undefined && email != "") {
var matchingPersonsByEmail = ApplicationEntity.getResultSet("Person").query("contactInformation.emailPreferred.emailAddress = '" + email + "'").elements();
var matchingPersonsByEmailCount = matchingPersonsByEmail.count();
if(matchingPersonsByEmailCount == 0) {
person = createPerson(email, firstName, lastName, organization, organizationName, isSubscribedToProfileData, submission, attributePath);
} else if(matchingPersonsByEmailCount == 1) {
// Get only match for PI
person = matchingPersonsByEmail.item(1);
} else {
person = matchByName(firstName, lastName, matchingPersonsByEmail);
}
}
// Email is not set, match by name
else if(lastName != undefined) {
person = matchByName(firstName, lastName, null);
if(person == null) {
person = createPerson(null, firstName, lastName, organization, organizationName, isSubscribedToProfileData, submission, attributePath);
}
}
// No user info supplied, so return null
else {
return null;
}
return person;
}
// Helper method to create a Person
function createPerson(email, firstName, lastName, organization, organizationName, isSubscribedToProfileData, submission, attributePath) {
var person = null;
// Create Person for PI
if(isSubscribedToProfileData == true) {
var createPersonEventArgs = {};
createPersonEventArgs.type = "Person";
createPersonEventArgs.personFirstName = firstName;
createPersonEventArgs.personLastName = lastName;
createPersonEventArgs.personEmail = email;
if(organization != null) {
createPersonEventArgs.personEmployerPoRef = organization + "";
} else if(organizationName != null) {
createPersonEventArgs.personEmployerName = organizationName;
} else {
throw new Error("organization or organizationName must be set");
}
createPersonEventArgs.submissionPoRef = submission + "";
createPersonEventArgs.attributePath = attributePath;
submission.triggerEvent("ClickIRBSubmissionCreateProfileData", JSON.stringify(createPersonEventArgs, null, null));
} else {
person = Person.createEntity();
person.ID = Person.getID();
person.setQualifiedAttribute("contactInformation.emailPreferred.emailAddress", email);
person.setQualifiedAttribute("firstName", firstName);
person.setQualifiedAttribute("lastName", lastName);
person.setQualifiedAttribute("employer", organization);
}
return person;
}
// Helper method to match a person by name
function matchByName(firstName, lastName, matchingPersonsByEmail) {
var person = null;
// Multiple matches, so now match on name out of that set
var matchingPersonsByName = ApplicationEntity.getResultSet("Person").query("firstName like '" + firstName + "' and lastName like '" + lastName + "'").elements();
if(matchingPersonsByName.count() > 0) {
// Matched at least one person by name, get the first match
person = matchingPersonsByName.item(1);
} else if(matchingPersonsByEmail != null) {
// No match by name, get the first match by email
person = matchingPersonsByEmail.item(1);
}
return person;
}
Technical notes about this method example:
- If this store is integrated using CPIP and does not host the ProfileData publication, then it fires an event to have the profile data created.
- In the unlikely case that more than one match is made based on preferred email address, then the method attempts to additionally match by first and last name. If match by name doesn't produce a single match, then it uses the first match based on email.