6
\$\begingroup\$

Part 1: General Cleanup

(Edit: Part 2 is here.)


I have a script in a Work Order Management System (Maximo) that performs a spatial query .

Details:

  1. Takes the X&Y coordinates of a work order in Maximo
  2. Generates a URL from the coordinates (including other spatial query information)
  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

Jython main automation script

from psdi.mbo import MboConstants
from java.util import HashMap
#Get the y and x coordinates(UTM projection) from the WOSERVICEADDRESS table via the SERVICEADDRESS system relationship
#The datatype of the LatitydeY and LongitudeX is decimal: 1234567.8335815760, 123456.4075621164
#Convert the decimals to integers, and then strings, for the purpose of generating the URL (I don't think the URL can have decimal places)
laty = str(mbo.getInt("SERVICEADDRESS.LatitudeY"))
longx = str(mbo.getInt("SERVICEADDRESS.LONGITUDEX"))
#Verify if the numbers are legitimate UTM coordinates
if len(laty) == 7 and len(longx) == 6:
 #Assemble the URL (including the longx and the laty). Note: The coordinates are flipped in the url
 url="http://something.com/arcgis/rest/services/something/Zones/MapServer/15/query?geometry=" + longx + "%2C" + laty + "&geometryType=esriGeometryPoint&spatialRel=esriSpatialRelIntersects&outFields=*&returnGeometry=false&f=pjson"
 #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"))
 #Enter the zone value into the zone field in the work order
 mbo.setValue("DESCRIPTION","Waste Zone: "+parsed_val,MboConstants.NOACCESSCHECK)

Jython library (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")

JavaScript library (LIB_PARSE_JSON)

#The field name (ZONE) is hardcoded. I'm not sure if this is best practice or not.
var obj = JSON.parse(json_text);
parsed_val = obj.features[0].attributes.ZONE

How can this code be improved?

asked Aug 19, 2019 at 23:17
\$\endgroup\$
1
  • \$\begingroup\$ Jython... I know nothing about Maximo/PSDI but it already makes me sad. Are you able to use "real Python libraries" like Requests? \$\endgroup\$ Commented Aug 20, 2019 at 1:04

1 Answer 1

5
\$\begingroup\$

Functions are your friend

Even in Jython, functions are a valid construct. They allow for scope, so that you can better reason about temporary variables; give better stack traces when things go wrong; increase testability; etc. So you should move your code into some functions.

Invert your logic

This:

if len(laty) == 7 and len(longx) == 6:

should probably be inverted, and something done about it, i.e.

if len(laty) != 7 || len(longx) != 6:
 # throw, or at least print...
# continue on with the rest of the function

If you had requests...

then this:

url="http://something.com/arcgis/rest/services/something/Zones/MapServer/15/query?geometry=" + longx + "%2C" + laty + "&geometryType=esriGeometryPoint&spatialRel=esriSpatialRelIntersects&outFields=*&returnGeometry=false&f=pjson"

could be fairly reduced in insanity. The query params can be formed as a dictionary passed to the get method.

PEP8

Use any kind of linter or modern IDE, and it will give you suggestions on how to reformat this thing to follow Python formatting standards (PEP8). The most obvious thing it will notice is lack of spaces after commas.

Otherwise - perhaps you should provide some context about why this thing exists in Jython.

Suggested

I don't know whether this will work, because I don't have your setup; so you'll probably have to tweak it.

from psdi.mbo import MboConstants
from java.util import HashMap
from urllib import urlencode
from urlparse import urlunparse, ParseResult
def get_coords():
 """
 Get the y and x coordinates(UTM projection) from the WOSERVICEADDRESS table
 via the SERVICEADDRESS system relationship.
 The datatype of the LatitydeY and LongitudeX is decimal, i.e.
 1234567.8335815760, 123456.4075621164.
 """
 laty = mbo.getDouble("SERVICEADDRESS.LatitudeY")
 longx = mbo.getDouble("SERVICEADDRESS.LONGITUDEX")
 return laty, longx
def is_valid(laty, longx):
 """
 Verify if the numbers are legitimate UTM coordinates
 """
 return (0 <= laty <= 10e6 and
 167e3 <= longx <= 833e3)
def make_url(laty, longx):
 """
 Assemble the URL (including the longx and the laty). Note: The coordinates
 are flipped in the url
 """
 query = {
 'geometry': '%d,%d' % (laty, longx),
 'geometryType': 'esriGeometryPoint',
 'spatialRel': 'esriSpatialRelIntersects',
 'outFields': '*', # You should narrow this if you only care about work zone.
 'returnGeometry': 'false',
 'f': 'pjson'
 }
 parts = ParseResult(scheme='http',
 netloc='something.com',
 path='/arcgis/rest/services/something/Zones/MapServer'
 '/15/query',
 query=urlencode(query),
 fragment='')
 url = urlunparse(parts)
 return url
def fetch_waste_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
def main():
 laty, longx = get_coords()
 if not is_valid(laty, longx):
 print('Invalid coordinates')
 return
 url = make_url(laty, longx)
 waste_zone = fetch_waste_zone(url)
 # Enter the zone value into the zone field in the work order
 mbo.setValue("DESCRIPTION", "Waste Zone: " + waste_zone,
 MboConstants.NOACCESSCHECK)
main()

Note:

  • There are functions
  • The functions contain """docstrings""" at the top
  • You should preserve the UTM coordinates as doubles until it's necessary to format them as integers in the URL string
  • UTM coordinates have real limits that you should use - not just string length
  • Even if you don't have requests, you can still do somewhat saner query string formation via dictionary
  • If the coordinates are invalid, do something - maybe print, definitely quit
answered Aug 20, 2019 at 1:13
\$\endgroup\$
8
  • \$\begingroup\$ Good question! Rather than you, the developer, having to write out the long-form URL manually - including ampersands, escape characters if needed, etc., you compose a logic-friendly dictionary. This is more maintainable and less prone to error. \$\endgroup\$ Commented Aug 20, 2019 at 1:43
  • \$\begingroup\$ Fine; but that doesn't mean that you can't write sub-routines (which you still should). Subroutines can still use global variables when needed. \$\endgroup\$ Commented Aug 20, 2019 at 1:48
  • 4
    \$\begingroup\$ It's not frowned upon, per se, but it's also not doing you any favours. I seem to remember Jython being stuck in a very old version of Python with fairly poor compatibility to external Python libraries, coupled with awkward access to Java constructs. As a small scripting language embedded in Java it's fine, but you're better to either go full Java or full Python, if you can. \$\endgroup\$ Commented Aug 20, 2019 at 1:56
  • \$\begingroup\$ I'll post an example. \$\endgroup\$ Commented Aug 20, 2019 at 12:05
  • \$\begingroup\$ @User1973 edited \$\endgroup\$ Commented Aug 20, 2019 at 12:35

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.