The PLUM Prostate Cancer Risk Calculator
by lewpaka
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<meta name="author" content="Sumeet S. Shah">
<title>The PLUM Prostate Cancer Risk Calculator</title>
</head>
<body>
<div class="header-container">
<h2>
The PLUM Prostate Cancer Risk Calculator
</h2>
<em>
A calculator to estimate prostate cancer detection risk for patients receiving a multiparametric MRI (mpMRI) of the prostate. This calculator is only for patients who have never had a diagnosis of prostate cancer.
</em>
</div>
<form>
<div class="table">
<div class="tr">
<label class="td" for="age">Age (years): </label>
<input class="td" id="age" type="number" onchange="validateNumberField('age')">
<p class="td field-description">(40 - 90 years allowed)</p>
</div>
<div class="tr">
<label class="td" for="psa">PSA (ng/dL): </label>
<input class="td" id="psa" type="number" onchange="validateNumberField('psa')">
<p class="td field-description">(0.3 - 84 ng/dL allowed)</p>
</div>
<div class="tr">
<label class="td" for="prostate-volume">Prostate Volume (cc): </label>
<input class="td" id="prostate-volume" type="number" onchange="validateNumberField('prostate-volume')">
<p class="td field-description">(15 - 350 cc allowed)</p>
</div>
<div class="tr">
<label class="td" for="pi-rads-score">PI-RADS Score: </label>
<input class="td" id="pi-rads-score" type="number" onchange="validateNumberField('pi-rads-score', true)">
<p class="td field-description">(Highest PI-RADS scored lesion on MRI: 0, 1, 2, 3, 4, 5)</p>
</div>
</div>
<div class="form-question">
<label for="african-ancestry">Do you have African ancestry?</label>
<div>
<label>
<input type="radio" name="african-ancestry" value="1">
Yes
...
CSS
table,
th {
border-bottom: 1px solid black;
}
tr td {
min-width: 100px;
}
body {
font-family: 'Open Sans', arial, sans-serif;
margin: 30px;
}
.table {
display: table;
}
.form-question {
margin-top: 10px;
}
.form-question div {
margin-top: 5px;
}
.header-container {
margin-bottom: 10px;
}
.tr {
display: table-row;
}
.td {
display: table-cell;
min-width: 160px;
margin-top: 10px;
}
.field-description {
color: grey;
padding-left: 10px;
}
.result {
text-align: right;
}
.pbcg-table {
font-size: 1em;
color: gray;
}
.notes-text {
font-size: 0.6em;
margin-bottom: 10px;
}
#notes-header {
margin-bottom: -5px;
}
#references {
margin-bottom: -5px;
}
#calculate-risk {
font-size: 1em;
}
JavaScript
const h1Coefficients = {
piRads: {
0: 0,
1: 0,
2: 0,
3: 0.353384,
4: 0.949164,
5: 1.874741
},
age: 0.0350792,
africanAncestry: 0.5251729,
familyHistory: 0.3260991,
priorNegativeBiopsy: -1.17898,
psad: 0.1668917,
prostateVolume: -0.0186987,
constant: -2.397652
}
const h2Coefficients = {
piRads: {
0: 0,
1: 0,
2: 0,
3: 0.4550155,
4: 2.210478,
5: 3.634756
},
age: 0.0772189,
africanAncestry: 0.490936,
familyHistory: 0.4781321,
priorNegativeBiopsy: -1.15653,
psad: 1.204331,
prostateVolume: -0.0201334,
constant: -3.492468
}
const s1Coefficients = {
age: 0.01780617,
africanAncestry: 0.78721039,
familyHistory: 0.33233636,
priorNegativeBiopsy: -0.83613721,
psad: 0.13617244,
constant: -2.44052108,
dre: 0.04612721
}
const s2Coefficients = {
age: 0.05566536,
africanAncestry: 0.61596975,
familyHistory: 0.61003848,
priorNegativeBiopsy: -1.27437249,
psad: 0.7999651,
constant: -6.36851856,
dre: 0.85780143
}
const minMaxValues = {
age: {
min: 40,
max: 90
},
psa: {
min: 0.3,
max: 84
},
'prostate-volume': {
min: 15,
max: 350
},
'pi-rads-score': {
min: 0,
max: 5
}
}
const calculateRisk = () => {
// Get form values
const {
age,
africanAncestry,
familyHistory,
priorNegativeBiopsy,
psa,
prostateVolume,
piRadsScore,
dre
} = getFormFieldValues();
// Calculate risk values
calculatePlumRisk(age, africanAncestry, familyHistory, priorNegativeBiopsy, psa, prostateVolume, piRadsScore);
calcualtePbcgRisk(age, africanAncestry, familyHistory, priorNegativeBiopsy, psa, dre);
}
const calculatePlumRisk = (age, africanAncestry, familyHistory, priorNegativeBiopsy, psa, prostateVolume, piRadsScore) => {
const h1 = h1Coefficients.constant +
(age * h1Coefficients.age) +
(africanAncestry * h1Coefficients.africanAncestry) +
(familyHistory * h1Coefficients.familyHistory) +
...