Skip to content

Commit

Permalink
Beginning of calculating new risk score and state resource to refresh…
Browse files Browse the repository at this point in the history
… single property (formula isn't quite there yet)
  • Loading branch information
meenahoda committed Mar 16, 2018
1 parent 995c9e0 commit 57dcd49
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 2 deletions.
@@ -0,0 +1,19 @@
'use strict'

module.exports = function calculateNewRiskScore (range, riskScore, growthCurve, mean, stdev) {
let value
switch (range) {
case 'veryHigh':
case 'high':
value = +growthCurve + +mean + +stdev
break
case 'medium':
case 'low':
case 'veryLow':
value = (+riskScore / 2) + +growthCurve
break
}

if (value < riskScore) return value.toFixed(2)
else return riskScore
}
Expand Up @@ -5,6 +5,7 @@ const async = require('async')
const stats = require('stats-lite')
const dist = require('distributions')
const moment = require('moment')
const calculateNewRiskScore = require('./calculate-new-risk-score')
const debug = require('debug')('tymly-rankings-plugin')

module.exports = async function generateStats (options, callback) {
Expand Down Expand Up @@ -37,15 +38,16 @@ module.exports = async function generateStats (options, callback) {

options.rankingModel.findById(r.uprn)
.then(row => {
// TODO: Make this less specific and more generic to any rankings
const growthCurve = row.lastAuditDate ? calculateGrowthCurve(ranges[range].exponent, row.lastAuditDate, r.risk_score).toFixed(5) : null
const updatedRiskScore = growthCurve ? calculateNewRiskScore(range, r.risk_score, growthCurve, mean, stdev) : null

options.rankingModel.upsert({
[options.pk]: r[_.snakeCase(options.pk)],
rankingName: options.category,
range: _.kebabCase(range),
distribution: distribution,
growthCurve: growthCurve
growthCurve: growthCurve,
updatedRiskScore: updatedRiskScore
}, {
setMissingPropertiesToNull: false
})
Expand Down
@@ -0,0 +1,10 @@
{
"RefreshRiskScore": {
"Type": "Task",
"Resource": "module:refreshRiskScore",
"ResourceConfig": {
"schema": "test",
"category": "factory"
}
}
}
@@ -0,0 +1,4 @@
module.exports = {
description: 'Regenerates a risk score usin growth curve',
example: require('./example.json')
}
@@ -0,0 +1,46 @@
'use strict'

const _ = require('lodash')
const calculateNewRiskScore = require('./../../services/rankings/calculate-new-risk-score.js')

class RefreshRiskScore {
init (resourceConfig, env, callback) {
this.rankings = env.blueprintComponents.rankings
this.storage = env.bootedServices.storage
this.client = env.bootedServices.storage.client
callback(null)
}

async run (event, context) {
const uprn = event.uprn
const schema = event.schema
const category = event.category
const key = schema + '_' + category

const rankingModel = this.storage.models[`${schema}_${this.rankings[key].rankingModel}`]
const statsModel = this.storage.models[`${schema}_${this.rankings[key].statsModel}`]

const rankingDoc = await rankingModel.findById(uprn)
const statsDoc = await statsModel.findById(category)
const riskScore = await this.client.query(getRiskScoreSQL({
schema: schema,
category: category,
uprn: uprn
}))

const updatedRiskScore = calculateNewRiskScore(
rankingDoc.range,
riskScore.rows[0].risk_score,
rankingDoc.growthCurve,
statsDoc.mean,
statsDoc.stdev
)
context.sendTaskSuccess({updatedRiskScore})
}
}

function getRiskScoreSQL (options) {
return `SELECT risk_score FROM ${_.snakeCase(options.schema)}.${_.snakeCase(options.category)}_scores WHERE uprn = ${options.uprn}`
}

module.exports = RefreshRiskScore
@@ -0,0 +1,13 @@
{
"type": "object",
"properties": {
"schema": {
"type": "string",
"description": "The schema to refresh the ranking within"
},
"category": {
"type": "string",
"description": "Identifies the property type to refresh e.g. factory, hospital etc."
}
}
}
Expand Up @@ -34,6 +34,10 @@
},
"growthCurve": {
"type": "number"
},
"updatedRiskScore": {
"type": "number",
"description": "An updated risk score calculated using growth curve"
}
},
"required": [
Expand Down
@@ -0,0 +1,21 @@
{
"Comment": "State machine to test refresh risk score state resource",
"version": "1.0",
"StartAt": "RefreshRiskScore",
"States": {
"RefreshRiskScore": {
"Type": "Task",
"Resource": "module:refreshRiskScore",
"InputPath": "$",
"End": true
}
},
"restrictions": [
{
"roleId": "$authenticated",
"allows": [
"*"
]
}
]
}
91 changes: 91 additions & 0 deletions plugins/tymly-rankings-plugin/test/refresh-risk-score-tests.js
@@ -0,0 +1,91 @@
/* eslint-env mocha */
'use strict'

const chai = require('chai')
const expect = chai.expect
const tymly = require('tymly')
const path = require('path')
const HlPgClient = require('hl-pg-client')
const process = require('process')
const sqlScriptRunner = require('./fixtures/sql-script-runner.js')

describe('Tests the refresh risk score State Resource', function () {
this.timeout(process.env.TIMEOUT || 5000)
let statebox, tymlyService

// explicitly opening a db connection as seom setup needs to be carried
// out before tymly can be started up
const pgConnectionString = process.env.PG_CONNECTION_STRING
const client = new HlPgClient(pgConnectionString)

before(function () {
if (process.env.PG_CONNECTION_STRING && !/^postgres:\/\/[^:]+:[^@]+@(?:localhost|127\.0\.0\.1).*$/.test(process.env.PG_CONNECTION_STRING)) {
console.log(`Skipping tests due to unsafe PG_CONNECTION_STRING value (${process.env.PG_CONNECTION_STRING})`)
this.skip()
}
})

it('should create the test resources', () => {
return sqlScriptRunner('./db-scripts/setup.sql', client)
})

it('should run the tymly service', function (done) {
tymly.boot(
{
pluginPaths: [
path.resolve(__dirname, './..'),
require.resolve('tymly-pg-plugin')
],
blueprintPaths: [
path.resolve(__dirname, './fixtures/blueprint'),
path.resolve(__dirname, '../lib/blueprints/ranking-blueprint')
],
config: {}
},
function (err, tymlyServices) {
expect(err).to.eql(null)
tymlyService = tymlyServices.tymly
statebox = tymlyServices.statebox
done()
}
)
})

it('should start the state resource execution to generate risk score', function (done) {
statebox.startExecution(
{
schema: 'test',
category: 'factory',
uprn: 3
}, // input
'test_refreshRiskScore_1_0', // state machine name
{
sendResponse: 'COMPLETE'
}, // options
function (err, executionDescription) {
if (err) {
return done(err)
}
console.log(JSON.stringify(executionDescription, null, 2))
expect(executionDescription.currentStateName).to.eql('RefreshRiskScore')
expect(executionDescription.currentResource).to.eql('module:refreshRiskScore')
expect(executionDescription.stateMachineName).to.eql('test_refreshRiskScore_1_0')
expect(executionDescription.status).to.eql('SUCCEEDED')
done()
}
)
})

it('should clean up the test resources', () => {
return sqlScriptRunner('./db-scripts/cleanup.sql', client)
})

it('should shutdown Tymly', async () => {
await tymlyService.shutdown()
})

it('Should close database connections', function (done) {
client.end()
done()
})
})
25 changes: 25 additions & 0 deletions plugins/tymly-rankings-plugin/test/service-test.js
Expand Up @@ -13,6 +13,7 @@ const existsCase = require('./../lib/components/services/rankings/case-statement
const optionCase = require('./../lib/components/services/rankings/case-statements/option.js')
const constantCase = require('./../lib/components/services/rankings/case-statements/constant.js')
const generateView = require('./../lib/components/services/rankings/generate-view-statement.js')
const calculateNewRiskScore = require('./../lib/components/services/rankings/calculate-new-risk-score.js')

describe('Tests the Ranking Service', function () {
this.timeout(process.env.TIMEOUT || 5000)
Expand Down Expand Up @@ -317,31 +318,37 @@ describe('Tests the Ranking Service', function () {
expect(result[0].range).to.eql('very-high')
expect(result[0].distribution).to.eql('0.0160')
expect(result[0].growthCurve).to.not.eql(null)
expect(result[0].updatedRiskScore).to.not.eql(null)

expect(result[1].uprn).to.eql('2')
expect(result[1].range).to.eql('very-low')
expect(result[1].distribution).to.eql('0.0107')
expect(result[1].growthCurve).to.not.eql(null)
expect(result[1].updatedRiskScore).to.not.eql(null)

expect(result[2].uprn).to.eql('3')
expect(result[2].range).to.eql('medium')
expect(result[2].distribution).to.eql('0.0329')
expect(result[2].growthCurve).to.not.eql(null)
expect(result[2].updatedRiskScore).to.not.eql(null)

expect(result[3].uprn).to.eql('4')
expect(result[3].range).to.eql('medium')
expect(result[3].distribution).to.eql('0.0323')
expect(result[3].growthCurve).to.not.eql(null)
expect(result[3].updatedRiskScore).to.not.eql(null)

expect(result[4].uprn).to.eql('5')
expect(result[4].range).to.eql('very-high')
expect(result[4].distribution).to.eql('0.0195')
expect(result[4].growthCurve).to.not.eql(null)
expect(result[4].updatedRiskScore).to.not.eql(null)

expect(result[5].uprn).to.eql('6')
expect(result[5].range).to.eql('medium')
expect(result[5].distribution).to.eql('0.0244')
expect(result[5].growthCurve).to.eql(null)
expect(result[5].updatedRiskScore).to.eql(null)
done()
})
.catch(err => done(err))
Expand Down Expand Up @@ -383,6 +390,7 @@ describe('Tests the Ranking Service', function () {
rankingModel.findById(5, (err, doc) => {
expect(+doc.growthCurve).to.not.eql(+growthCurveBefore)
expect(+doc.growthCurve).to.eql(0.78049)
expect(+doc.updatedRiskScore).to.eql(63.96)
done(err)
})
})
Expand Down Expand Up @@ -422,10 +430,27 @@ describe('Tests the Ranking Service', function () {
it('should check the growth curve has changed again', (done) => {
rankingModel.findById(5, (err, doc) => {
expect(+doc.growthCurve).to.eql(0.90501)
expect(+doc.updatedRiskScore).to.eql(64)
done(err)
})
})

it('should calculate new risk score', (done) => {
const options = {
riskScore: 146,
growthCurve: 1.78,
mean: 88.19,
stdev: 11.81
}

const highExpected = options.growthCurve + options.mean + options.stdev
const lowExpected = (options.riskScore / 2) + options.growthCurve

expect(+calculateNewRiskScore('veryHigh', options.riskScore, options.growthCurve, options.mean, options.stdev)).to.eql(highExpected)
expect(+calculateNewRiskScore('veryLow', options.riskScore, options.growthCurve, options.mean, options.stdev)).to.eql(lowExpected)
done()
})

it('should clean up the test resources', () => {
return sqlScriptRunner('./db-scripts/cleanup.sql', client)
})
Expand Down

0 comments on commit 57dcd49

Please sign in to comment.