(!******************************************************
Xpress Executor Example Model
=============================
file blendxe.mos
````````````````
Demonstrates executing the 'blend3c' model using Xpress Executor
with the supplied input data & displaying the results.
Uses mmhttp to call Xpress Executor REST webservice endpoints.
Assumes that you have an Xpress Executor component already configured
with the blend3c model.
(c) 2015-2019 Fair Isaac Corporation
author: J. Farmer, Jul. 2017
*******************************************************!)
model "Blend XE"
uses "mmhttp", "mmssl", "mmsystem", "mmxml"
parameters
! You should set the DMP_XE_REST_ENDPOINT, DMP_SOLUTION_CLIENT_ID and DMP_SOLUTION_SECRET parameters
! to point to the component you want to test.
! The REST endpoint of the Xpress Executor DMP component
! Obtain this by clicking "View Links" for the Xpress Executor component on the DMP UI
DMP_XE_REST_ENDPOINT=""
! The client ID of solution containing the Xpress Executor DMP component
! Obtain this through the DMP UI
DMP_SOLUTION_CLIENT_ID=""
! The secret of the solution containing the Xpress Executor DMP component
! Obtain this through the DMP UI
DMP_SOLUTION_SECRET=""
! The endpoint from which to request the authorization token.
! See DMP help page "Requesting a Bearerer Token" for details on how to obtain this
DMP_BEARER_TOKEN_URL="https://iam-svc.dms.usw2.ficoanalyticcloud.com/registration/rest/client/token"
! The input file for the remote model
INPUTFILE="../data/blend.csv"
! File into which to save the results
RESULTFILE="blendresults.csv"
end-parameters
declarations
! XML document containing final status of execution
executionStatus: xmldoc
! Array of parameters
modelParameterNames: set of string
modelParameters: dynamic array(modelParameterNames) of text
! Token used to authorise access to the Xpress Executor component
bearerToken: text
end-declarations
forward procedure authoriseDmpComponent
forward function startExecution(inputfile:text,modelParameters:array(modelParameterNames) of text) : xmldoc
forward procedure waitForCompletion(executionStatus:xmldoc)
forward procedure deleteExecution(executionStatus:xmldoc)
forward function getURLFromPath(path:text) : text
forward function getNodeValue(doc:xmldoc,path:text) : text
forward function getNodeValue(doc:xmldoc,path:string) : text
forward function fetchPathFromComponent(path:text,destfile:text) : integer
public declarations
! Temporary variables used in assembling HTTP requests
public httpPostBody: text
public httpResponseBody: text
public inputText: text
public runLog: text
end-declarations
! Configure Mosel to use TLS1.2 ciphers with HTTPS to allow us to talk to DMP
setparam("https_ciphers","TLSv1.2+HIGH:\!SSLv2:\!aNULL:\!eNULL:\!3DES:@STRENGTH")
! Log into DMP
writeln("Requesting authorization token from DMP")
authoriseDmpComponent
! Execute our model
writeln("Initiating model execution")
modelParameters("INPUTFILE"):="input" ! In Xpress Executor, the inputs we provide will be provided to the model in a file called "input"
modelParameters("RESULTFILE"):="result" ! In Xpress Executor, we can only access model results from a file called "result"
executionStatus := startExecution(INPUTFILE,modelParameters)
! Model will be executing asynchronously; wait for it to complete.
writeln("Waiting for completion of execution")
waitForCompletion(executionStatus)
! Check execution was successful
writeln("Processing model results")
! In event of failure, echo the remote model status, exit code & run log to aid with troubleshooting
if getNodeValue(executionStatus,"/jsv/status")<>"OK" or getNodeValue(executionStatus,"/jsv/exitCode")<>"0" then
writeln("Execution failed!")
writeln("Execution status: ", getNodeValue(executionStatus,"/jsv/status"))
writeln("Execution exit code: ", getNodeValue(executionStatus,"/jsv/exitCode"))
writeln
writeln("Execution log:")
! Fetch the remote execution log as it may contain error messages from the model
if fetchPathFromComponent( getNodeValue(executionStatus,"/jsv/runLogPath"), "text:runLog" )<>200 then
writeln("Execution log not available")
end-if
writeln(runLog)
exit(1)
end-if
! Download results file
if fetchPathFromComponent( getNodeValue(executionStatus,"/jsv/resultPath"), RESULTFILE )<>200 then
writeln("Model results are not available!")
exit(1)
end-if
! Parse results and display to user
declarations
ORES=1..2
use: array(ORES) of real
end-declarations
initializations from "mmsheet.csv:"+RESULTFILE
use as "[A1:B2]"
end-initializations
writeln
writeln("Solution:")
forall(o in ORES) writeln(" use(",o,"): ",use(o))
! Delete execution from component
deleteExecution( executionStatus )
! Use the client ID and secret from the solution to request a token that we can use to
! authorise access to the Xpress Executor component
procedure authoriseDmpComponent
declarations
payloadDoc: xmldoc
responseCode: integer
end-declarations
! Assemble payload to send to DMP
reset(payloadDoc)
rootElem := addnode(payloadDoc, 0, XML_ELT, "jsv")
setattr(payloadDoc, rootElem, "jst", "obj")
clientIdElem := addnode(payloadDoc, rootElem, XML_ELT, "clientId")
setattr(payloadDoc, clientIdElem, "jst", "str")
setvalue(payloadDoc, clientIdElem, DMP_SOLUTION_CLIENT_ID)
secretElem := addnode(payloadDoc, rootElem, XML_ELT, "secret")
setattr(payloadDoc, secretElem, "jst", "str")
setvalue(payloadDoc, secretElem, DMP_SOLUTION_SECRET)
! Post to server
jsonsave(payloadDoc, "text:httpPostBody")
reset(payloadDoc)
responseCode := httppost( DMP_BEARER_TOKEN_URL, "text:httpPostBody", "text:httpResponseBody", "Content-Type: application/json" );
reset(httpPostBody)
! Check response
if responseCode<>200 then
writeln("ERROR: Failed to authorise with DMP (HTTP response: "+responseCode+")")
writeln(httpResponseBody)
exit(1)
end-if
! Store bearer token
bearerToken := httpResponseBody
reset(httpResponseBody)
end-procedure
! Start execution of model, using the given file of input data. Returns initial execution status
function startExecution(inputfile:text,modelParameters:array(modelParameterNames) of text) : xmldoc
declarations
payloadDoc: xmldoc
responseDoc: xmldoc
responseCode: integer
headers: text
end-declarations
! Assemble payload to send to DMP
reset(payloadDoc)
rootElem := addnode(payloadDoc, 0, XML_ELT, "jsv")
setattr(payloadDoc, rootElem, "jst", "obj")
! Set parameters
paramsElem := addnode(payloadDoc, rootElem, XML_ELT, "parameters")
setattr(payloadDoc, paramsElem, "jst", "obj")
forall( paramName in modelParameterNames | exists(modelParameters(paramName)) ) do
paramElem := addnode(payloadDoc, paramsElem, XML_ELT, "jsv")
setattr(payloadDoc, paramElem, "name", paramName)
setattr(payloadDoc, paramElem, "jst", "str")
setvalue(payloadDoc, paramElem, modelParameters(paramName))
end-do
! Set input data (use inputText for textual input, inputBase64 for binary input (after encoding it)
fcopy( inputfile, "text:inputText" )
if getsysstat<>0 then
writeln("Error reading ",inputfile)
exit(1)
end-if
inputDataElem := addnode(payloadDoc, rootElem, XML_ELT, "inputText")
setattr(payloadDoc, inputDataElem, "jst", "str")
setvalue(payloadDoc, inputDataElem, inputText )
reset(inputText)
! Assemble HTTP headers
headers += "Content-Type: application/json\n"
headers += "Accepts: application/json\n"
headers += "Authorization: Bearer "+bearerToken+"\n"
! Post to server
jsonsave(payloadDoc, "mmsystem.text:httpPostBody")
reset(payloadDoc)
responseCode := httppost( DMP_XE_REST_ENDPOINT, "text:httpPostBody", "text:httpResponseBody", headers );
reset(httpPostBody)
! Check response
if responseCode<>200 then
writeln("ERROR: Failed to start model execution (HTTP response: "+responseCode+")")
writeln(httpResponseBody)
exit(1)
end-if
! Parse response JSON
jsonload(responseDoc, "text:httpResponseBody", 0)
reset(httpResponseBody)
returned := responseDoc
end-function
! Return URL of given path on Xpress Executor component
function getURLFromPath(path:text) : text
declarations
url: text
end-declarations
! Strip the path off the endpoint URL
url := DMP_XE_REST_ENDPOINT
lastSlash := 0
forall (i in 1..3) do
nextSlash := findtext(url,"/",lastSlash+1)
if nextSlash=0 then
writeln("Failed to parse URL!")
exit(1)
end-if
lastSlash := nextSlash
end-do
deltext(url, lastSlash, getsize(url))
! Append given path
url += path
! And done
returned := url
end-function
! Return content of given XPATH expression in given document. Aborts model if was not found.
function getNodeValue(doc:xmldoc,path:string) : text
declarations
nodeId: integer
end-declarations
nodeId := getnode(doc,path)
if nodeId=-1 then
writeln("Failed to find node '",path,"' in document")
exit(1)
end-if
returned := getvalue(doc,nodeId)
end-function
function getNodeValue(doc:xmldoc,path:text) : text
returned := getNodeValue(doc,string(path))
end-function
! Fetch the given path from the Xpress Executor component and save content to the given destination file. Returns response code.
function fetchPathFromComponent(path:text,destfile:text) : integer
declarations
responseCode: integer
headers: text
url: text
end-declarations
reset(headers)
headers += "Accepts: application/json\n"
headers += "Authorization: Bearer "+bearerToken+"\n"
url := getURLFromPath( path )
responseCode := httpget( url, string(destfile), headers )
returned := responseCode
end-function
! Wait for model execution to complete; updates the supplied execution status document
procedure waitForCompletion(executionStatus: xmldoc)
declarations
responseCode: integer
end-declarations
! Loop while status indicates execution not completed
status := getNodeValue(executionStatus,"/jsv/status")
while (status="NOT_COMPLETED" or status="NOT_LOADED") do
! Wait for half a second
sleep(500)
! Refresh status
responseCode := fetchPathFromComponent( getNodeValue(executionStatus,"/jsv/statusPath"), "text:httpResponseBody" )
if responseCode<>200 then
writeln("ERROR: Encountered HTTP error code ",responseCode," when querying execution status")
writeln(httpResponseBody) ! Write out response body, in case it contains an error message.
exit(1)
end-if
jsonload(executionStatus, "text:httpResponseBody", 0)
reset(httpResponseBody)
status := getNodeValue(executionStatus,"/jsv/status")
end-do
end-procedure
! Delete the execution from the service
procedure deleteExecution(executionStatus: xmldoc)
declarations
responseCode: integer
headers: text
end-declarations
reset(headers)
headers += "Accepts: application/json\n"
headers += "Authorization: Bearer "+bearerToken+"\n"
responseCode := httpdel( getURLFromPath(getNodeValue(executionStatus,"/jsv/statusPath")), "null:", headers )
if responseCode<>200 then
writeln("Warning: Received HTTP response ",responseCode," when deleting execution")
end-if
end-procedure
end-model
|