Part 2: Error Handling (and anything else that can be improved)
(Part 1 focused on general cleanup.)
I received this comment in an unrelated post (thanks @Milton):
...Since it is obvious you are using a GIS endpoint, I would note that a lot can go wrong with your script if the GIS were to go down, move url, request gets lost, etc. I discourage url links to GIS data...
I think this concern is legitimate. If this script produces an error, then the user will not be able to save their work orders in the work order management system (Maximo). This would render the entire system unusable until the problem is fixed.
To address this concern, I've put a generic try, except
error handler in the main part of the script. Is this good enough, or should it be improved?
The Script:
#What the script does:
# 1. Takes the X&Y coordinates of a work order in Maximo
# 2. Generates a URL from the coordinates
# 3. Executes the URL via a separate script/library (LIB_HTTPCLIENT)
# 4. Performs a spatial query in an ESRI REST feature service (a separate GIS system)
# 5. JSON text is returned to Maximo with the attributes of the zone that the work
# order intersected
# 6. The zone number is parsed from the JSON text via a separate script/library
# (LIB_PARSE_JSON)
# 7. Inserts the zone number into the work order record
#
#Notes about libraries:
# - Unfortunately, I'm unable to add external Python libraries (like urllib or
# urlparse) to my Maximo/Jython implementation.
# - Furthermore, some libraries (exampple: JSON) that are normally included in Python
# and Jython have been excluded from my Maximo implementation. I don't have control
# over this.
# - Instead, if there is functionality missing from my Jython implementation, I need
# to use Java classes to fill in the gap:
# https://www.ibm.com/support/knowledgecenter/ja/SSEUEX_2.0.2/com.ibm.javaeuc.doc/com/ibm/json/java/package-summary.html
#
#Notes about creating the URL:
# - Previous code reviewers have strongly suggested "...splitting the URL creation
# into different variables and adding a looping function that adds query parameters
# to the URL."
# - I've tried doing this, but I found that it made the url parts harder for me to
# manage, not easier. Maybe I'm nuts, but I've tried it, and figured it was overly
# complicated, so I think we can skip this idea for now. Thanks all the same.
from psdi.mbo import MboConstants
from java.util import HashMap
field_to_update = "ZONE"
def get_coords():
"""
Get the y and x coordinates(UTM projection) from the WOSERVICEADDRESS table
via the SERVICEADDRESS system relationship.
The datatype of the LatitdeY and LongitudeX fields is decimal.
"""
laty = mbo.getDouble("SERVICEADDRESS.LatitudeY")
longx = mbo.getDouble("SERVICEADDRESS.LongitudeX")
return laty, longx
def is_latlong_valid(laty, longx):
#Verify if the numbers are legitimate UTM coordinates
return (4000000 <= laty <= 5000000 and
600000 <= longx <= 700000)
def make_url(laty, longx):
"""
Assemble the URL (including the longx and the laty).
Note: The coordinates are flipped in the url.
Consider replacing the field wildcard(*) with the specific field name (zone)
"""
url="http://example.com/arcgis/rest/services/Something/Zones/MapServer/15/query?geometry={0}%2C{1}&geometryType=esriGeometryPoint&spatialRel=esriSpatialRelIntersects&outFields=*&returnGeometry=false&f=pjson".format(str(longx),str(laty))
return url
def fetch_zone(url):
# Get the JSON text from the feature service (the JSON text contains the zone value).
ctx = HashMap()
ctx.put("url", url)
service.invokeScript("LIB_HTTPCLIENT", ctx)
json_text = str(ctx.get("response"))
# Parse the zone value from the JSON text
ctx = HashMap()
ctx.put("json_text", json_text)
service.invokeScript("LIB_PARSE_JSON", ctx)
parsed_val = str(ctx.get("parsed_val"))
return parsed_val
#Is this sort of generic error checking acceptable (try/except)?
try:
laty, longx = get_coords()
if not is_latlong_valid(laty, longx):
service.log('Invalid coordinates')
else:
url = make_url(laty, longx)
zone = fetch_zone(url)
#Insert the zone value into the zone field in the work order
mbo.setValue(field_to_update, zone, MboConstants.NOACCESSCHECK)
except:
"""
If the script fails, then set the field vaule to null.
Reason: If the work order coordinates have changed (thereby triggering this script),
then setting the zone's field value to null is better than leaving it as the wrong
zone number (if the coordinates of the work order changed, that means
that the zone number likely changed too).
Furthermore, if there is an error in the script, then an error message will pop up
and prevent users from creating/saving work orders.
We don't want that! (error messages would render the system unusable until fixed)
So, I figure, I'll set the field value to null if there is an error.
"""
mbo.setValue(field_to_update, "", MboConstants.NOACCESSCHECK) #Should I
# set the field value to "" or to None?
service.log("An exception occurred")
LIB_HTTPCLIENT:
from psdi.iface.router import HTTPHandler
from java.util import HashMap
from java.lang import String
handler = HTTPHandler()
map = HashMap()
map.put("URL", url)
map.put("HTTPMETHOD", "GET")
responseBytes = handler.invoke(map, None)
response = String(responseBytes, "utf-8")
LIB_PARSE_JSON:
#Is there a way to NOT hardcode the field name (ZONE)?
from com.ibm.json.java import JSONObject
obj = JSONObject.parse(json_text)
#The "features" element of the JSON object has an array. Get the first feature in the
#array by specifying [0].
parsed_val = obj.get("features")[0].get("attributes").get("ZONE")