Merge branch 'Symptoms-caused-by-Lots'
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,9 @@
|
|||||||
class PageInitializer {
|
class PageInitializer {
|
||||||
|
|
||||||
static initializePage({ symptom, vaccine }) {
|
static initializePage({ symptom, vaccine, symptomVsSymptomChart }) {
|
||||||
PageInitializer.#configureSymptom(symptom);
|
PageInitializer.#configureSymptom(symptom);
|
||||||
PageInitializer.#configureVaccine(vaccine);
|
PageInitializer.#configureVaccine(vaccine);
|
||||||
|
PageInitializer.#configureSymptomVsSymptomChart(symptomVsSymptomChart);
|
||||||
}
|
}
|
||||||
|
|
||||||
static #configureSymptom({ symptomSelectElement, prrByVaccineTableElement, downloadPrrByVaccineTableButton }) {
|
static #configureSymptom({ symptomSelectElement, prrByVaccineTableElement, downloadPrrByVaccineTableButton }) {
|
||||||
@@ -25,6 +26,12 @@ class PageInitializer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static #configureSymptomVsSymptomChart(symptomVsSymptomChart) {
|
||||||
|
new SymptomVsSymptomChartViewInitializer().configureSymptomVsSymptomChart(
|
||||||
|
symptomVsSymptomChart,
|
||||||
|
PageInitializer.#initializeSelectElement);
|
||||||
|
}
|
||||||
|
|
||||||
static #initializeSelectElement({ selectElement, onValueSelected, minimumInputLength }) {
|
static #initializeSelectElement({ selectElement, onValueSelected, minimumInputLength }) {
|
||||||
selectElement.select2({ minimumInputLength: minimumInputLength });
|
selectElement.select2({ minimumInputLength: minimumInputLength });
|
||||||
selectElement.on(
|
selectElement.on(
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
class Sets {
|
||||||
|
|
||||||
|
static filterSet(set, predicate) {
|
||||||
|
return new Set([...set].filter(predicate));
|
||||||
|
}
|
||||||
|
|
||||||
|
static union(sets) {
|
||||||
|
return sets.reduce(
|
||||||
|
(union, set) => new Set([...union, ...set]),
|
||||||
|
new Set());
|
||||||
|
}
|
||||||
|
|
||||||
|
static intersection(set1, set2) {
|
||||||
|
return new Set([...set1].filter(x => set2.has(x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static isEmpty(set) {
|
||||||
|
return set.size == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
class SymptomVsSymptomChartDataProvider {
|
||||||
|
|
||||||
|
// FK-TODO: extract utility class and test
|
||||||
|
static retainCommonKeys({ dict1, dict2 }) {
|
||||||
|
const commonKeys = SymptomVsSymptomChartDataProvider.#getCommonKeys(dict1, dict2);
|
||||||
|
return {
|
||||||
|
dict1: SymptomVsSymptomChartDataProvider.#retainKeysOfDict(dict1, commonKeys),
|
||||||
|
dict2: SymptomVsSymptomChartDataProvider.#retainKeysOfDict(dict2, commonKeys)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static getChartData({ prrByLotX, prrByLotY }) {
|
||||||
|
const { dict1: prrByLotXCommon, dict2: prrByLotYCommon } =
|
||||||
|
SymptomVsSymptomChartDataProvider.retainCommonKeys(
|
||||||
|
{
|
||||||
|
dict1: prrByLotX,
|
||||||
|
dict2: prrByLotY
|
||||||
|
});
|
||||||
|
const lots = Object.keys(prrByLotXCommon);
|
||||||
|
return {
|
||||||
|
labels: lots,
|
||||||
|
data:
|
||||||
|
lots.map(
|
||||||
|
lot => ({
|
||||||
|
x: prrByLotXCommon[lot],
|
||||||
|
y: prrByLotYCommon[lot]
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static #getCommonKeys(dict1, dict2) {
|
||||||
|
return Sets.intersection(
|
||||||
|
SymptomVsSymptomChartDataProvider.#getKeySet(dict1),
|
||||||
|
SymptomVsSymptomChartDataProvider.#getKeySet(dict2));
|
||||||
|
}
|
||||||
|
|
||||||
|
static #getKeySet(dict) {
|
||||||
|
return new Set(Object.keys(dict));
|
||||||
|
}
|
||||||
|
|
||||||
|
static #retainKeysOfDict(dict, keys2Retain) {
|
||||||
|
const entries2Retain =
|
||||||
|
Object
|
||||||
|
.entries(dict)
|
||||||
|
.filter(([key, _]) => keys2Retain.has(key));
|
||||||
|
return Object.fromEntries(entries2Retain);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
class SymptomVsSymptomChartView {
|
||||||
|
|
||||||
|
#canvas;
|
||||||
|
#chart;
|
||||||
|
|
||||||
|
constructor(canvas) {
|
||||||
|
this.#canvas = canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadAndDisplayChart(symptomX, symptomY) {
|
||||||
|
Promise
|
||||||
|
.all([symptomX, symptomY].map(symptom => PrrByVaccineProvider.getPrrByVaccine(symptom)))
|
||||||
|
.then(
|
||||||
|
([prrByLotX, prrByLotY]) => {
|
||||||
|
const { labels, data } = SymptomVsSymptomChartDataProvider.getChartData({ prrByLotX, prrByLotY });
|
||||||
|
this.#displayChart(symptomX, symptomY, labels, data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#displayChart(symptomX, symptomY, labels, data) {
|
||||||
|
if (this.#chart != null) {
|
||||||
|
this.#chart.destroy();
|
||||||
|
}
|
||||||
|
this.#chart =
|
||||||
|
new Chart(
|
||||||
|
this.#canvas,
|
||||||
|
this.#getConfig(symptomX, symptomY, labels, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
#getConfig(symptomX, symptomY, labels, data) {
|
||||||
|
return {
|
||||||
|
type: 'scatter',
|
||||||
|
data: this.#getData(labels, data),
|
||||||
|
options: this.#getOptions(symptomX, symptomY)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#getData(labels, data) {
|
||||||
|
return {
|
||||||
|
datasets:
|
||||||
|
[
|
||||||
|
{
|
||||||
|
labels: labels,
|
||||||
|
data: data,
|
||||||
|
backgroundColor: 'rgb(0, 0, 255)'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#getOptions(symptomX, symptomY) {
|
||||||
|
return {
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
type: 'linear',
|
||||||
|
position: 'bottom',
|
||||||
|
title: this.#getAxisTitle(symptomX)
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
title: this.#getAxisTitle(symptomY)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: function (context) {
|
||||||
|
return 'Batch: ' + context.dataset.labels[context.dataIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#getAxisTitle(symptom) {
|
||||||
|
return {
|
||||||
|
display: true,
|
||||||
|
text: 'PRR ratio of Batch for ' + symptom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
class SymptomVsSymptomChartViewInitializer {
|
||||||
|
|
||||||
|
#symptomVsSymptomChartView;
|
||||||
|
#symptomX;
|
||||||
|
#symptomY;
|
||||||
|
|
||||||
|
configureSymptomVsSymptomChart(
|
||||||
|
{ symptomSelectXElement, symptomSelectYElement, symptomVsSymptomChartViewElement },
|
||||||
|
initializeSelectElement) {
|
||||||
|
|
||||||
|
this.#symptomVsSymptomChartView = new SymptomVsSymptomChartView(symptomVsSymptomChartViewElement);
|
||||||
|
{
|
||||||
|
initializeSelectElement(
|
||||||
|
{
|
||||||
|
selectElement: symptomSelectXElement,
|
||||||
|
onValueSelected: symptomX => {
|
||||||
|
this.#symptomX = symptomX;
|
||||||
|
this.#loadAndDisplayChart();
|
||||||
|
},
|
||||||
|
minimumInputLength: 4
|
||||||
|
});
|
||||||
|
this.#symptomX = null;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
initializeSelectElement(
|
||||||
|
{
|
||||||
|
selectElement: symptomSelectYElement,
|
||||||
|
onValueSelected: symptomY => {
|
||||||
|
this.#symptomY = symptomY;
|
||||||
|
this.#loadAndDisplayChart();
|
||||||
|
},
|
||||||
|
minimumInputLength: 4
|
||||||
|
});
|
||||||
|
this.#symptomY = null;
|
||||||
|
}
|
||||||
|
this.#loadAndDisplayChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
#loadAndDisplayChart() {
|
||||||
|
if (this.#symptomX != null && this.#symptomY != null) {
|
||||||
|
this.#symptomVsSymptomChartView.loadAndDisplayChart(this.#symptomX, this.#symptomY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
QUnit.module('SymptomVsSymptomChartDataProviderTest', function () {
|
||||||
|
|
||||||
|
QUnit.test.each(
|
||||||
|
'shouldRetainCommonLots',
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{
|
||||||
|
dict1: {
|
||||||
|
"lotX": 1.0
|
||||||
|
},
|
||||||
|
dict2: {
|
||||||
|
"lotY": 2.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dict1: {
|
||||||
|
},
|
||||||
|
dict2: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
dict1: {
|
||||||
|
"lotX": 1.0,
|
||||||
|
"lotCommon": 2.0
|
||||||
|
},
|
||||||
|
dict2: {
|
||||||
|
"lotCommon": 3.0,
|
||||||
|
"lotY": 4.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dict1: {
|
||||||
|
"lotCommon": 2.0
|
||||||
|
},
|
||||||
|
dict2: {
|
||||||
|
"lotCommon": 3.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
(assert, [dicts, dictsHavingCommonKeys]) => {
|
||||||
|
// Given
|
||||||
|
|
||||||
|
// When
|
||||||
|
const dictsHavingCommonKeysActual = SymptomVsSymptomChartDataProvider.retainCommonKeys(dicts);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assert.deepEqual(dictsHavingCommonKeysActual, dictsHavingCommonKeys);
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test('shouldProvideChartData', function (assert) {
|
||||||
|
// Given
|
||||||
|
const prrByLotX = {
|
||||||
|
"lotX": 1.0,
|
||||||
|
"lotCommon": 2.0
|
||||||
|
};
|
||||||
|
const prrByLotY = {
|
||||||
|
"lotCommon": 3.0,
|
||||||
|
"lotY": 4.0
|
||||||
|
};
|
||||||
|
|
||||||
|
// When
|
||||||
|
const chartData = SymptomVsSymptomChartDataProvider.getChartData({ prrByLotX, prrByLotY });
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assert.deepEqual(
|
||||||
|
chartData,
|
||||||
|
{
|
||||||
|
labels: ["lotCommon"],
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
x: 2.0,
|
||||||
|
y: 3.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Safety Signals for COVID Batches</title>
|
||||||
|
<script src="../../Utils.js"></script>
|
||||||
|
<script src="../../UIUtils.js"></script>
|
||||||
|
<script src="../../NumberWithBarElementFactory.js"></script>
|
||||||
|
<script src="../js/Sets.js"></script>
|
||||||
|
<script src="../js/PrrByKey2CsvConverter.js"></script>
|
||||||
|
<script src="../js/PageInitializer.js"></script>
|
||||||
|
<script src="../js/PrrByVaccineProvider.js"></script>
|
||||||
|
<script src="../js/PrrByKeyTable.js"></script>
|
||||||
|
<script src="../js/PrrByKeyTableView.js"></script>
|
||||||
|
<script src="../js/PrrByVaccineTableView.js"></script>
|
||||||
|
<script src="../js/PrrBySymptomTableView.js"></script>
|
||||||
|
<script src="../js/SymptomVsSymptomChartView.js"></script>
|
||||||
|
<script src="../js/SymptomVsSymptomChartDataProvider.js"></script>
|
||||||
|
<script src="../js/SymptomVsSymptomChartViewInitializer.js"></script>
|
||||||
|
<link rel="stylesheet" href="https://code.jquery.com/qunit/qunit-2.17.2.css">
|
||||||
|
<script src="https://code.jquery.com/qunit/qunit-2.17.2.js"></script>
|
||||||
|
<script type="text/javascript" src="./jshamcrest.js"></script>
|
||||||
|
<script type="text/javascript" src="./jsmockito-1.0.4.js"></script>
|
||||||
|
<script>
|
||||||
|
JsHamcrest.Integration.QUnit();
|
||||||
|
JsMockito.Integration.QUnit();
|
||||||
|
</script>
|
||||||
|
<script src="./SymptomVsSymptomChartDataProviderTest.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="qunit"></div>
|
||||||
|
<div id="qunit-fixture"></div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -438,7 +438,7 @@
|
|||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"from SymptomsCausedByVaccines.HtmlUpdater import updateHtmlFile\n",
|
"from SymptomsCausedByVaccines.HtmlUpdater import updateHtmlFile, updateHtmlFile4SymptomsCausedByCOVIDLots\n",
|
||||||
"from SymptomsCausedByVaccines.PrrSeriesFactory import PrrSeriesFactory\n",
|
"from SymptomsCausedByVaccines.PrrSeriesFactory import PrrSeriesFactory\n",
|
||||||
"from SymptomsCausedByVaccines.PrrSeriesTransformer import PrrSeriesTransformer\n",
|
"from SymptomsCausedByVaccines.PrrSeriesTransformer import PrrSeriesTransformer\n",
|
||||||
"from SymptomsCausedByVaccines.ProportionalReportingRatiosPersister import saveProportionalReportingRatios\n",
|
"from SymptomsCausedByVaccines.ProportionalReportingRatiosPersister import saveProportionalReportingRatios\n",
|
||||||
@@ -627,11 +627,10 @@
|
|||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"updateHtmlFile(\n",
|
"updateHtmlFile4SymptomsCausedByCOVIDLots(\n",
|
||||||
" symptoms = list(prrByLotAndSymptom.columns),\n",
|
" symptoms = list(prrByLotAndSymptom.columns),\n",
|
||||||
" vaccines = list(prrByLotAndSymptom.index),\n",
|
" batches = list(prrByLotAndSymptom.index),\n",
|
||||||
" htmlFile = \"../docs/SymptomsCausedByCOVIDLots/index.html\",\n",
|
" htmlFile = \"../docs/SymptomsCausedByCOVIDLots/index.html\")"
|
||||||
" defaultSelectVaccineOptionText = 'Select Batch')"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,6 +16,19 @@ def updateHtmlFile(symptoms, vaccines, htmlFile, defaultSelectVaccineOptionText
|
|||||||
htmlFile = htmlFile,
|
htmlFile = htmlFile,
|
||||||
selectElementId = 'vaccineSelect')
|
selectElementId = 'vaccineSelect')
|
||||||
|
|
||||||
|
def updateHtmlFile4SymptomsCausedByCOVIDLots(symptoms, batches, htmlFile):
|
||||||
|
symptomOptions = getSymptomOptions(symptoms)
|
||||||
|
for selectElementId in ['symptomSelect', 'symptomSelectX', 'symptomSelectY']:
|
||||||
|
_saveOptions(
|
||||||
|
options = symptomOptions,
|
||||||
|
htmlFile = htmlFile,
|
||||||
|
selectElementId = selectElementId)
|
||||||
|
|
||||||
|
_saveOptions(
|
||||||
|
options = getVaccineOptions(batches, 'Select Batch'),
|
||||||
|
htmlFile = htmlFile,
|
||||||
|
selectElementId = 'vaccineSelect')
|
||||||
|
|
||||||
def _saveOptions(options, htmlFile, selectElementId):
|
def _saveOptions(options, htmlFile, selectElementId):
|
||||||
HtmlTransformerUtil().applySoupTransformerToFile(
|
HtmlTransformerUtil().applySoupTransformerToFile(
|
||||||
file=htmlFile,
|
file=htmlFile,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ FK-TODO:
|
|||||||
- Symptomhistogramm
|
- Symptomhistogramm
|
||||||
- http://www.howbadismybatch.info/SymptomsCausedByVaccines/index.html
|
- http://www.howbadismybatch.info/SymptomsCausedByVaccines/index.html
|
||||||
- http://www.howbadismybatch.info/SymptomsCausedByCOVIDLots/index.html
|
- http://www.howbadismybatch.info/SymptomsCausedByCOVIDLots/index.html
|
||||||
|
http://www.howbadismybatch.info/SymptomsCausedByCOVIDLots/test/index.test.html
|
||||||
|
|
||||||
anacron job:
|
anacron job:
|
||||||
sudo cp src/intensivstationen_howbadismybatch.sh /etc/cron.daily/intensivstationen_howbadismybatch
|
sudo cp src/intensivstationen_howbadismybatch.sh /etc/cron.daily/intensivstationen_howbadismybatch
|
||||||
|
|||||||
Reference in New Issue
Block a user