Discussing the Civic Platform Object Model

The Civic Platform object model comprises a hierarchy of objects that organizes access to different parts of Civic Platform. At the root of the tree is the aa object.

Object Model shows that the aa object is at the root, with a few of the objects underneath the aa object listed. Each of the objects listed beneath the aa object is a property of the aa object.

Figure: Object Model



From earlier examples, we know that the aa object also has methods.Object Model Root Methods shows two of the methods of the aa object. You can find documentation for all the methods of the aa object in the EMSE Javadocs.

Figure: Object Model Root Methods



Notice that each of the objects under the aa object has a name that corresponds to a piece of Civic Platform. Each of these objects beneath aa also provides methods for interacting with (Object Model Object Methods).

Figure: Object Model Object Methods



In this diagram, we can see that the inspection object has some methods that we can use. If we look at the documentation for the getInspections method we find the method definition:

getInspections(CapID capID) returns Result

The method syntax tells us that the name of the method is getInspections and that this method takes one parameter. Two words describe each parameter. The first word tells us the kind of parameter. The second tells us the parameter named. The name helps us to understand how to use the parameter inside the method. The type is “String”, “Number”, or perhaps the name of an object. In the case of this parameter the type is CapID. We can look at the documentation for the method’s parameters and see:

capID – The CapID for the record from which you want to get the array of inspections.

We can also look up the CapID object, and read its description to find out more about it. So now, we know that we need to pass in a CapID object that identifies the record for which we want to get an array of inspections.

If we look at the end of the method definition we see “returns Result”. This last part of the method tells us that, when we call this method, we get back a result object. The result object provides an indicator of whether the method succeeded, an error type and error message if the method failed, and the output if the method succeeded.

The getSuccess method returns a Boolean value that is true if the method succeeded and false if it did not. If the getSuccess method returns false you can check the getErrorType and getErrorMessage methods return values to retrieve some information about the error. The getSuccess method returns true you can retrieve the actual output of the method by calling getOutput.

When we look at the documentation for the getInspections method we can see that the getOutput method of the result object returned by the getInspections method returns InspectionItem. The double brackets [ ] tell us that it is an array of InspectionItem objects and not just one InspectionItem object.

Return Value:

Result – Object, see description in this document. The getOutput method of the result object returns InspectionItem[], an array of InspectionItem object. See InspectionItem object description in this document.

At this point, we still have three questions. First, how do we create a CapID object that identifies the record we want? Second, how do we call the method? Third, how do we work with the array of InspectionItem objects returned to us by calling this method?

For the first question, we notice that there is a cap object beneath the aa object. We go to the reference documentation and see that the cap object has this method:

getCapID(String id1, String id2, String id3) returns Result

Return Value:

Result - Object, see description in this document. The getOutput method of the result object returns a CapID object. The getErrorMessage method returns CapNotFound if the method does not find a record that matches the three five digit ids.

This method returns a result object that has the CapID object we need. The method takes three strings that are the three ids for the permit. You can set up your Civic Platform instance to use a custom id, rather than a fifteen digit id, and a method of the cap object allows you to retrieve a CapID object using a custom id. Now we need to know how to call this method. Here is how:

myResult = aa.cap.getCapID(“01BLD”, “00000”, “00027”);
if(myResult.getSuccess()) {
 myCap = myResult.getOutput();
} else {
 aa.print(myResult.getErrorMessage());
 aa.abortScript();
}

We can see from this line that we access the cap object as a property of the aa object and then call the getCapID method of the cap object. We pass the three strings, that identify the record we want, to the method . The method returns the result object, which we use to see if the getCapID method succeeded. The retrieved CapID object, by calling the getOutput method of the result object, identifies our record and we assign that object as the value of the myCap variable.

Now we have the value we need as the parameter to pass in to the getInspections method. We can see, from the example of calling a method of the cap object, how to call the getInspections method of the inspection object. We know that we get back an array object from the getInspections method call, so now we know how to write a two line script that retrieves an array of inspections for a particular record. Here is our script so far:

myResult = aa.cap.getCapID(“01BLD”, “00000”, “00027”);
if(myResult.getSuccess()) {
 myCap = myResult.getOutput();
} else {
 aa.print(myResult.getErrorMessage());
 aa.abortScript();
}
myResult = aa.inspection.getInspections(myCap);

if(myResult.getSuccess()) {
 myInspections = myResult.getOutput();
} else {
 aa.print(myResult.getErrorMessage());
 aa.abortScript();
}

This script does not display any output yet. At this point, we should note two things. First, use a record id that exists in the Civic Platform instance for which you are writing scripts. Second, use a record that has scheduled, resulted, or cancelled inspections.

What happens if you choose a record that does not exist? If the method does not find a record that matches the id, the result object’s getSuccess method returns false, and you need to check the error type and error message.

What happens if the record does not include any scheduled, resulted, or cancelled inspections? You get back a zero length array that means there are no inspections for the record you selected. We come back to these possibilities a bit later, but for now let us assume that we have the right record id, and that the record includes inspections.

Now we need to do something with the array of InspectionItem objects we got back from calling the getInspections. What can we do? Well, let us start by trying to print out some information about the inspections scheduled. We want to print out a few important pieces of information about each inspection. We go to each element in the array and call some methods on the object stored at that position. This example reminds us of the example use of a ‘while’ loop. Here is the example again for your review:

myArray = new Array();
myArray[0] = “Oranges”;
myArray[1] = “Bagels”;
myArray[2] = “Spinach”;
i=0;
while(i < myArray.length) {
 aa.print(myArray[i]);
 i = i + 1;
}

This example approximates what we need. We have an array and we want to loop over its elements. So lets try adding a rough version of this to our script:

myResult = aa.cap.getCapID(“01BLD”, “00000”, “00027”);
if(myResult.getSuccess()) {
 myCap = myResult.getOutput();
} else {
 aa.print(myResult.getErrorMessage());
 aa.abortScript();
}
myResult = aa.inspection.getInspections(myCap);

if(myResult.getSuccess()) {
 myInspections = myResult.getOutput();
} else {
 aa.print(myResult.getErrorMessage());
 aa.abortScript();
}
i=0;
while(i < myInspections.length) { 
 // At this point we need to get the inspection and do something with it to print. 
 i = i + 1;
} 

Now we have added five more lines to our script that execute a while loop one time for each element in the array. If we have three items in our array, the loop counter has the values 0, 1, and 2. When the loop counter reaches three, the loop stops repeating. Inside the loop, we have a comment as a placeholder for a print function.

Inside the loop, we retrieve the InspectionItem object from the array, that is at the position specified by the loop counter, and we use that object to print out the information. Add the line to retrieve the object:

myResult = aa.cap.getCapID(“01BLD”, “00000”, “00027”);
if(myResult.getSuccess()) {
 myCap = myResult.getOutput();
} else {
 aa.print(myResult.getErrorMessage());
 aa.abortScript();
}
myResult = aa.inspection.getInspections(myCap);
if(myResult.getSuccess()) {
 myInspections = myResult.getOutput();
} else {
 aa.print(myResult.getErrorMessage());
 aa.abortScript();
}
i=0;
while(i < myInspections.length) {
 theItem = myInspections[i]; 
 // At this point we need to use the object to print some stuff. 
 i = i + 1;
} 

After adding a line to the beginning of the loop we now have a variable that contains the InspectionItem object at the current position in the array. Now we just use that object to print out the inspection id number, type, and status. Here is the script:

myResult = aa.cap.getCapID(“01BLD”, “00000”, “00027”);
if(myResult.getSuccess()) {
 myCap = myResult.getOutput();
} else {
 aa.print(myResult.getErrorMessage());
 aa.abortScript();
}
myResult = aa.inspection.getInspections(myCap);
if(myResult.getSuccess()) {
 myInspections = myResult.getOutput();
} else {
 aa.print(myResult.getErrorMessage());
 aa.abortScript();
}
i=0;
while(i < myInspections.length) {
 theItem = myInspections[i];
 aa.print(theItem.getIdNumber());
 aa.print(theItem.getInspectionType());
 aa.print(theItem.getInspectionStatus() + “\n”);
 i = i + 1;
}

If you run this script with the right record id you receive an output that, depending on the inspections for the record and their status, looks something like this:

4238 
Trenches 
Scheduled 
4257 
Reinforcing 
Approved 
4293 
Foundation Wall 
Approved 

Now we have a useful script that retrieves some important information about a record. In our script we used three methods of the InspectionItem object: getIdNumber, getInspectionType, and getInspectionStatus. These methods do not take any arguments because their purpose is only to return information about the inspection to your script.

The tenth line of the script shows that we added the special character “\n” at the end of the value that the getInspection status method returned and passed the resulting string to the print method. This special character added an extra blank line in between each inspection’s printed values.

Up until this point, we have always used the print method to produce output from our script that we can see, but many of the scripts that you write for Civic Platform do not produce output in this way. You may want to modify a record’s workflow or automatically assess a fee when you schedule a new inspection. In other words, the output of your script may modify some data in Civic Platform.

As an example, we are going to check for a problem with the statuses of the inspections of our record, and if a problem exists, we are going to create a smart notice to let staff know. Let us suppose that we do not want to approve a Foundation Wall inspection before there is an approved Trenches inspection. If this scenario happens we want to create a smart notice that informs staff that the record with the record id we were checking has this problem.

So how do we approach this scenario? Well, first we need to know if there is an approved Foundation Wall inspection. If there is, then we need to know if there is an approved Trenches inspection. We already have a script that loops over all the inspections for a record. We can start from there, but instead of printing out information about the inspection, we want to see if the inspection is an approved Foundation Wall inspection. Let us add this check to the script:

myResult = aa.cap.getCapID(“01BLD”, “00000”, “00027”);
if(myResult.getSuccess()) {
 myCap = myResult.getOutput();
} else {
 aa.print(myResult.getErrorMessage());
 aa.abortScript();
}
myResult = aa.inspection.getInspections(myCap);
if(myResult.getSuccess()) {
 myInspections = myResult.getOutput();
} else {
 aa.print(myResult.getErrorMessage());
 aa.abortScript();
}
i=0;
while(i < myInspections.length) {
 theItem = myInspections[i];
 if(theItem.getInspectionType() == “Foundation Wall” &&
 theItem.getInspectionStatus() == “Approved”) { 
 //Check to see if there is an approved Trenches inspection. 
 } i = i + 1;
} 

When this script executes let us suppose that it finds an inspection that is a Foundation Wall inspection with a status of Approved. When this scenario happens we need to check to see if there is an approved Trenches inspection.

The check for a Trenches inspection requires that we use a second loop inside our main loop, but we can simplify things by using a function. Let us add a function that checks to see if there is a Trenches inspection that is Approved and a condition that uses our new function to do the checking:

Function checkForApprovedTrenchesInspection(inspectionItemArray) { 
 j = 0;
 while(j < myInspections.length) { 
 currentInspection = myInspections[j];
 if(currentInspection.getInspectionType() == “Trenches” && 
 currentInspection.getInspectionStatus() == “Approved”) { 
 return true;
 } 
 j = j + 1;
 } 
 return false;
}
myResult = aa.cap.getCapID(“01BLD”, “00000”, “00027”);
if(myResult.getSuccess()) {
 myCap = myResult.getOutput();
} else {
 aa.print(myResult.getErrorMessage());
 aa.abortScript();
}
myResult = aa.inspection.getInspections(myCap);
if(myResult.getSuccess()) {
 myInspections = myResult.getOutput();
} else {
 aa.print(myResult.getErrorMessage());
 aa.abortScript();
}
i=0;
while(i < myInspections.length) {
 theItem = myInspections[i];
 if(theItem.getInspectionType() == “Foundation Wall” &&
 theItem.getInspectionStatus() == “Approved”) {
if(checkForApprovedTrenchesInspection(myInspections) == false) { 
 // If we reach this line we have confirmed that the record has a problem. 
 }
 }
 i = i + 1;
} 

Our function looks very similar to the previously written part of our script. We have placed the function at the top of the script, although you could also put it at the bottom as a matter of preference. We have tried to give our function a meaningful name that tells us what it does. We could also put a comment before the function to explain to other people reading our script what the function does.

The function accepts one parameter, an inspection array. We named the parameter inspectionItemArray to remind ourselves of what type of value we need to pass in when calling the function. The function loops over the array passed in and checks to see if each of the inspections meets the condition that it is a Trenches inspection with Approved status. As soon as the function finds an inspection it returns the value true.

When a return statement appears in the middle of a function like this, the function stops what it is doing and immediately returns the specified value; it does not wait for the loop to finish. If the loop goes all the way through the inspections for the record and does not find a matching inspection, the loop exits and the next command after the loop executes. The command after the loop is “return false”, so if the function gets through the loop without finding a matching inspection, the function returns false.

The condition we added to the middle of the previously written loop uses the new function to check for an Approved Trenches inspection. If the function does not find an inspection, we know that the record has inspection statuses inconsistent with how we want to run our agency and we need to do something.

Let us suppose that the inspection matches the conditions we have set up so far. In this case, we need to replace the comment line in our script with a command to insert a smart notice with the information that we need. The smartNotice object has this method:

addNotice(String id1, String id2, String id3, String activityType, String activityComment) returns null

So when we confirm that the record has the problem, we need to call this method to create the new smart notice. After adding this method call to the script here is what we get:

function checkForApprovedTrenchesInspection(inspectionItemArray) {
 j = 0;
 while(j < myInspections.length) {
 currentInspection = myInspections[j];
 if(currentInspection.getInspectionType() == “Trenches” &&
 currentInspection.getInspectionStatus() == “Approved”) {
 return true;
 }
 j = j + 1;
 }
 return false;
}
myResult = aa.cap.getCapID(“01BLD”, “00000”, “00027”);
if(myResult.getSuccess()) {
 myCap = myResult.getOutput();
} else {
 aa.print(myResult.getErrorMessage());
 aa.abortScript();
}
myResult = aa.inspection.getInspections(myCap);
if(myResult.getSuccess()) {
 myInspections = myResult.getOutput();
} else {
 aa.print(myResult.getErrorMessage());
 aa.abortScript();
}
i=0;
while(i < myInspections.length) {
 theItem = myInspections[i];
 if(theItem.getInspectionType() == “Foundation Wall” &&
 theItem.getInspectionStatus() == “Approved”) {
if(checkForApprovedTrenchesInspection(myInspections) == false) {
 aa.smartNotice.addNotice(“01BLD”, “00000”, “00027”, “Inspection Problem”,
 “Application 01BLD 00000 00027 has an approved Foundation Wall inspection” +
 “but no approved Trenches inspection.”);
 }
 }
 i = i + 1;
}

So now, we have a script that checks a record to see if it meets a certain criteria, and takes action by inserting a new smart notice if the record does meet the criteria.

The list of Civic Platform events includes an event called InspectionResultSubmitAfter that is a good place to run our script and check the application. However, as our current script only checks the record 01BLD-00000-00027. We need to modify our script to that it uses the input parameters from the event to dynamically determine which application to check.

The documentation for the InspectionResultSubmitAfter event shows three parameters that can tell us which record to check:

IN: PermitId1
IN: PermitId2
IN: PermitId3

We need to use the getValue method of the env object to retrieve parameters passed in to our script from an event. We add the following three lines at the top of our script to retrieve the permit id values:

myId1 = aa.env.getValue(“PermitId1”);
myId2 = aa.env.getValue(“PermitId2”);
myId3 = aa.env.getValue(“PermitId3”);

After adding these lines at the beginning of the script, we use the three values we retrieved in place of the unchanging strings we passed as parameters to getCapID. We also use these values to dynamically create the message for the smart notice. Here is the script:

function checkForApprovedTrenchesInspection(inspectionItemArray) {
 j = 0;
 while(j < myInspections.length) {
 currentInspection = myInspections[j];
 if(currentInspection.getInspectionType() == “Trenches” &&
 currentInspection.getInspectionStatus() == “Approved”) {
 return true;
 }
 j = j + 1;
 }
 return false;
}
myId1 = aa.env.getValue(“PermitId1”);
myId2 = aa.env.getValue(“PermitId2”);
myId3 = aa.env.getValue(“PermitId3”);
myResult = aa.cap.getCapID(myId1, myId2, myId3);
if(myResult.getSuccess()) {
 myCap = myResult.getOutput();
} else {
 aa.print(myResult.getErrorMessage());
 aa.abortScript();
}
myResult = aa.inspection.getInspections(myCap);
if(myResult.getSuccess()) {
 myInspections = myResult.getOutput();
} else {
 aa.print(myResult.getErrorMessage());
 aa.abortScript();
}
i=0;
while(i < myInspections.length) {
 theItem = myInspections[i];
 if(theItem.getInspectionType() == “Foundation Wall” &&
 theItem.getInspectionStatus() == “Approved”) {
if(checkForApprovedTrenchesInspection(myInspections) == false) {
 aa.smartNotice.addNotice(myId1, myId2, myId3, “Inspection Problem”,
 “Application “ + myId1 + “ “ + myId2 + “ “ + myId3 +
 ” has an approved Foundation Wall inspection“ +
 “ but no approved Trenches inspection.”);
 }
 }
 i = i + 1;
}

You can now associate the script with the InspectionResultSubmitAfter event. However, there is one more thing to do.

Currently, we use aa.print to send messages to the user when something goes wrong. While aa.print works with the Script Test page, use the environment object to send messages back to the user when you attach the script to an event. Here is the final script with the aa.print statements, replaced with the appropriate statements for informing the user with a message:

function checkForApprovedTrenchesInspection(inspectionItemArray) {
 j = 0;
 while(j < myInspections.length) {
 currentInspection = myInspections[j];
 if(currentInspection.getInspectionType() == “Trenches” &&
 currentInspection.getInspectionStatus() == “Approved”) {
 return true;
 }
 j = j + 1;
 }
 return false;
}
myId1 = aa.env.getValue(“PermitId1”);
myId2 = aa.env.getValue(“PermitId2”);
myId3 = aa.env.getValue(“PermitId3”);
myResult = aa.cap.getCapID(myId1, myId2, myId3);
if(myResult.getSuccess()) {
 myCap = myResult.getOutput();
} else {
 aa.env.setValue(“ScriptReturnMessage”, myResult.getErrorMessage());
 aa.abortScript();
}
myResult = aa.inspection.getInspections(myCap);
if(myResult.getSuccess()) {
 myInspections = myResult.getOutput();
} else {
 aa.env.setValue(“”ScriptReturnMessage”, myResult.getErrorMessage());
 aa.abortScript();
}
i=0;
while(i < myInspections.length) {
 theItem = myInspections[i];
 if(theItem.getInspectionType() == “Foundation Wall” &&
 theItem.getInspectionStatus() == “Approved”) {
if(checkForApprovedTrenchesInspection(myInspections) == false) {
 aa.smartNotice.addNotice(myId1, myId2, myId3, “Inspection Problem”,
 “Application “ + myId1 + “ “ + myId2 + “ “ + myId3 +
 ” has an approved Foundation Wall inspection“ +
 “ but no approved Trenches inspection.”);
 }
 }
 i = i + 1;
}

Before you deploy a script like this to an environment where real users use Civic Platform, test the script thoroughly to make sure that it works as expected. Use a test environment where any mistakes do not affect your production data.

To test a script, go in to the Event Manager pages and associate the script with the event, then try to exercise different parts of the script. For example, try doing several different kinds of inspection results that do not insert a smart notice. Then try some inspection results that do insert a smart notice. With proper testing you can be much more sure that the script works, before you deploy it in your production environment.