1
0
Fork 0

feat: Created dashboard and reworked folder structure

This commit is contained in:
Benedikt Bastin 2021-01-17 02:34:49 +01:00
parent 7cba369103
commit 38a9cb39e8
7 changed files with 300 additions and 16 deletions

122
dashboard_template.xhtml Normal file
View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8" ?>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de">
<head>
<title></title>
<style>
<![CDATA[
]]>
</style>
<link rel="stylesheet" type="text/css" href="rki-dashboard.css" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<h1>Dashboard</h1>
<h2>
Quelle: <a href="https://www.rki.de/DE/Content/InfAZ/N/Neuartiges_Coronavirus/Daten/Impfquotenmonitoring.xlsx?__blob=publicationFile">Robert-Koch-Institut (RKI)</a><br />
Stand: {{ stand }}
</h2>
<section>
<p class="data-text">
Bislang wurden <em>{{ total_vaccinations }}</em> Impfungen<sup><a href="#footnote-001">1</a></sup> innerhalb von <em>{{ days_since_start }}</em> Tagen<sup><a href="#footnote-002">2</a></sup> vorgenommen.
Das entspricht einem Anteil von <em>{{ total_vaccinations_percentage }} %</em> der Bevölkerung<sup><a href="#footnote-003">3</a></sup>.
</p>
<p class="data-text">
</p>
<p class="data-text">
Durchschnittlich fanden seit Start täglich <em>{{ mean_vaccinations_daily }}</em> Impfungen statt.
Mit dieser durschnittlichen Rate dauert es bis zum <em>{{ mean_vaccinations_daily_herd_immunity }}</em>, bis {{ herd_immunity }} %<sup><a href="#footnote-004">4</a></sup> der Bevölkerung die erste Impfung erhalten haben,
und bis zum <em>{{ mean_vaccinations_daily_done }}</em> für 100 %.
</p>
<p class="data-text">
Am <em>{{ last_date }}</em> wurden <em>{{ last_date_day_rate }}</em> Impfungen vorgenommen.
Mit der Rate vom <em>{{ last_date }}</em> dauert es bis zum <em>{{ last_date_day_rate_herd_immunity }}</em> für {{ herd_immunity }} % und bis zum <em>{{ last_date_day_rate_done }}</em> für 100 %<sup><a href="#footnote-005">5</a></sup>.
</p>
<p class="data-text">
In den letzten sieben Tagen wurden <em>{{ mean_vaccinations_last_seven_days }}</em> Impfungen vorgenommen.
Mit dieser Rate dauert es bis zum <em>{{ mean_vaccinations_last_seven_days_herd_immunity }}</em> für {{ herd_immunity }} % und bis zum <em>{{ mean_vaccinations_last_seven_days_done }}</em> für 100 %.
</p>
</section>
<section>
<h2>Was ist dieses Dashboard?</h2>
<p>
Dieses Dashboard soll einen Überblick über den aktuellen Stand sowie die Entwicklungen der Impfungen in Deutschland geben.
Inspiriert wurde es durch <a href="https://impfdashboard.de/">impfdashboard.de</a>, ein Impfdashboard, das ebenfalls die Daten des Robert-Koch-Instituts auswertet und vom Bundesministerium für Gesundheit betrieben wird.
</p>
<p>
Als zusätzliche Informationen rechnet es aber aus, wie lange es dauert könnte, bis die Impfungen abgeschlossen sind oder wenigstens eine Herdenimmunität erreicht wird (sofern die aktuelle Schätzung der für eine Herdenimmunität benötigten Bevölkerung korrekt ist).
Dafür wird einerseits die durchschnittliche Impfrate aller Tage und andererseits die Impfrate der letzten sieben Tage herangezogen, die noch zu impfende Bevölkerung ausgerechnet und diese durch die Impfrate geteilt.
Dadurch erhält man die lineare Extrapolation, wenn jeden weiteren Tag genau mit dieser Impfrate weitergeimpft würde.
</p>
</section>
<section>
<h2>Wie genau sind die Daten?</h2>
<p>
Die Datenquelle ist das Robert-Koch-Institut.
Für die Meldungen der jeweiligen Tage gibt es immer etwas Meldeverzug, das heißt, dass teilweise Meldungen einige Tage im Nachhinein noch korrigiert werden.
Das allerdings für gewöhnlich nur nach oben, und bislang hat das auch nur wenig Unterschied verursacht.
</p>
<p>
Für die Extrapolation sieht das etwas anders aus.
Die lineare Extrapolation nimmt natürlich an, dass sich an dem Vorgang des Impfens über einen längeren Zeitraum nichts signifikant ändert.
Diese Annahme ist aber falsch;
aktuell (Stand: 17. Januar 2021) sind die meisten Impfzentren beispielsweise noch nicht geöffnet, stattdessen sind mobile Impfteams unterwegs, die hauptsächlich die Altenheime besuchen.
Dadurch liegt die aktuelle Impfrate deutlich unter dem, was bei den Impfzentren unter voller Last zu erwarten ist.
Entsprechend darf man vorsichtig optimistisch hoffen, dass die Impfrate in den nächsten Wochen und Monaten noch stark ansteigen wird.
Dennoch wird es vermutlich ungefähr anderthalb Jahre dauern, bis die meisten Menschen in Deutschland geimpft wurden.
</p>
<p>
Ich finde den Ausblick, auch wenn die Schätzung sehr unpräzise sein wird, dennoch interessant, deshalb habe ich das gemacht.
</p>
</section>
<figure>
<a href="{{ filename_stand }}_extrapolated_to_10_percent.png">
<img
src="{{ filename_stand }}_extrapolated_to_10_percent.png"
alt="" />
<figcaption>
<a name="figure-001"><span class="ref">Abbildung 1:</span></a>
Tägliche Impfquote, kumulierte Impfungen und lineare Extrapolation bis 10 % der Bevölkerung Deutschlands
</figcaption>
</a>
</figure>
<figure>
<a href="{{ filename_stand }}_extrapolated_to_70_percent.png">
<img
src="{{ filename_stand }}_extrapolated_to_70_percent.png"
alt="" />
<figcaption>
<a name="figure-002"><span class="ref">Abbildung 2:</span></a>
Tägliche Impfquote, kumulierte Impfungen und lineare Extrapolation bis 70 % der Bevölkerung Deutschlands
</figcaption>
</a>
</figure>
<figure>
<a href="{{ filename_stand }}_extrapolated_to_100_percent.png">
<img
src="{{ filename_stand }}_extrapolated_to_100_percent.png"
alt="" />
<figcaption>
<a name="figure-003"><span class="ref">Abbildung 3:</span></a>
Tägliche Impfquote, kumulierte Impfungen und lineare Extrapolation bis 100 % der Bevölkerung Deutschlands
</figcaption>
</a>
</figure>
<section>
<h3>Fußnoten</h3>
<ol>
<li><a name="footnote-001">Gezählt werden verabreichte Dosen, unabhängig ob erste oder zweite Dosis.</a></li>
<li><a name="footnote-002">Starttermin der Impfungen war der 27. Dezember 2020.</a></li>
<li><a name="footnote-003">Bevölkerungsstand vom 31. Dezember 2019: {{ einwohner_deutschland }}.</a></li>
<li><a name="footnote-004">{{ herd_immunity }} % ist der Wert, bei dem aktuell von einer Herdenimmunität ausgegangen wird.</a></li>
<li><a name="footnote-005">Die täglichen Impfraten unterliegen starken Schwankungen und sind daher wenig aussagekräftig.</a></li>
</ol>
</section>
</body>
</html>

View File

129
plot.py
View File

@ -10,14 +10,15 @@ import re
import requests as req
import locale
import os.path
import shutil
locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8')
sources_folder = 'Quellen'
plots_folder = 'Plots'
site_folder = 'site/'
data_folder = 'data/'
einwohner_deutschland = 83190556
herd_immunity = 0.7
today = datetime.date.today()
print_today = today.isoformat()
@ -30,17 +31,18 @@ plt.rcParams["figure.figsize"] = [11.69, 8.27]
# Download
filename = '{}/{}_Impfquotenmonitoring.xlsx'.format(sources_folder, filename_now)
data_filename = '{}/{}_Impfquotenmonitoring.xlsx'.format(data_folder, filename_now)
r = req.get('https://www.rki.de/DE/Content/InfAZ/N/Neuartiges_Coronavirus/Daten/Impfquotenmonitoring.xlsx?__blob=publicationFile')
with open(filename, 'wb') as outfile:
with open(data_filename, 'wb') as outfile:
outfile.write(r.content)
rki_file = pd.read_excel(filename, sheet_name=None, engine='openpyxl')
rki_file = pd.read_excel(data_filename, sheet_name=None, engine='openpyxl')
raw_data = rki_file['Impfungen_proTag']
@ -61,10 +63,41 @@ days_extrapolated = int(np.ceil(to_be_vaccinated / mean_vaccinations_daily))
extrapolated_dates = np.array([dates[0] + datetime.timedelta(days=i) for i in range(days_extrapolated)])
mean_vaccinations_daily_done = extrapolated_dates[-1]
mean_vaccinations_daily_herd_immunity = extrapolated_dates[int(np.ceil(days_extrapolated * herd_immunity))]
days_extrapolated_with_todays_rate = int(np.ceil(to_be_vaccinated / daily.iloc[-1]))
last_date = dates.iloc[-1]
last_date_day_rate = daily.iloc[-1]
last_date_day_rate_done = dates[0] + datetime.timedelta(days=days_extrapolated_with_todays_rate)
last_date_day_rate_herd_immunity = dates[0] + datetime.timedelta(days=int(np.ceil(days_extrapolated_with_todays_rate * herd_immunity)))
extrapolated_vaccinations = mean_vaccinations_daily * range(1, days_extrapolated + 1)
days_since_start = (dates.iloc[-1].date() - dates[0].date()).days
print(days_since_start)
mean_vaccinations_last_seven_days = np.mean(daily[-7:])
mean_vaccinations_last_seven_days_int = int(np.round(mean_vaccinations_last_seven_days))
days_extrapolated_last_seven_days = int(np.ceil(to_be_vaccinated / mean_vaccinations_last_seven_days))
extrapolated_vaccinations_last_seven_days = total_vaccinations + mean_vaccinations_last_seven_days * range(-days_since_start, days_extrapolated - days_since_start)
mean_vaccinations_last_seven_days_done = dates.iloc[-1] + datetime.timedelta(days=days_extrapolated_last_seven_days)
mean_vaccinations_last_seven_days_herd_immunity = dates.iloc[-1] + datetime.timedelta(days=int(np.ceil(days_extrapolated_last_seven_days * herd_immunity)))
mean_vaccinations_daily_up_to_date = np.round(cumulative / range(1, len(cumulative) + 1))
print(extrapolated_vaccinations[:20])
print(extrapolated_vaccinations_last_seven_days[:20])
print(cumulative)
# Stand aus Daten auslesen
#stand = dates.iloc[-1]
#print_stand = stand.isoformat()
@ -79,14 +112,22 @@ print_stand = stand_date.isoformat()
filename_stand = stand_date.strftime("%Y%m%d%H%M%S")
archive_folder = site_folder + 'archive/' + filename_stand
if os.path.isdir(archive_folder):
print('Archive folder {} already exists'.format(archive_folder))
else:
os.mkdir(archive_folder)
def plot_extrapolation_portion(percentage):
print_percentage = int(percentage * 100)
plot_filename = '{}/{}_extrapolated_to_{}_percent'.format(plots_folder, filename_stand, print_percentage)
plot_filename_latest = '{}/latest_extrapolated_to_{}_percent'.format(plots_folder, print_percentage)
archive_plot_filename = '{}/extrapolated_to_{}_percent'.format(archive_folder, print_percentage)
latest_plot_filename = '{}/extrapolated_to_{}_percent'.format(site_folder, print_percentage)
if os.path.isfile(plot_filename + 'pdf'):
print('Plot {} already exists'.format(plot_filename))
if os.path.isfile(archive_plot_filename + '.pdf'):
print('Plot {} already exists'.format(archive_plot_filename))
return
fig, ax = plt.subplots(1)
@ -111,7 +152,8 @@ def plot_extrapolation_portion(percentage):
ax2.set_xlim(xmax=dates[0] + datetime.timedelta(days=percentage * days_extrapolated))
ax2.grid(True)
ax2.plot(dates, cumulative, color='red', label='Kumulierte Impfungen')
ax2.plot(extrapolated_dates, extrapolated_vaccinations, color='orange', label='Extrap. kumulierte Impfungen\n({:n} Impfungen/Tag)'.format(mean_vaccinations_daily_int))
ax2.plot(extrapolated_dates, extrapolated_vaccinations, color='orange', label='Extrap. kumulierte Impfungen (Øgesamt)\n{:n} Impfungen/Tag'.format(mean_vaccinations_daily_int))
ax2.plot(extrapolated_dates, extrapolated_vaccinations_last_seven_days, color='goldenrod', label='Extrap. kumulierte Impfungen (Ø7 Tage)\n{:n} Impfungen/Tag'.format(mean_vaccinations_last_seven_days_int))
#ax2.plot()
ax.legend(loc='upper left')
@ -128,14 +170,69 @@ def plot_extrapolation_portion(percentage):
ax2.set_ylabel('Kumulierte Impfungen')
plt.savefig(plot_filename + '.pdf')
plt.savefig(plot_filename + '.png')
plt.savefig(plot_filename_latest + '.pdf')
plt.savefig(plot_filename_latest + '.png')
plt.savefig(archive_plot_filename + '.pdf')
plt.savefig(archive_plot_filename + '.png')
plt.savefig(latest_plot_filename + '.pdf')
plt.savefig(latest_plot_filename + '.png')
plt.close()
print('Created plot {} as pdf and png'.format(plot_filename))
print('Created plot {} as pdf and png'.format(archive_plot_filename))
plot_extrapolation_portion(0.1)
plot_extrapolation_portion(0.7)
plot_extrapolation_portion(1.0)
def render_dashboard():
dashboard_filename = 'site/index.xhtml'
dashboard_archive_filename = 'site/archive/{}/index.xhtml'.format(filename_stand)
stylesheet_filename = 'site/rki-dashboard.css'
stylesheet_archive_filename = 'site/archive/{}/rki-dashboard.css'.format(filename_stand)
if os.path.isfile(dashboard_archive_filename):
print('Dashboard {} already exists'.format(dashboard_archive_filename))
return
from jinja2 import Template, Environment, FileSystemLoader, select_autoescape
env = Environment(
loader=FileSystemLoader('./'),
autoescape=select_autoescape(['html', 'xml', 'xhtml'])
)
german_text_date_format = '%d. %B %Y'
df = german_text_date_format
german_text_datetime_format = '%d. %B %Y, %H:%M:%S Uhr'
dtf = german_text_datetime_format
latest_dashboard_filename = site_folder + 'index.xhtml'
archive_dashboard_filename = archive_folder
template = env.get_template('dashboard_template.xhtml')
template.stream(
stand = stand_date.strftime(dtf),
filename_stand = filename_stand,
einwohner_deutschland = '{:n}'.format(einwohner_deutschland).replace('.', ''),
herd_immunity = '{:n}'.format(int(herd_immunity * 100)),
total_vaccinations = '{:n}'.format(total_vaccinations).replace('.', ''),
total_vaccinations_percentage = '{:.3n}'.format(total_vaccinations_percentage * 100),
days_since_start = days_since_start,
last_date = last_date.strftime(df),
last_date_day_rate = '{:n}'.format(last_date_day_rate).replace('.', ''),
mean_vaccinations_daily = '{:n}'.format(mean_vaccinations_daily_int).replace('.', ''),
mean_vaccinations_daily_herd_immunity = mean_vaccinations_daily_herd_immunity.strftime(df),
mean_vaccinations_daily_done = mean_vaccinations_daily_done.strftime(df),
last_date_day_rate_herd_immunity = last_date_day_rate_herd_immunity.strftime(df),
last_date_day_rate_done = last_date_day_rate_done.strftime(df),
mean_vaccinations_last_seven_days = '{:n}'.format(mean_vaccinations_last_seven_days_int).replace('.', ''),
mean_vaccinations_last_seven_days_herd_immunity = mean_vaccinations_last_seven_days_herd_immunity.strftime(df),
mean_vaccinations_last_seven_days_done = mean_vaccinations_last_seven_days_done.strftime(df)
).dump('site/index.xhtml')
shutil.copyfile(dashboard_filename, dashboard_archive_filename)
shutil.copyfile(stylesheet_filename, stylesheet_archive_filename)
print('Created dashboard')
render_dashboard()

View File

@ -3,3 +3,5 @@ matplotlib
pandas
openpyxl
requests
jinja2

4
site/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*
!.gitignore
!archive

59
site/rki-dashboard.css Normal file
View File

@ -0,0 +1,59 @@
body {
font-family: Noto Sans;
max-width: 1000px;
margin: 0px auto;
padding: 0.4em;
hyphens: auto;
}
h1, h2, h3 {
font-weight: normal;
}
a {
text-decoration: none;
}
p {
text-align: justify;
}
.data-text {
color: #666666;
font-size: 2em;
font-weight: normal;
line-height: 1.75em;
}
.data-text em {
font-weight: inherit;
font-style: inherit;
/*color: #ffac12;*/
color: #333333;
/*background-image: linear-gradient(rgba(255, 255, 255, 0) 90%, #f7bb3c 91%, #f7bb3c 98%, rgba(255, 255, 255, 0) 99%);*/
text-decoration: underline;
text-decoration-color: #ffac12;
}
sup {
line-height: 0;
}
figure a {
color: inherit;
}
figure img {
width: 90%;
height: auto;
}
figure figcaption {
}
figure figcaption .ref {
font-weight: bolder;
}