-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathnotify_entrypoint.mjs
More file actions
111 lines (85 loc) · 3.47 KB
/
notify_entrypoint.mjs
File metadata and controls
111 lines (85 loc) · 3.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import {v4 as uuidv4} from "uuid"
import pino from "pino"
import {getSharedAuthToken, getBody} from "./helper/psu.mjs"
import {allowedOdsCodes, blockedOdsCodes} from "./helper/odscodes.mjs"
export { getSharedAuthToken }
const logger = pino()
const LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const DIGITS = "0123456789";
const randomChar = (chars) => chars[Math.floor(Math.random() * chars.length)];
/** Generate one two-letter, three-digit ODS code, e.g. "AB123" */
const generateOdsCode = () =>
`${randomChar(LETTERS)}${randomChar(LETTERS)}${randomChar(DIGITS)}${randomChar(DIGITS)}${randomChar(DIGITS)}`;
function buildFullOdsCodes(targetCount, seedCodes) {
const codes = new Set(seedCodes);
while (codes.size < targetCount) {
codes.add(generateOdsCode());
}
return Array.from(codes);
}
// The complete list of ODS codes
const fullOdsCodes = allowedOdsCodes.concat(blockedOdsCodes)
function computeCheckDigit(nhsNumber) {
const factors = [10,9,8,7,6,5,4,3,2]
let total = 0
for (let i = 0; i < 9; i++) {
total += parseInt(nhsNumber.charAt(i),10) * factors[i]
}
const rem = total % 11
let d = 11 - rem
if (d === 11) d = 0
return d
}
function generateValidNhsNumber() {
while (true) {
const partial = Array.from({length:9},() => Math.floor(Math.random()*10)).join("")
const cd = computeCheckDigit(partial)
if (cd < 10) return partial + cd
}
}
// Apparently Math.sampleNormal isn't a function? Do a quick Box-Muller transform instead
function sampleNormal(mean = 0, sd = 1) {
let u = 0, v = 0;
// avoid zeros because of log(0)
while (u === 0) u = Math.random();
while (v === 0) v = Math.random();
const z = Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
return z * sd + mean;
}
export function initUser(context, events, done) {
// Generate data for a patient
context.vars.odsCode = fullOdsCodes[Math.floor(Math.random()*fullOdsCodes.length)]
context.vars.nhsNumber = generateValidNhsNumber()
let prescriptionCount = Math.round(sampleNormal(3,1))
if (prescriptionCount < 1) prescriptionCount = 1 // just truncate at 1.
context.vars.prescriptionCount = prescriptionCount
context.vars.loopcount = 0
logger.info(`Patient ${context.vars.nhsNumber}, ODS ${context.vars.odsCode} has ${context.vars.prescriptionCount} prescriptions`)
done()
}
export function generatePrescData(requestParams, context, ee, next) {
logger.debug(`Generating a prescription for patient ${context.vars.nhsNumber}`)
const body = getBody(
true, /* isValid */
"completed", /* status */
context.vars.odsCode, /* odsCode */
context.vars.nhsNumber, /* nhsNumber */
"ready to collect" /* Item status */
)
// The body is fine - it works when I put it in postman
requestParams.json = body
context.vars.x_request_id = uuidv4()
context.vars.x_correlation_id = uuidv4()
context.vars.loopcount += 1
// Wait this long between requests
let meanDelay = 10 // seconds
let stdDevDelay = 10 // seconds
let delay = 0
if (context.vars.loopcount < context.vars.prescriptionCount) {
delay = sampleNormal(meanDelay, stdDevDelay)
while (delay < 0) delay = sampleNormal(meanDelay, stdDevDelay)
}
context.vars.nextDelay = delay
logger.debug(`Patient ${context.vars.nhsNumber} (on prescription update ${context.vars.loopcount}/${context.vars.prescriptionCount}) will think for ${context.vars.nextDelay} seconds`)
next()
}