This commit is contained in:
Sean Wells 2014-10-14 21:44:35 -07:00
Коммит ef30788382
20 изменённых файлов: 1264 добавлений и 0 удалений

3
.bowerrc Normal file
Просмотреть файл

@ -0,0 +1,3 @@
{
"directory": "/client/app/bower_components"
}

6
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,6 @@
node_modules/
client/app/bower_components/
client/app/js/bundled.js
npm-debug.log
.DS_Store
dist

Двоичные данные
.modules/deployr-v7.3.0.zip Normal file

Двоичный файл не отображается.

Двоичные данные
.modules/rbroker-v7.3.0.zip Normal file

Двоичный файл не отображается.

8
LICENSE.md Normal file
Просмотреть файл

@ -0,0 +1,8 @@
Copyright (C) 2010-2014 by Revolution Analytics Inc.
This program is licensed to you under the terms of Version 2.0 of the
Apache License. This program is distributed WITHOUT
ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
details.

306
README.md Normal file
Просмотреть файл

@ -0,0 +1,306 @@
js-example-fraud-score
======================
Example use of DeployR as a real-time, R analytics scoring engine.
## Example Quick Start
By default, the example assumes an instance of the **DeployR server** is running
on `localhost`. If your instance of DeployR is running at some other IP address
then please udpate the `host` location property in the configuration file
`config/config.json`.
1. Download and install [Node.js](http://nodejs.org/download/), which ships with
npm.
2. Clone the repository:
```
$ git clone https://github.com/deployr/js-example-fraud-score.git
$ cd js-example-fraud-score
```
3. Install the global dependencies:
```
$ npm install -g gulp bower browserify
```
You might need `sudo` for globally installed packages, in that case:
```
$ sudo npm install -g gulp bower browserify
```
4. Install the local dependencies:
```
$ npm install
```
5. Set the DeployR server `host` location *if* not running at `localhost` in the
configuration file `config/config.json`
6. Run locally:
```
$ gulp
```
7. Point your browser to `http://localhost:9080`
## About
This example demonstrates the use of
[DeployR](http://deployr.revolutionanalytics.com) as a real-time, R analytics
scoring engine.
The example scenario mimics a real world application where employees at a
fictitious bank can request _fraud scores_ for one or more bank account records
to help detect fraudulent account activity.
This example is built using the _DeployR_ [RBroker
Framework](http://deployr.revolutionanalytics.com/dev), the simplest way to
integrate R analytics inside any Java, JavaScript or .NET application.
This example consists of three distinct parts:
1. Example R Analytics
2. Example Server Application
3. Example Client Application
The final section of this document provides additional details regarding the
_DeployR integration_ implemented for this example.
## Example R Analytics
```
Source: analytics/*
```
This example uses an R model built to score fictitious bank account data to help
uncover fraudulent account activity. The model used is found here:
```
analytics/fraudModel.rData
```
The model uses three variables associated with individual bank accounts:
1. The number of transactions on the account
2. The account balance
3. The credit line on the account
This example makes use of a scoring function that uses the model to help
determine the likelihood of _fraud_ on a given account based on these
data inputs. The scoring function is found here:
```
analytics/ccFraudScore.R
```
The R scripts and data models used by this example application are
bundled by default within the DeployR 7.3 repository, inside the
example-fraud-score directory owned by testuser.
However, if for any reason your DeployR repository does not contain
these files you can add them using the DeployR Repository Manager as
follows:
1. Log in as testuser into the Repository Manager
2. Create a new repository directory called example-fraud-score
3. Upload analytics/fraudModel.rData to the example-fraud-score
directory
4. Upload analytics/ccFraudScore.R to the example-fraud-score directory
## Example Server Application
Source:
```
server.js
server/**/*
```
The example server application represents a real world application, capable of
processing client requests for _fraud scores_ on one or more bank account
records.
The example server application is implemented as a simple
[Express](http://expressjs.com/) Node.js Web application.
The example server application exposes a simple REST API through which client
applications can submit _fraud scoring_ requests:
```
/fraud/score/{num_of_records}
```
The example server application handles client requests internally using the
[RBroker Framework](http://deployr.revolutionanalytics.com/dev) to run
the required R analytics that _scores_ each requested bank account record.
The _score_, generated by the R analytics and returned to the server via
the _RBroker Framework_, is then pushed by the server application, using
[WebSockets](https://www.npmjs.org/package/ws) through an abstraction layer for
real-time communication called [Primus](https://github.com/primus/primus) to
client applications that have subscribed as listeners on:
```
/fraudengine/topic/fraud
```
## Example Client Application
```
Source: client/app/*
```
The example client application simulates a computer system made available to
employees at a fictitious bank.
The example client application is implemented as a simple, single page
[AngularJS](https://angularjs.org) application. The single page is divided into
two distinct sections:
1. RBroker Runtime Window
This window gives the user a live view of all activity that
is taking place through the _RBroker Framework_ on behalf of the client
application. The runtime data provided indicates among other things the
live throughput performance being delivered by _DeployR_ behaving as a
real-time R analytics scoring engine.
Note: The details presented in the RBroker Runtime Window would not
typically appear in a real-world client application. These data are presented
here simply to aid developers understand by observation how an
_RBroker Framework_integration works.
2. Bank Account Fraud Score Result Window
This window displays the _fraud score_ results generated by the
real-time R analytics scoring engine powered by _DeployR_. The end user
can also use the _Execute_ button in this window to launch one or more
_fraud scoring_ requests.
The data for example bank account records is randomly generated by the
application. Requests are submitted by the client application through
the server application REST API.
The _score_ generated per request is returned to the client application within a
JSON message delivered over a WebSocket channel.
## Running the Example
A [gulp.js](http://gulpjs.com/) build script is provided to run the example:
```
gulpfile.js
```
By default, the build configuration assumes an instance of the DeployR server
is running on `localhost`. If your instance of DeployR is running at some
other IP address then please update the `host` property in the configuration
file `config/config.json` as appropriate:
```
{
"host": "http://localhost:7300",
"port": "9080",
"credentials": {
"username": "testuser",
"password": "changeme"
},
"logging": false,
"constants" : {
"REPO_OWNER": "testuser",
"REPO_SCRIPT": "ccFraudScore.R",
"REPO_DIRECTORY": "example-fraud-score",
"FRAUD_MODEL": "fraudModel.rData"
}
}
```
To run this example application:
```
$ gulp
```
Observe the console output in your terminal window to determine if the server
application has started successfully. Once started, open the client application
in your Web browser:
```
http://localhost:9080
```
## Multiple Users Running the Example
By default, the example build configuration defaults to using *testuser* account
credentials when authenticating with the DeployR server. If two or more users
intend running this example application at the same time against the same
DeployR server instance then:
1. Each user must update the `username` property in their `config/config.json`
configuration file, each indicating the *username* of their own user account.
2. The _Access Control_ setting for each of the R analytics file dependencies in
the DeployR repository must be changed from _Private_ access to _Shared_ access.
## DeployR Integration Details
#### R Analytics Dependencies
DeployR-powered applications typically depend on repository-managed R analytics
scripts, models and/or data files. See the _DeployR_ [Repository Manager](http://deployr.revolutionanalytics.com/documents/help/repo-man/) for details on how best to manage your own R analytics dependencies.
This example depends on two repository-managed files:
1. /testuser/example-fraud-score/fraudModel.rData
2. /testuser/example-fraud-score/ccFraudScore.R
Both files, an R model and scoring function respectively, are owned by
_testuser_ and can be found in the _example-fraud-score_ repository-managed
directory owned by _testuser_.
These example file dependencies ship, pre-deployed in the _DeployR_ repository
so there is no further action for you to take in order for this example to use
them.
#### RBroker Framework - Pooled Task Runtime
This examples uses the [RBroker Framework](http://deployr.revolutionanalytics.com/documents/dev/rbroker/)
to integrate _DeployR_ real-time scoring capabilities inside the example server
application.
Specifically, this example uses the [Pooled Task Runtime](http://deployr.revolutionanalytics.com/documents/dev/rbroker/#pooled)
provided by the _RBroker Framework_.
#### RBroker Framework - Throughput
The _Resize_ button in the _RBroker Runtime Window_ in the example client
application lets the end user experiment with the size of the pool of R sessions
associated with the _Pooled Task Runtime_.
We recommend experimenting with the size of the pool and observing the effect
this has on throughput. See the following sections of the _RBroker Framework_
tutorial for related details:
- [Client Application Simulations](http://deployr.revolutionanalytics.com/documents/dev/rbroker/#simulation)
- [Client Application Profiling](http://deployr.revolutionanalytics.com/documents/dev/rbroker/#profiling)
- [Grid Resource Management](http://deployr.revolutionanalytics.com/documents/dev/rbroker/#gridprimer)
## License ##
Copyright (C) 2010-2014 by Revolution Analytics Inc.
This program is licensed to you under the terms of Version 2.0 of the
Apache License. This program is distributed WITHOUT
ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
details.

10
analytics/ccFraudScore.R Executable file
Просмотреть файл

@ -0,0 +1,10 @@
### SCORING THE MODEL
require(RevoScriptTools)
revoInput('{"name": "bal", "render":"integer", "default": 5000, "min" : 0, "max": 25000 }')
revoInput('{"name": "trans", "render":"integer", "default": 12, "min" : 0, "max": 100 }')
revoInput('{"name": "credit", "render":"integer", "default": 8, "min" : 0, "max": 75 }')
if(!exists('fraudModel')){load('fraudModel.rData')}
score<-data.frame(balance=bal,numTrans=trans,creditLine=credit)
x<-predict(fraudModel, score)

Двоичные данные
analytics/fraudModel.rData Executable file

Двоичный файл не отображается.

22
bower.json Normal file
Просмотреть файл

@ -0,0 +1,22 @@
{
"name": "js-example-fraud-score",
"version": "0.0.1",
"authors": [ "DeployR - Revolution Analytics Inc." ],
"license": "Apache License 2.0",
"ignore": [
"**/.*",
"node_modules",
"client/app/bower_components",
"test",
"tests"
],
"dependencies": {
"bootstrap": "~3.2.0",
"angular": "~1.2.22",
"angular-animate": "~1.2.22",
"angular-route": "~1.2.22",
"animate.css": "~3.2.0",
"angular-bootstrap": "~0.11.2",
"fontawesome": "~4.1.0"
}
}

24
client/app/css/main.css Normal file
Просмотреть файл

@ -0,0 +1,24 @@
/*
* Copyright (C) 2010-2014 by Revolution Analytics Inc.
*
* This program is licensed to you under the terms of Version 2.0 of the
* Apache License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
* details.
*/
.runtime {
margin-left: auto;
margin-right: auto;
max-width: 1000px;
}
.results {
margin-left: auto;
margin-right: auto;
max-width: 1000px;
}
.stats {
margin-left: 20px;
}

34
client/app/index.html Normal file
Просмотреть файл

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html ng-app="FraudApp">
<head lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<title>R Analytics Fraud Score Example</title>
<!-- styles -->
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css"/>
<link rel="stylesheet" href="bower_components/fontawesome/css/font-awesome.css"/>
<link rel="stylesheet" href="bower_components/animate.css/animate.css"/>
<link rel="stylesheet" href="./css/main.css"/>
<script src="/primus/primus.io.js"></script>
</head>
<body>
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#">R Analytics Fraud Score Example Application - <small>Powered by DeployR RBroker Framework</small></a>
</div>
</div>
</nav>
<div class="container">
<!-- views -->
<div ng-view></div>
</div>
<!-- scripts -->
<script src="bower_components/angular/angular.min.js"></script>
<script src="bower_components/angular-route/angular-route.min.js"></script>
<script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js"></script>
<script src="./js/bundled.js"></script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,158 @@
/*
* Copyright (C) 2010-2014 by Revolution Analytics Inc.
*
* This program is licensed to you under the terms of Version 2.0 of the
* Apache License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
* details.
*/
/*
* JavaScript Fraud Detection Example Application
*
* AngularJS ng-controller
*
* Initialization:
*
* Establishes WS connection on /fraudengine, subscribes on /topic/fraud.
*
* Events:
*
* FRAUDSCORE - RTask result message.
* RUNTIMESTATS - RBroker runtime statistics message.
* CLIENTALERT - RBroker runtime (error) notification message.
*
* User driven (index.html) events:
*
* Resize click -> $scope.resizePool() -> POST:/fraud/pool/init/{size}
*
* Execute click -> $scope.executeTasks() -> GET:/fraud/score/{tasks}
*
*/
module.exports = function($scope, $http, $location) {
//
// ng-controller model on $scope.
//
$scope.brokerInitialized = false;
$scope.alertMessage = null;
$scope.fraudScoreResults = [];
$scope.poolSize = 1;
$scope.taskCount = 1;
$scope.runtimeStats = {
requestedPoolSize: 1,
allocatedPoolSize: 1,
submittedTasks: 0,
successfulTasks: 0,
failedTasks: 0,
averageCodeExecution: 0,
averageServerOverhead: 0,
averageNetworkLatency: 0
};
$scope.targetTaskThroughput = 0;
$scope.currentTaskThroughput = 0;
$scope.startTaskThroughput = 0;
$scope.secondTaskThroughput = 0;
$scope.minuteTaskThroughput = 0;
//
// Resize Button Handler:
//
$scope.resizePool = function() {
$scope.alertMessage = 'RBroker pool is initializing. ' +
'Requested ' + $scope.poolSize + ' R session(s) in the pool. ' +
'This may take some time. Please wait.';
$scope.brokerInitialized = false;
console.log('Attempt to resize pool succeeded, new size=' + $scope.poolSize);
$http.post('/fraud/pool/init/' + $scope.poolSize)
.success(function(data, status, headers, config) {
$scope.alertMessage = null;
console.log('Attempt to resize pool succeeded, new size=' + $scope.poolSize);
}).error(function(data, status, headers, config) {
$scope.errorMessage = 'Attempt to resize pool failed, error=' + data;
}).finally(function() {
$scope.fraudScoreResults = [];
$scope.brokerInitialized = true;
$scope.currentTaskThroughput = 0;
$scope.secondTaskThroughput = 0;
$scope.minuteTaskThroughput = 0;
});
};
//
// Execute Button Handler:
//
$scope.executeTasks = function() {
$scope.currentTaskThroughput = 0;
$scope.secondTaskThroughput = 0;
$scope.minuteTaskThroughput = 0;
$scope.targetTaskThroughput = $scope.taskCount;
$scope.startTaskThroughput = Date.now();
$http.get('/fraud/score/' + $scope.taskCount)
.success(function(data, status, headers, config) {
console.log('Attempt to execute tasks succeeded, taskCount=' + $scope.taskCount);
}).error(function(data, status, headers, config) {
$scope.errorMessage = 'Can\'t retrieve scores list!';
$scope.errorMessage = 'Attempt to execute tasks failed, error=' + data;
});
};
var primus = Primus.connect('ws://localhost:' + $location.port());
// Subscribe for events on /topic/fraud.
primus.on('open', function() {
primus.on('/topic/fraud', function(msgObj) {
if (msgObj.msgType === 'FRAUDSCORE') {
var elapsedTime = Date.now() - $scope.startTaskThroughput;
// $apply to propgate change to model.
$scope.$apply(function() {
$scope.currentTaskThroughput += 1;
var throughput =
(1000 / elapsedTime) * $scope.currentTaskThroughput;
$scope.secondTaskThroughput =
+(Math.round((throughput - (throughput % 0.01)) + 'e+2') + 'e-2');
$scope.minuteTaskThroughput =
Math.round($scope.secondTaskThroughput * 60);
// Discard older fraudScore from fraudScoreResults
// list to prevent browser rendering exhaustion.
if ($scope.fraudScoreResults.length > 300) {
$scope.fraudScoreResults.length = 150;
}
$scope.fraudScoreResults.unshift(msgObj);
});
} else if (msgObj.msgType === 'RUNTIMESTATS') {
// $apply to propogate change to model.
$scope.$apply(function() {
$scope.alertMessage = null;
$scope.runtimeStats = msgObj;
});
} else if (msgObj.msgType === 'CLIENTALERT') {
// $apply to propogate change to model.
$scope.$apply(function() {
$scope.alertMessage = msgObj.msg;
});
}
});
//
// Initialize initial RBroker pool on application startup.
//
$scope.resizePool();
});
};

39
client/app/js/main.js Normal file
Просмотреть файл

@ -0,0 +1,39 @@
/*
* Copyright (C) 2010-2014 by Revolution Analytics Inc.
*
* This program is licensed to you under the terms of Version 2.0 of the
* Apache License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
* details.
*/
(function () {
'use strict';
var mainCtrl = require('./controllers/main-controller');
angular.module('FraudApp', ['ngRoute', 'ui.bootstrap'])
.config([
'$locationProvider',
'$routeProvider',
function($locationProvider, $routeProvider) {
$locationProvider.hashPrefix('!');
// routes
$routeProvider
.when("/", {
templateUrl: "./partials/stats.html",
controller: "MainController"
})
.otherwise({
redirectTo: '/'
});
}
])
// Load controller
.controller('MainController', ['$scope', '$http', '$location', mainCtrl]);
}());

Просмотреть файл

@ -0,0 +1,167 @@
<div class="row col-md-12">
<div class="panel panel-default runtime">
<div class="panel-heading clearfix">
<h5 class="panel-title pull-left">
<a tooltip-placement="bottom" tooltip="An RBroker runtime manages all aspects of RTask queueing, execution and result handling on behalf of client applicaitons integrating with DeployR.">
RBroker Runtime
</a>
</h5>
<span ng-show="runtimeStats.username">
<span style="margin-left:160px;font-size:14px; font-weight:bold">{{ runtimeStats.username }} @ {{ runtimeStats.endpoint }}</span>
</span>
<div class="pull-right">
<span><b>Pool Size:</b>
</span>
<input type="number" name="ps" ng-model="poolSize" ng-disabled="!brokerInitialized" min="1" max="500" step="1" value="1" />
<a class="btn btn-primary btn-xs" ng-disabled="!brokerInitialized" tooltip-placement="bottom" tooltip="Resize the pool of R sessions dedicated to the RBroker runtime in use by this demo application. The larger the pool, the higher the expected throughput. Note, the max pool size any application can acquire is ultimately determined by the size and configuration of the DeployR grid." ng-disabled="!brokerInitialized" ng-click="resizePool()">Resize
<span class="glyphicon glyphicon-refresh"></span>
</a>
</div>
</div>
<div class="row">
<div class="col-md-12" style="height:4px;">
</h5>
</div>
</div>
<div class="row stats">
<div class="col-md-12">
<h5>
Pool
</h5>
</div>
</div>
<div class="row stats">
<div class="col-md-4">
<a tooltip-placement="bottom" tooltip="Number of dedicated R sessions requested by the RBroker runtime.">
Requested Size:
</a>
<span style="font-size:16px;color:gray">{{runtimeStats.requestedPoolSize}}</span>
</p>
</div>
<div class="col-md-4">
<a tooltip-placement="bottom" tooltip="Number of dedicated R sessions allocated by DeployR to the RBroker runtime.">
Allocated Size:
</a>
<span style="font-size:16px;color:gray">{{runtimeStats.allocatedPoolSize}}</span>
</div>
<div class="col-md-4">
<a tooltip-placement="bottom" tooltip="Maximum number of RTask that can execute in parallel on the RBroker runtime. Note, the max concurrency of the RBroker (pooled) runtime always equals the number of dedicated R sessions allocated by DeployR to the runtime.">
Max Concurrency:
</a>
<span style="font-size:16px;color:gray">{{runtimeStats.allocatedPoolSize}}</span>
</div>
</div>
<div class="row">
<div class="col-md-12" style="height:4px;">
</h5>
</div>
</div>
<div class="row stats">
<div class="col-md-12">
<h5>Tasks</h5>
</div>
</div>
<div class="row stats">
<div class="col-md-4">
<a tooltip-placement="bottom" tooltip="Number of RTask that have been submitted to the RBroker runtime.">
Submitted Tasks:
</a>
<span style="font-size:16px;color:gray">{{runtimeStats.submittedTasks}}</span>
</p>
</div>
<div class="col-md-4">
<a tooltip-placement="bottom" tooltip="Number of RTask that have been successfully executed by the RBroker runtime.">
Successful Tasks:
</a>
<span style="font-size:16px;color:gray">{{runtimeStats.successfulTasks}}</span>
</div>
<div class="col-md-4">
<a tooltip-placement="bottom" tooltip="Number of RTask that have resulted in error when executed by the RBroker runtime. A failure may occur for a number of reasons. For example, if any required input values to your R script are missing or invalid, if there is an error in the syntax or logic of your R script or if your instance of the RBroker runtime loses its connection to the DeployR server due to a dropped network connection.">
Failed Tasks:
</a>
<span style="font-size:16px;color:gray">{{runtimeStats.failedTasks}}</span>
</div>
</div>
<div class="row">
<div class="col-md-12" style="height:4px;">
</h5>
</div>
</div>
<div class="row stats">
<div class="col-md-12">
<h5>Fraud Task Throughput</h5>
</div>
</div>
<div class="row stats">
<div class="col-md-4">
<a tooltip-placement="bottom" tooltip="Number of RTask executed following the most recent click on the Execute button.">
Executed Tasks:
</a>
<span style="font-size:16px;color:gray">{{currentTaskThroughput}}</span>
</div>
<div class="col-md-4">
<a tooltip-placement="bottom" tooltip="Estimated RTask throughput, measured in tasks per second, based on the observed RBroker runtime performance for tasks executing following the most recent click on the Execute button.">
Tasks Per Second:
</a>
<span style="font-size:16px;color:gray">{{secondTaskThroughput}}</span>
</p>
</div>
<div class="col-md-4">
<a tooltip-placement="bottom" tooltip="Estimated RTask throughput, measured in tasks per minute, based on the observed RBroker runtime performance for tasks executing following the most recent click on the Execute button.">
Tasks Per Minute:
</a>
<span style="font-size:16px;color:gray">{{minuteTaskThroughput}}</span>
</div>
</div>
<div class="row">
<div class="col-md-12" style="height:10px;">
</h5>
</div>
</div>
<div class="row" ng-show="alertMessage">
<div class="col-md-12 alert alert-info">{{alertMessage}}</div>
</div>
</div>
</div>
<div class="row col-md-12">
<div class="panel panel-default runtime">
<div class="panel-heading clearfix">
<h5 class="panel-title pull-left">
<a tooltip-placement="bottom" tooltip="This demo application performs real-time scoring on user account data to determine the risk of fraudulent transactions.">
Fraud Analysis
</a>
</h5>
<div class="pull-right">
<span><b>Task Count:</b>
</span>
<input name="tc" ng-model="taskCount" ng-disabled="!brokerInitialized" type="number" min="1" max="100000" step="1" value="1" />
<a class="btn btn-primary btn-xs" ng-disabled="!brokerInitialized" tooltip-placement="bottom" tooltip="Submit simulated account data for real-time scoring. Each set of account data is executed as an RTask using the RBroker runtime where a fraud score is generated and returned." ng-disabled="!brokerInitialized" ng-click="executeTasks()">Execute
<span class="glyphicon glyphicon-repeat"></span>
</a>
</div>
</div>
<div class="row">
<div class="col-md-12" style="height:4px;">
</h5>
</div>
</div>
<table class="table table-hover table-striped table-condensed">
<thead>
<th class="col-md-3">Account Balance</th>
<th class="col-md-3">Transactions</th>
<th class="col-md-3">Credit</th>
<th class="col-md-3">Fraud Score</th>
</thead>
<tbody>
<tr ng-repeat="fraudScoreResult in fraudScoreResults">
<td>{{fraudScoreResult.balance}}</td>
<td>{{fraudScoreResult.transactions}}</td>
<td>{{fraudScoreResult.credit}}</td>
<td>{{fraudScoreResult.score}}</td>
</tr>
</tbody>
</table>
</div>
</div>

15
config/config.json Normal file
Просмотреть файл

@ -0,0 +1,15 @@
{
"host": "http://localhost:7300",
"port": "9080",
"credentials": {
"username": "testuser",
"password": "changeme"
},
"logging": false,
"constants" : {
"REPO_OWNER": "testuser",
"REPO_SCRIPT": "ccFraudScore.R",
"REPO_DIRECTORY": "example-fraud-score",
"FRAUD_MODEL": "fraudModel.rData"
}
}

57
gulpfile.js Normal file
Просмотреть файл

@ -0,0 +1,57 @@
/*
* Copyright (C) 2010-2014 by Revolution Analytics Inc.
*
* This program is licensed to you under the terms of Version 2.0 of the
* Apache License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
* details.
*/
var gulp = require('gulp'),
jshint = require('gulp-jshint'),
browserify = require('gulp-browserify'),
concat = require('gulp-concat'),
server = require('gulp-express'),
unzip = require('gulp-unzip');
gulp.task('deployr-deps', function(){
gulp.src('./.modules/*.zip')
.pipe(unzip())
.pipe(gulp.dest('./node_modules'))
});
gulp.task('lint', function() {
gulp.src([ '!./client/app/js/bundled.js',
'./client/app/**/*.js',
'!./client/app/bower_components/**/*',
'!./client/app/js/vendor/**/*' ])
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(jshint.reporter('fail'));
});
gulp.task('browserify', function() {
gulp.src(['client/app/js/main.js'])
.pipe(browserify({
insertGlobals: true,
debug: true
}))
.pipe(concat('bundled.js'))
.pipe(gulp.dest('./client/app/js'))
});
gulp.task('server', function () {
//start the server at the beginning of the task
server.run({
file: 'server.js'
});
//restart the server when file changes
gulp.watch(['client/app/**/*.html'], server.notify);
gulp.watch(['client/app/js/**/*.js'], ['lint', 'browserify']);
gulp.watch(['client/app/css/**/*.css']);
});
gulp.task('default', ['lint', 'browserify', 'server'] );

31
package.json Normal file
Просмотреть файл

@ -0,0 +1,31 @@
{
"name": "js-example-fraud-score",
"version": "0.0.1",
"description": "R Analytics Fraud Score Example Application - Powered by DeployR RBroker Framework",
"repository": "https://github.com/deployr/js-example-fraud-score.git",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"postinstall": "bower install && gulp deployr-deps"
},
"main": "gulpfile.js",
"author": "DeployR - Revolution Analytics Inc.",
"main": "server.js",
"dependencies": {
"gulp": "^3.8.7",
"gulp-jshint": "^1.8.4",
"gulp-concat": "^2.3.4",
"gulp-unzip": "^0.1.2",
"gulp-browserify": "^0.5.0",
"bower": "^1.3.9",
"browserify": "^5.10.0",
"primus": "^2.4.8",
"express": "^4.9.5",
"ws": "^0.4.32",
"primus.io": "^3.1.2",
"primus-multiplex": "^3.0.3",
"primus-emitter": "^3.0.3",
"primus-rooms": "^3.1.0",
"gulp-express": "^0.1.0"
},
"license": "Apache License 2.0"
}

56
server.js Normal file
Просмотреть файл

@ -0,0 +1,56 @@
/*
* Copyright (C) 2010-2014 by Revolution Analytics Inc.
*
* This program is licensed to you under the terms of Version 2.0 of the
* Apache License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
* details.
*/
var express = require('express'),
Primus = require('primus.io'),
config = require('./config/config'),
FraudService = require('./server/service/fraud-service'),
app = express(),
router = express.Router();
app.use('/', router);
app.use(express.static(__dirname + '/client/app'));
// -- Start Primus server --
var server = require('http').createServer(app);
var primus = new Primus(server, { transformer: 'websockets', parser: 'JSON' });
primus.on('connection', function (spark) {
var fraudService = new FraudService(primus);
router.get('/fraud/score/:tasks', function(req, res) {
var tasks = req.params.tasks === 0 ? 1 : req.params.tasks;
console.log('REST:/fraud/score/' + tasks + ' called.');
for(var i = 0; i < tasks; i++) {
fraudService.submit(fraudService.buildTask());
}
res.json({ success: true });
});
router.post('/fraud/pool/init/:size', function (req, res) {
var size = req.params.size === 0 ? 1 : req.params.size;
console.log('REST:/pool/init/' + size + ' called.');
fraudService.buildPool(size);
res.json({ success: true });
});
});
primus.on('disconnection', function () {
console.log('disconnect...');
});
// -- Start server --
server.listen(config.port, function(){
console.log('\033[96mlistening on localhost:' + config.port +' \033[39m');
});

Просмотреть файл

@ -0,0 +1,216 @@
/*
* Copyright (C) 2010-2014 by Revolution Analytics Inc.
*
* This program is licensed to you under the terms of Version 2.0 of the
* Apache License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
* details.
*/
'use strict';
var rbroker = require('rbroker'),
RIn = require('deployr').RInput,
print = require('../util/rbroker-print-helper'),
config = require('../../config/config');
var FRAUDMSGTOPIC = '/topic/fraud',
MSG_TYPES = {
runtime: 'RUNTIMESTATS',
score: 'FRAUDSCORE',
error: 'CLIENTALERT'
};
var round = function(num) {
return +(Math.round(num + 'e+2') + 'e-2');
};
function FraudService(primus) {
this.primus = primus;
this.broker = null;
this.lastAllocatedPoolSize = 0;
this.brokerConfig = {
maxConcurrentTaskLimit: 0,
host: config.host,
credentials: config.credentials,
releaseGridResources: true,
logging: config.logging,
pool: {
preloadobjectname: config.constants.FRAUD_MODEL,
preloadobjectauthor: config.constants.REPO_OWNER,
preloadobjectdirectory: config.constants.REPO_DIRECTORY
}
};
}
FraudService.prototype = {
buildPool: function(poolSize) {
var self = this;
this.brokerConfig.maxConcurrentTaskLimit = poolSize;
if (!this.broker) {
this.attachBroker();
} else {
this.broker.shutdown()
.then(function() {
console.log('Pooled: RBroker shutdown `successful`.');
self.attachBroker();
}, function() {
console.log('Pooled: RBroker has shutdown `failure`.');
});
}
this.broadcast(this.runtimeStats());
},
buildTask: function(task) {
var bal = Math.abs(Math.random() * 25000),
trans = Math.abs(Math.random() * 100),
credit = Math.abs(Math.random() * 75);
return rbroker.pooledTask({
filename: config.constants.REPO_SCRIPT,
directory: config.constants.REPO_DIRECTORY,
author: config.constants.REPO_OWNER,
routputs: ['x'],
rinputs: [RIn.numeric('bal', bal),
RIn.numeric('trans', trans),
RIn.numeric('credit', credit)
]
});
},
submit: function(task) {
if (this.broker && task) {
this.broker.submit(task);
}
},
destroy: function() {
if (this.broker) {
this.broker.shutdown()
.then(function() {
console.log('Pooled: RBroker shutdown `successful`.');
self.attachBroker();
}, function() {
console.log('Pooled: RBroker has shutdown `failure`.');
});
}
},
/**
* Push RuntimeStats message over STOMP Web Socket to clients
* listening on FRAUDMSGTOPIC.
*
* @api private
*/
broadcast: function(message) {
this.primus.send(FRAUDMSGTOPIC, message);
},
/**
* Attach and listen on a new PooledTaskBroker.
* @api private
*/
attachBroker: function() {
var self = this;
this.broker = rbroker.pooledTaskBroker(this.brokerConfig)
.complete(function(rTask, rTaskResult) {
print.results(rTask, rTaskResult);
// -- notify successful result --
self.broadcast(self.buildFraudScore(rTask, rTaskResult));
})
.error(function(err) {
print.error(err);
// -- notify error --
self.broadcast({
msgType: MSG_TYPES.error,
cause: err,
msg: 'The RBroker runtime has indicated an unexpected ' +
' runtime error has occured. Cause: ' + err
});
})
.progress(function(stats) {
print.stats(stats);
self.broadcast(self.runtimeStats(stats));
});
this.lastAllocatedPoolSize = this.broker.maxConcurrency();
console.log('RBroker pool initialized with ' +
this.lastAllocatedPoolSize + ' R sessions.');
},
/**
* Private helper methods.
* @api private
*/
runtimeStats: function(stats) {
var runtime = {
msgType: MSG_TYPES.runtime,
requestedPoolSize: this.brokerConfig.maxConcurrentTaskLimit,
allocatedPoolSize: this.lastAllocatedPoolSize,
endpoint: this.brokerConfig.host
};
if (this.brokerConfig.credentials) {
runtime.username = this.brokerConfig.credentials.username;
}
if (stats) {
runtime.submittedTasks = stats.totalTasksRun;
runtime.successfulTasks = stats.totalTasksRunToSuccess;
runtime.failedTasks = stats.totalTasksRunToFailure;
runtime.averageCodeExecution = 0;
runtime.averageServerOverhead = 0;
runtime.averageNetworkLatency = 0;
if (stats.totalTasksRunToSuccess > 0) {
runtime.averageCodeExecution =
round(stats.totalTimeTasksOnCode / stats.totalTasksRunToSuccess);
var avgTimeOnServer =
stats.totalTimeTasksOnServer / stats.totalTasksRunToSuccess;
runtime.averageServerOverhead =
round(avgTimeOnServer - runtime.averageCodeExecution);
var avgTimeOnCall =
stats.totalTimeTasksOnCall / stats.totalTasksRunToSuccess;
runtime.averageNetworkLatency =
round(avgTimeOnCall - avgTimeOnServer);
}
}
return runtime;
},
/**
* Private helper methods.
* @api private
*/
buildFraudScore: function(rTask, rTaskResult) {
var rinputs = rTask.toJSON().rinputs;
return {
msgType: MSG_TYPES.score,
success: rTaskResult ? true : false,
balance: Math.round(rinputs[0].value),
transactions: Math.round(rinputs[1].value),
credit: Math.round(rinputs[2].value),
score: rTaskResult ? rTaskResult.generatedObjects[0].value : -1
};
}
};
module.exports = FraudService;

Просмотреть файл

@ -0,0 +1,112 @@
/*
* Copyright (C) 2010-2014 by Revolution Analytics Inc.
*
* This program is licensed to you under the terms of Version 2.0 of the
* Apache License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
* details.
*/
var taskType = {
DISCRETE: 'DISCRETE',
POOLED: 'POOLED',
BACKGROUND: 'BACKGROUND'
};
/*
* RBroker runtime summary statistics (print) helper class.
*/
module.exports = {
/**
* Prints RBroker Runtime Stats to console output.
*/
stats: function(stats) {
var displayAvgTimeOnCode = 0,
displaySAvgTimeOnServer = 0,
displayAvgTimeOnCall = 0,
maxConcurrency = stats.maxConcurrency;
console.log('[progress]-----------------------------------------');
console.log('RBroker Activity Summary');
console.log('RBroker: Max Concurrency [ ' + maxConcurrency + ' ]');
console.log('RBroker: Total Tasks Run [ ' +
stats.totalTasksRun + ' ]');
console.log('RBroker: Tasks Ok [ ' +
stats.totalTasksRunToSuccess + ' ] Fail [ ' +
stats.totalTasksRunToFailure + ' ]');
if (stats.totalTasksRunToSuccess > 0) {
displayAvgTimeOnCode =
stats.totalTimeTasksOnCode / stats.totalTasksRunToSuccess;
displayAvgTimeOnServer =
stats.totalTimeTasksOnServer / stats.totalTasksRunToSuccess;
displayAvgTimeOnCall =
stats.totalTimeTasksOnCall / stats.totalTasksRunToSuccess;
}
console.log('RBroker: Task Average Time On Code [ ' +
displayAvgTimeOnCode + ' ]');
console.log('RBroker: Task Average Time On Server [ ' +
displayAvgTimeOnServer + ' ]');
console.log('RBroker: Task Average Time On Call [ ' +
displayAvgTimeOnCall + ' ]\n');
console.log('---------------------------------------------------');
},
/**
* Prints errors to console output.
*/
error: function(err) {
console.log('[error]--------------------------------------------');
console.log('Status[fail]: cause= ' + err);
console.log('---------------------------------------------------');
},
/**
* Prints RTaskResult to console output.
*/
results: function(task, result) {
console.log('[completed]----------------------------------------');
console.log('Task: ' + task);
switch (result.type) {
case taskType.DISCRETE:
if (result.success) {
console.log('Status[ok]: [ code : ' +
result.timeOnCode + ' , server : ' +
result.timeOnServer + ' , call : ' +
result.timeOnCall + ' ]');
} else {
console.log('Status[fail]: cause=' + result.failure);
}
break;
case taskType.POOLED:
if (result.success) {
console.log('Status[ok]: [ code : ' +
result.timeOnCode + ' , server : ' +
result.timeOnServer + ' , call : ' +
result.timeOnCall + ' ]');
} else {
console.log('Status[fail]: cause=' + result.failure);
}
break;
case taskType.BACKGROUND:
if (result.success) {
console.log('Status[ok]: [ server : ' +
result.timeOnServer + ' , call : ' +
result.timeOnCall + ' ]');
} else {
console.log('Status[fail]: cause=' + result.failure);
}
break;
}
console.log('---------------------------------------------------');
}
};