Initializing help system before first use

Solving a Mosel model using a REST webservice, from NodeJS


Description: In this example, the Xpress Executor should first be configured with the blend3c.mos model. Then, you run the blendxe.js program locally; this uses REST webservice requests to send the blend.csv file to the Xpress Executor and remotely solve the model using this, then downloads and displays the results. This example requires a local installation of Xpress for compiling the initial model, but not for starting the execution.
File(s): blendxe.js, blend3c.mos
Data file(s): blend.csv, package.json


blendxe.js
/*******************************************************
  Xpress Executor Example Model
  =============================

  file blendxe.js
  ```````````````
  Demonstrates executing the 'blend3c' model using Xpress Executor
  with the supplied input data & displaying the results.  This example is written
  in JavaScript and intended to be run using node.js.

  Local prerequisites:
    node.js
    npm

  Instructions
    1) Configure your Xpress Executor component with the blend3c example model
    2) Fill in the DMP_* variables with the details of your Xpress Executor component
    3) Open a command prompt and type:   npm install
    4) When that completes, type:        node blendxe.js

  (c) 2017 Fair Isaac Corporation
  author: J. Farmer, Jul. 2017
*******************************************************/

// The REST endpoint of the Xpress Executor DMP component
// ! Obtain this by clicking "View Links" for the Xpress Executor component on the DMP UI
var DMP_XE_REST_ENDPOINT="";

// The client ID of solution containing the Xpress Executor DMP component
// Obtain this through the DMP UI
var DMP_SOLUTION_CLIENT_ID="";

// The secret of the solution containing the Xpress Executor DMP component
// Obtain this through the DMP UI
var 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
var DMP_BEARER_TOKEN_URL="https://iam-svc.dms.usw2.ficoanalyticcloud.com/registration/rest/client/token"

// The input file for the remote model
var INPUTFILE="../data/blend.csv";



// Third-party dependency: prequest
// for promise-based HTTP requests
var prequest = require('prequest');

// Third-party dependency: delay
// for promise-based delays
var delay = require('delay');

// Third-party dependency: csv-parse
// for parsing CSV data
// The example model blend3c.mos reads & outputs CSV; this is a feature of this example rather than a requirement
// of Xpress Executor.
var parse = require('csv-parse/lib/sync');


// Standard node.js filesystem module
var fs = require('fs');
// Standard node.js URL handling module
var url = require('url');

// Request an authentication token from DMP
console.log("Requesting authorization token from DMP");
var authorizationToken;
prequest({
    method: 'POST',
    url: DMP_BEARER_TOKEN_URL,
    body: {
        clientId: DMP_SOLUTION_CLIENT_ID,
        secret: DMP_SOLUTION_SECRET
    }
}).then(function(body) {
    // This token can be re-used in subsequent requests, but should be refreshed every half hour
    authorizationToken = body;

    // Start the execution of the model in our Xpress Executor service
    console.log("Initiating model execution");
    return prequest({
        method: 'POST',
        url: DMP_XE_REST_ENDPOINT,
        headers: {
            "Authorization": 'Bearer '+authorizationToken
        },
        body: {
            // parameters defined in 'parameters' section at the top of blend3c.mos
            parameters: {
                INPUTFILE: "input",
                RESULTFILE: "result"
            },
            // Use inputText for passing input data as a string, inputBase64 for encoded binary input data
            inputText: fs.readFileSync(INPUTFILE,"utf8")
        }
    });
}).then(function(executionStatus) {
    // executionStatus is a standard structure that contains various meta-data about an execution in
    // the Xpress Executor service.  It also contains relative paths to various REST resources
    // relating to this execution - e.g. input, result, status, run log...

    // Model will be executing asynchronously; repeatedly wait 1/4 second then re-fetch status until it
    // is complete
    console.log("Waiting for completion of execution");
    function waitForCompletion() {
        if (executionStatus.status!=='NOT_COMPLETED' && executionStatus.status!=='NOT_LOADED') {
            // Execution has finishd!
            return Promise.resolve(executionStatus);
        }
        else {
            // Wait 250ms
            return delay(250).then(function() {
                // Refresh executionInfo
                return prequest({
                    method: 'GET',
                    url: url.resolve(DMP_XE_REST_ENDPOINT, executionStatus.statusPath),
                    headers: {
                        "Authorization": 'Bearer '+authorizationToken
                    }
                }).then(function(body) {
                    // Request returns updated executionStatus
                    executionStatus = body;
                    return waitForCompletion();
                });
            });
        }
    }
    return waitForCompletion();
}).then(function(executionStatus) {

    // Execution has completed; check that it was successful and display results as appropriate
    console.log("Processing model results");

    // In event of failure, echo the remote model status, exit code & run log to aid with troubleshooting
    if (executionStatus.status!=='OK' || executionStatus.exitCode!==0) {
        // Execution failed for some reason
        console.log("Execution failed!");
        console.log("Execution status: "+executionStatus.status);
        console.log("Execution exit code: "+executionStatus.exitCode);
        console.log("");
        console.log("Execution log:");
        // Fetch the remote execution log as it will likely contain error messages from the model
        return prequest({
            method: 'GET',
            url: url.resolve(DMP_XE_REST_ENDPOINT, executionStatus.runLogPath),
            headers: {
                "Authorization": 'Bearer '+authorizationToken,
                "Accept": 'text/plain'
            }
        }).then(function(runLog) {
            console.log(runLog);
            return executionStatus;
        });
    }

    else {
        // Download results file
        return prequest({
            method: 'GET',
            url: url.resolve(DMP_XE_REST_ENDPOINT, executionStatus.resultPath),
            headers: {
                "Authorization": 'Bearer '+authorizationToken,
                "Accept": 'application/octet-stream'
            }
        }).then(function(csvResults) {

            // The blend3c.mos example downloads its results as a CSV file.  Use the NodeJS 'csv' module to parse this.
            console.log();
            console.log("Results of optimization:");
            records = parse(csvResults);
            records.forEach(function(record) {
                console.log("  use("+record[0]+"): "+record[1]);
            });
            console.log();

            return executionStatus;
        });
    }

}).then(function(executionStatus) {
    // Finally, delete execution from component, to free the resources it holds
    console.log("Deleting execution from component");
    return prequest({
        method: 'DELETE',
        url: url.resolve(DMP_XE_REST_ENDPOINT, executionStatus.statusPath),
        headers: {
            "Authorization": 'Bearer '+authorizationToken
        }
    });

}).catch(function(err) {
    if (err.statusCode) {
        console.error("ERROR returned by Xpress Executor service: HTTP status code "+err.statusCode);
    }
    else {
        console.error("ERROR encountered: "+err.message);
    }
});

blend3c.mos
(!******************************************************
   Mosel User Guide Example Problems
   ================================= 

   file blend3c.mos
   ````````````````
   Reading data from an Excel spreadsheet
   - using the CSV driver -
   
   (c) 2013 Fair Isaac Corporation
       author: S. Heipcke, Jan. 2013
*******************************************************!)

model "Blend 3" 
 uses  "mmxprs", "mmsheet" 

 parameters
  INPUTFILE="../data/blend.csv"
  RESULTFILE="result.csv"
 end-parameters

 declarations
  REV = 125                      ! Unit revenue of product
  MINGRADE = 4                   ! Minimum permitted grade of product
  MAXGRADE = 5                   ! Maximum permitted grade of product
  ORES = 1..2                    ! Range of ores

  COST: array(ORES) of real      ! Unit cost of ores
  AVAIL: array(ORES) of real     ! Availability of ores
  GRADE: array(ORES) of real     ! Grade of ores (measured per unit of mass)

  use: array(ORES) of mpvar      ! Quantities of ores used
 end-declarations

!*** Read data from spreadsheet blend.csv ***
! Spreadsheet range contains data only (no header line):
 initializations from "mmsheet.csv:"+INPUTFILE
  [COST,AVAIL,GRADE] as "[B3:E4]"    ! or: "[R3C2:R4C5]"
 end-initializations

(! Or (spreadsheet range includes a header line as with ODBC):
 initializations from "mmsheet.csv:"+INPUTFILE
  [COST,AVAIL,GRADE] as "skiph;[B2:E4]" 
 end-initializations
!)

! Objective: maximize total profit
 Profit:= sum(o in ORES) (REV-COST(o))* use(o)

! Lower and upper bounds on ore quality
 sum(o in ORES) (GRADE(o)-MINGRADE)*use(o) >= 0
 sum(o in ORES) (MAXGRADE-GRADE(o))*use(o) >= 0

! Set upper bounds on variables
 forall(o in ORES) use(o) <= AVAIL(o)

 maximize(Profit)                 ! Solve the LP-problem

 ! Print out the solution
 writeln("Solution:\n Objective: ", getobjval)
 forall(o in ORES)  writeln(" use(" + o + "): ", getsol(use(o)))

 ! Write solution to CSV file
 declarations
  solvalues: array(ORES) of real
 end-declarations
 forall (o in ORES) solvalues(o) := getsol(use(o))
 initializations to "mmsheet.csv:"+RESULTFILE
  solvalues as "grow;[A1:B1]"
 end-initializations

end-model

© 2001-2019 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.