Merge branch 'main' into pages

This commit is contained in:
frankknoll
2022-03-20 18:42:56 +01:00
9 changed files with 1797 additions and 80 deletions

View File

@@ -11,12 +11,13 @@ class FreeBedsChartView {
if (this.#chart != null) { if (this.#chart != null) {
this.#chart.destroy(); this.#chart.destroy();
} }
const label = 'Anteil freier Betten';
this.#chart = new Chart( this.#chart = new Chart(
this.#canvas, this.#canvas,
{ {
type: 'line', type: 'line',
data: this.#getData(data), data: this.#getData(data, label),
options: this.#getOptions(title), options: this.#getOptions(title, label),
plugins: [this.#getBackgroundTrafficLightsPlugin()] plugins: [this.#getBackgroundTrafficLightsPlugin()]
}); });
} }
@@ -47,11 +48,11 @@ class FreeBedsChartView {
return { beforeDraw: drawTrafficLights }; return { beforeDraw: drawTrafficLights };
} }
#getData(data) { #getData(data, label) {
return { return {
datasets: [ datasets: [
{ {
label: 'Anteil freier Betten', label: label,
data: data, data: data,
parsing: { parsing: {
yAxisKey: 'free_beds_divided_by_all_beds_in_percent' yAxisKey: 'free_beds_divided_by_all_beds_in_percent'
@@ -59,7 +60,7 @@ class FreeBedsChartView {
backgroundColor: 'rgba(0, 0, 150, 1)' backgroundColor: 'rgba(0, 0, 150, 1)'
}, },
{ {
label: 'Median des Anteils freier Betten', label: 'Median der Anteile freier Betten',
data: data, data: data,
parsing: { parsing: {
yAxisKey: 'median_free_beds_in_percent' yAxisKey: 'median_free_beds_in_percent'
@@ -70,7 +71,7 @@ class FreeBedsChartView {
}; };
} }
#getOptions(title) { #getOptions(title, label) {
return { return {
plugins: { plugins: {
title: { title: {
@@ -79,17 +80,7 @@ class FreeBedsChartView {
}, },
tooltip: { tooltip: {
callbacks: { callbacks: {
label: function (context) { label: UIUtils.getLabelWithPercent
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
if (context.parsed.y !== null) {
label += context.parsed.y.toFixed(1) + "%";
}
return label;
}
} }
} }
}, },
@@ -105,17 +96,7 @@ class FreeBedsChartView {
} }
} }
}, },
y: { y: UIUtils.getPercentageScale(label)
min: 0,
max: 100,
title: {
display: true,
text: "Anteil freier Betten"
},
ticks: {
callback: value => value + "%"
}
}
}, },
parsing: { parsing: {
xAxisKey: 'date' xAxisKey: 'date'

View File

@@ -0,0 +1,66 @@
class MedianOfFreeBedsByKreisChartView {
#canvas;
#chart;
constructor(canvas) {
this.#canvas = canvas;
}
displayChart(data) {
if (this.#chart != null) {
this.#chart.destroy();
}
const label = 'Median der Anteile freier Betten';
this.#chart = new Chart(
this.#canvas,
{
type: 'bar',
data: this.#getData(data, label),
options: this.#getOptions(label)
});
}
setData(data) {
this.#chart.config.data.datasets[0].data = data;
this.#chart.update();
}
#getData(data, label) {
return {
datasets: [
{
label: label,
data: data,
parsing: {
yAxisKey: 'median_free_beds_in_percent'
},
backgroundColor: 'rgba(0, 255, 0, 1)'
}
]
};
}
#getOptions(label) {
return {
plugins: {
title: {
display: true,
text: label
},
tooltip: {
callbacks: {
label: UIUtils.getLabelWithPercent
}
}
},
responsive: true,
scales: {
y: UIUtils.getPercentageScale(label)
},
parsing: {
xAxisKey: 'Kreis'
}
};
}
}

View File

@@ -16,4 +16,30 @@ class UIUtils {
static getSelectedOption(selectElement) { static getSelectedOption(selectElement) {
return selectElement.options[selectElement.selectedIndex]; return selectElement.options[selectElement.selectedIndex];
} }
static getLabelWithPercent(context) {
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
if (context.parsed.y !== null) {
label += context.parsed.y.toFixed(1) + "%";
}
return label;
}
static getPercentageScale(label) {
return {
min: 0,
max: 100,
title: {
display: true,
text: label
},
ticks: {
callback: value => value + "%"
}
}
}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -24,6 +24,9 @@
kreisSelect.addEventListener( kreisSelect.addEventListener(
'change', 'change',
event => onKreisOptionSelected(kreisSelect, intensiveCareCapacitiesChartView, freeBedsChartView)); event => onKreisOptionSelected(kreisSelect, intensiveCareCapacitiesChartView, freeBedsChartView));
displayMedianOfFreeBedsByKreisChart(
document.getElementById('medianOfFreeBedsByKreis'),
document.getElementById('slider'));
}); });
function onKreisOptionSelected(kreisSelect, intensiveCareCapacitiesChartView, freeBedsChartView) { function onKreisOptionSelected(kreisSelect, intensiveCareCapacitiesChartView, freeBedsChartView) {

View File

@@ -44,4 +44,34 @@ function add_median_free_beds_in_percent(dataDicts) {
for (const dataDict of dataDicts) { for (const dataDict of dataDicts) {
dataDict["median_free_beds_in_percent"] = median_free_beds_in_percent; dataDict["median_free_beds_in_percent"] = median_free_beds_in_percent;
} }
} }
function displayMedianOfFreeBedsByKreisChart(canvas, slider) {
fetch(`data/intensivstationen/medianOfFreeBedsByKreisTable.json`)
.then(response => response.json())
.then(json => _displayMedianOfFreeBedsByKreisChart(canvas, slider, json));
}
function _displayMedianOfFreeBedsByKreisChart(canvas, sliderElement, data) {
const medianOfFreeBedsByKreisChartView = new MedianOfFreeBedsByKreisChartView(canvas);
medianOfFreeBedsByKreisChartView.displayChart(data);
createSlider(
sliderElement,
{
min: 0,
max: data.length - 1
},
values => medianOfFreeBedsByKreisChartView.setData(data.slice(values[0], values[1] + 1)));
}
function createSlider(sliderElement, range, onUpdate) {
noUiSlider.create(
sliderElement,
{
start: [range.min, range.max],
connect: true,
range: range,
step: 1,
});
sliderElement.noUiSlider.on('update', onUpdate);
}

View File

@@ -806,14 +806,6 @@
" country = 'Global',\n", " country = 'Global',\n",
" minADRsForLethality = minADRsForLethality)" " minADRsForLethality = minADRsForLethality)"
] ]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9c70f2db",
"metadata": {},
"outputs": [],
"source": []
} }
], ],
"metadata": { "metadata": {

View File

@@ -15,32 +15,12 @@ get VAERS data:
- download data (e.g. 2022VAERSData.zip) from https://vaers.hhs.gov/data/datasets.html and save and unzip in VAERS folder - download data (e.g. 2022VAERSData.zip) from https://vaers.hhs.gov/data/datasets.html and save and unzip in VAERS folder
FK-FIXME: FK-FIXME:
- Fcron funktioniert nicht, jobs werden im Nachhinein nicht ausgeführt, verwende Autocron statt Fcron
FK-TODO: FK-TODO:
- Format des jeweiligen Herstellers berücksichtigen: - Darstellung als Dashboard, siehe https://covid-karte.de/ oder https://experience.arcgis.com/experience/3a132983ad3c4ab8a28704e9addefaba
039k20a - Rot-Gelb-Grün eingefärbte Deutschland-Karte anzeigen wie in https://experience.arcgis.com/experience/3a132983ad3c4ab8a28704e9addefaba
MOD039K20A - Alle Charts mit Slidern versehen?
#039K20A
039K20A-MODERNA
039K20A-2A (vielleicht nicht)
039K20A or 039L
Moderna/039K20A
MODERNA 039K20A
MODERNA039K20A
Modena 039k20A
L039K20A
M039K20A
MOD; 039K20A
m0039k20A
u039k20a
6/21 039K20A
2039K20A
013L20A 039K20A#039K20A
#039K
039K20A 12-31-
039K20A & 031M2
039K20A and 032
039K20A, 011L20
man 5 fcrontab man 5 fcrontab

View File

@@ -838,6 +838,30 @@
" return BeautifulSoup(html, 'lxml')\n" " return BeautifulSoup(html, 'lxml')\n"
] ]
}, },
{
"cell_type": "code",
"execution_count": null,
"id": "29b0930a",
"metadata": {},
"outputs": [],
"source": [
"import unittest"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "45072a1d",
"metadata": {},
"outputs": [],
"source": [
"class TestHelper:\n",
"\n",
" @staticmethod\n",
" def createDataFrame(index, columns, data, dtypes = {}):\n",
" return pd.DataFrame(index = index, columns = columns, data = data).astype(dtypes)\n"
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
@@ -845,9 +869,6 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"import unittest\n",
"\n",
"\n",
"class KreisOptionsSetterTest(unittest.TestCase):\n", "class KreisOptionsSetterTest(unittest.TestCase):\n",
"\n", "\n",
" def test_setKreisOptions(self):\n", " def test_setKreisOptions(self):\n",
@@ -856,7 +877,7 @@
"\n", "\n",
" # When\n", " # When\n",
" htmlActual = kreisOptionsSetter.setKreisOptions(\n", " htmlActual = kreisOptionsSetter.setKreisOptions(\n",
" html = '''\n", " html='''\n",
" <html>\n", " <html>\n",
" <body>\n", " <body>\n",
" <p>Test<p/>\n", " <p>Test<p/>\n",
@@ -869,7 +890,7 @@
" </body>\n", " </body>\n",
" </html>\n", " </html>\n",
" ''',\n", " ''',\n",
" options = [\n", " options=[\n",
" '<option selected=\"\" value=\"de\">Alle Landkreise</option>',\n", " '<option selected=\"\" value=\"de\">Alle Landkreise</option>',\n",
" '<option value=\"Ahrweiler\">Ahrweiler</option>',\n", " '<option value=\"Ahrweiler\">Ahrweiler</option>',\n",
" '<option value=\"Aichach-Friedberg\">Aichach-Friedberg</option>'])\n", " '<option value=\"Aichach-Friedberg\">Aichach-Friedberg</option>'])\n",
@@ -891,6 +912,8 @@
" ''')\n", " ''')\n",
"\n", "\n",
"# adapted from https://stackoverflow.com/questions/8006909/pretty-print-assertequal-for-html-strings\n", "# adapted from https://stackoverflow.com/questions/8006909/pretty-print-assertequal-for-html-strings\n",
"\n",
"\n",
"def assertEqualHTML(string1, string2, file1='', file2=''):\n", "def assertEqualHTML(string1, string2, file1='', file2=''):\n",
" u'''\n", " u'''\n",
" Compare two unicode strings containing HTML.\n", " Compare two unicode strings containing HTML.\n",
@@ -920,16 +943,6 @@
" raise Exception('Not equal %s %s' % (file1, file2))\n" " raise Exception('Not equal %s %s' % (file1, file2))\n"
] ]
}, },
{
"cell_type": "code",
"execution_count": null,
"id": "e80117e5",
"metadata": {},
"outputs": [],
"source": [
"unittest.main(argv = [''], verbosity = 2, exit = False)"
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
@@ -1359,6 +1372,7 @@
"outputs": [], "outputs": [],
"source": [ "source": [
"import os\n", "import os\n",
"import json\n",
"\n", "\n",
"\n", "\n",
"class IOUtils:\n", "class IOUtils:\n",
@@ -1389,7 +1403,7 @@
" return timeSeries.groupby('date').agg(**{\n", " return timeSeries.groupby('date').agg(**{\n",
" 'betten_belegt': pd.NamedAgg(column = 'betten_belegt', aggfunc = 'sum'),\n", " 'betten_belegt': pd.NamedAgg(column = 'betten_belegt', aggfunc = 'sum'),\n",
" 'betten_frei': pd.NamedAgg(column = 'betten_frei', aggfunc = 'sum'),\n", " 'betten_frei': pd.NamedAgg(column = 'betten_frei', aggfunc = 'sum'),\n",
" 'Einwohnerzahl': pd.NamedAgg(column = 'Einwohnerzahl', aggfunc = 'sum') \n", " 'Einwohnerzahl': pd.NamedAgg(column = 'Einwohnerzahl', aggfunc = 'sum')\n",
" }).reset_index()" " }).reset_index()"
] ]
}, },
@@ -1400,9 +1414,6 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"import json\n",
"\n",
"\n",
"def getAndPersistIntensiveCareBeds(timeSeries, kreis = None):\n", "def getAndPersistIntensiveCareBeds(timeSeries, kreis = None):\n",
" intensiveCareBeds = getIntensiveCareBeds(timeSeries, kreis)\n", " intensiveCareBeds = getIntensiveCareBeds(timeSeries, kreis)\n",
" display(kreis)\n", " display(kreis)\n",
@@ -5052,6 +5063,96 @@
" getAndPersistIntensiveCareBeds(timeSeries, kreis)" " getAndPersistIntensiveCareBeds(timeSeries, kreis)"
] ]
}, },
{
"cell_type": "code",
"execution_count": null,
"id": "d9d4acab",
"metadata": {},
"outputs": [],
"source": [
"class MedianOfFreeBedsByKreisTableFactory:\n",
" \n",
" def __init__(self, dataFrame):\n",
" self.dataFrame = dataFrame\n",
"\n",
" def createMedianOfFreeBedsByKreisTable(self):\n",
" self.dataFrame['free_beds_divided_by_all_beds_in_percent'] = self.dataFrame['betten_frei'] / (self.dataFrame['betten_frei'] + self.dataFrame['betten_belegt']) * 100\n",
" aggregated = self.dataFrame.groupby('Kreis').agg(\n",
" median_free_beds_in_percent =\n",
" pd.NamedAgg(\n",
" column = 'free_beds_divided_by_all_beds_in_percent',\n",
" aggfunc = 'median'))\n",
" return aggregated.sort_values(by = 'median_free_beds_in_percent', ascending = False)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a739d4d1",
"metadata": {},
"outputs": [],
"source": [
"from pandas.testing import assert_frame_equal\n",
"import statistics\n",
"\n",
"class MedianOfFreeBedsByKreisTableFactoryTest(unittest.TestCase):\n",
"\n",
" def test_createMedianOfFreeBedsByKreisTable(self):\n",
" # Given\n",
" dataFrame = TestHelper.createDataFrame(\n",
" columns = ['date', 'betten_frei', 'betten_belegt', 'Kreis'],\n",
" data = [ ['2020-04-24', 40, 38, 'Flensburg, Stadt'],\n",
" ['2020-04-24', 42, 36, 'Flensburg, Stadt'],\n",
" ['2020-04-24', 44, 34, 'Flensburg, Stadt'],\n",
" ['2020-04-24', 9, 10, 'Bamberg']],\n",
" index = [\n",
" 0,\n",
" 1,\n",
" 2,\n",
" 3])\n",
" medianOfFreeBedsByKreisTableFactory = MedianOfFreeBedsByKreisTableFactory(dataFrame)\n",
" \n",
" # When\n",
" medianOfFreeBedsByKreisTable = medianOfFreeBedsByKreisTableFactory.createMedianOfFreeBedsByKreisTable()\n",
"\n",
" # Then\n",
" assert_frame_equal(\n",
" medianOfFreeBedsByKreisTable,\n",
" TestHelper.createDataFrame(\n",
" columns = ['median_free_beds_in_percent'],\n",
" data = [ [statistics.median([40/(40 + 38) * 100, 42/(42 + 36) * 100, 44/(44 + 34) * 100])],\n",
" [9/(9 + 10) * 100]],\n",
" index = pd.Index(\n",
" name = 'Kreis',\n",
" data = [\n",
" 'Flensburg, Stadt',\n",
" 'Bamberg'\n",
" ])),\n",
" check_dtype = False)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "af22cdc5",
"metadata": {},
"outputs": [],
"source": [
"unittest.main(argv = [''], verbosity = 2, exit = False)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f10704f3",
"metadata": {},
"outputs": [],
"source": [
"medianOfFreeBedsByKreisTableFactory = MedianOfFreeBedsByKreisTableFactory(timeSeries)\n",
"medianOfFreeBedsByKreisTable = medianOfFreeBedsByKreisTableFactory.createMedianOfFreeBedsByKreisTable()\n",
"medianOfFreeBedsByKreisTable.reset_index().to_json('../../docs/data/intensivstationen/medianOfFreeBedsByKreisTable.json', orient = \"records\")"
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,