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 {
|
||||
|
||||
static initializePage({ symptom, vaccine }) {
|
||||
static initializePage({ symptom, vaccine, symptomVsSymptomChart }) {
|
||||
PageInitializer.#configureSymptom(symptom);
|
||||
PageInitializer.#configureVaccine(vaccine);
|
||||
PageInitializer.#configureSymptomVsSymptomChart(symptomVsSymptomChart);
|
||||
}
|
||||
|
||||
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 }) {
|
||||
selectElement.select2({ minimumInputLength: minimumInputLength });
|
||||
selectElement.on(
|
||||
|
||||
20
docs/SymptomsCausedByCOVIDLots/js/Sets.js
Normal file
20
docs/SymptomsCausedByCOVIDLots/js/Sets.js
Normal file
@@ -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
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
38
docs/SymptomsCausedByCOVIDLots/test/index.test.html
Normal file
38
docs/SymptomsCausedByCOVIDLots/test/index.test.html
Normal file
@@ -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>
|
||||
1500
docs/SymptomsCausedByCOVIDLots/test/jshamcrest.js
Normal file
1500
docs/SymptomsCausedByCOVIDLots/test/jshamcrest.js
Normal file
File diff suppressed because it is too large
Load Diff
1019
docs/SymptomsCausedByCOVIDLots/test/jsmockito-1.0.4.js
Normal file
1019
docs/SymptomsCausedByCOVIDLots/test/jsmockito-1.0.4.js
Normal file
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": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from SymptomsCausedByVaccines.HtmlUpdater import updateHtmlFile\n",
|
||||
"from SymptomsCausedByVaccines.HtmlUpdater import updateHtmlFile, updateHtmlFile4SymptomsCausedByCOVIDLots\n",
|
||||
"from SymptomsCausedByVaccines.PrrSeriesFactory import PrrSeriesFactory\n",
|
||||
"from SymptomsCausedByVaccines.PrrSeriesTransformer import PrrSeriesTransformer\n",
|
||||
"from SymptomsCausedByVaccines.ProportionalReportingRatiosPersister import saveProportionalReportingRatios\n",
|
||||
@@ -627,11 +627,10 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"updateHtmlFile(\n",
|
||||
"updateHtmlFile4SymptomsCausedByCOVIDLots(\n",
|
||||
" symptoms = list(prrByLotAndSymptom.columns),\n",
|
||||
" vaccines = list(prrByLotAndSymptom.index),\n",
|
||||
" htmlFile = \"../docs/SymptomsCausedByCOVIDLots/index.html\",\n",
|
||||
" defaultSelectVaccineOptionText = 'Select Batch')"
|
||||
" batches = list(prrByLotAndSymptom.index),\n",
|
||||
" htmlFile = \"../docs/SymptomsCausedByCOVIDLots/index.html\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -16,6 +16,19 @@ def updateHtmlFile(symptoms, vaccines, htmlFile, defaultSelectVaccineOptionText
|
||||
htmlFile = htmlFile,
|
||||
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):
|
||||
HtmlTransformerUtil().applySoupTransformerToFile(
|
||||
file=htmlFile,
|
||||
|
||||
@@ -7,6 +7,7 @@ FK-TODO:
|
||||
- Symptomhistogramm
|
||||
- http://www.howbadismybatch.info/SymptomsCausedByVaccines/index.html
|
||||
- http://www.howbadismybatch.info/SymptomsCausedByCOVIDLots/index.html
|
||||
http://www.howbadismybatch.info/SymptomsCausedByCOVIDLots/test/index.test.html
|
||||
|
||||
anacron job:
|
||||
sudo cp src/intensivstationen_howbadismybatch.sh /etc/cron.daily/intensivstationen_howbadismybatch
|
||||
|
||||
Reference in New Issue
Block a user