diff --git a/corona_statistik.py b/corona_statistik.py new file mode 100644 index 0000000..025d320 --- /dev/null +++ b/corona_statistik.py @@ -0,0 +1,96 @@ +import toml +import os +import db_model as db +from peewee import PostgresqlDatabase +import datetime +from sshtunnel import SSHTunnelForwarder +import verdoppelungsrate +from dataclasses import dataclass +from dataclasses_json import dataclass_json +import scrape_bev + + + +@dataclass_json +@dataclass +class CoronaStatistik: + ts: datetime.datetime + quelle: str + country_region: str + typ: str + wert: float + + +def config_laden(): + configfile = os.path.join(SKRIPTPFAD, "config.toml") + with open(configfile) as file: + return toml.loads(file.read()) + + +SKRIPTPFAD = os.path.abspath(os.path.dirname(__file__)) +CONFIG = config_laden() + + +def verdoppelungsrate_bearbeiten(quelle, country): + letzter_ts = verdoppelungsrate.letzten_eintrag_ermitteln(quelle, country) + if letzter_ts is None: + letzter_ts = datetime.datetime(1970, 1, 1) + daten = verdoppelungsrate.letzte_verdopplungsraten_berechnen(quelle, country, letzter_ts) + return daten + + +def alle_eintraege_abarbeiten(): + daten = [] + bev_zahlen = {} + query_quelle = db.CoronaDaten.select(db.CoronaDaten.quelle).group_by(db.CoronaDaten.quelle) + for data in query_quelle: + if data.quelle == "rki_landkreis": + continue + query_country = db.CoronaDaten.select(db.CoronaDaten.country_region).where( + db.CoronaDaten.quelle == data.quelle).group_by(db.CoronaDaten.country_region) + for datensatz in query_country: + daten_verdoppelungsrate = verdoppelungsrate_bearbeiten(data.quelle, datensatz.country_region) + if daten_verdoppelungsrate: + daten.extend(daten_verdoppelungsrate) + if datensatz.country_region not in bev_zahlen: + bev_zahlen[datensatz.country_region] = scrape_bev.scrape_bev(datensatz.country_region) + if bev_zahlen[datensatz.country_region]: + daten_bev_prozent = scrape_bev.auslesen_unverarbeiteter_daten(data.quelle, + datensatz.country_region, bev_zahlen, + CoronaStatistik) + else: + daten_bev_prozent = None + if daten_bev_prozent: + daten.extend(daten_bev_prozent) + + if len(daten) > 5000: + print("Sende Daten") + daten_senden(daten) + daten = [] + return daten + + +def daten_senden(daten): + daten_dict = [] + for data in daten: + daten_dict.append(data.to_dict()) + db.CoronaStatistik.insert_many(daten_dict).execute() + + +def main(): + with SSHTunnelForwarder( + (CONFIG["ssh"]["ip_server"], CONFIG["ssh"]["ssh_port"]), ssh_username=CONFIG["ssh"]["user"], + ssh_password=CONFIG["ssh"]["pw"], remote_bind_address=('127.0.0.1', CONFIG["pg"]["pgport"])) as server: + + db.database.initialize(PostgresqlDatabase(CONFIG["pg"]["pgdb"], + user=CONFIG["pg"]["pguser"], password=CONFIG["pg"]["pgpw"], + host="127.0.0.1", + port=server.local_bind_port)) + db.create_tables() + daten = alle_eintraege_abarbeiten() + daten_senden(daten) + db.Database.close(db.database) + + +if __name__ == "__main__": + main() diff --git a/db_model.py b/db_model.py index 99c7b24..7b32222 100644 --- a/db_model.py +++ b/db_model.py @@ -38,7 +38,7 @@ class CoronaStatistik(BaseModel): def create_tables(): - database.create_tables([CoronaDaten]) + database.create_tables([CoronaDaten, CoronaStatistik]) diff --git a/db_model_postgrest.py b/db_model_postgrest.py index 198a7d0..4d1c774 100644 --- a/db_model_postgrest.py +++ b/db_model_postgrest.py @@ -17,20 +17,18 @@ class CoronaDaten: active: int -@dataclass_json -@dataclass -class CoronaCountry: - country_region: str - quelle: str - - def sende_daten(url, table, headers, daten): url = f"{url}{table}" - for data in daten: - try: - r = requests.post(url, headers=headers, json=data.to_dict()) - except AttributeError: - r = requests.post(url, headers=headers, data=data) + try: + r = requests.post(url, headers=headers, json=[data.to_dict() for data in daten]) + except AttributeError: + for data in daten: + try: + r = requests.post(url, headers=headers, json=data.to_dict()) + except AttributeError: + r = requests.post(url, headers=headers, data=data) + status_auswerten(r, daten) + else: status_auswerten(r, daten) diff --git a/scrape_bev.py b/scrape_bev.py index 95e0aa1..a2c1504 100644 --- a/scrape_bev.py +++ b/scrape_bev.py @@ -1,16 +1,50 @@ import requests from bs4 import BeautifulSoup +import db_model as db +import datetime -countrys = ["Germany"] +TYP = "prozent_bev" URL = "https://countrymeters.info/en/" +def letzten_eintrag_ermitteln(quelle, country_region): + ts = db.CoronaStatistik.select( + db.CoronaStatistik.ts).where((db.CoronaStatistik.typ == TYP) & + (db.CoronaStatistik.quelle == quelle) & + (db.CoronaStatistik.country_region == country_region) + ).order_by(db.CoronaStatistik.ts.desc()).limit(1).scalar() + if ts is None: + ts = datetime.datetime(1970, 1, 1) + return ts + + +def auslesen_unverarbeiteter_daten(quelle, land, bev_zahl, CoronaStatistik): + ts = letzten_eintrag_ermitteln(quelle, land) + daten = [] + query = db.CoronaDaten.select(db.CoronaDaten.ts, db.CoronaDaten.confirmed).where( + (db.CoronaDaten.quelle == quelle) & (db.CoronaDaten.country_region == land) & ( + db.CoronaDaten.ts > ts)).namedtuples() + for datensatz in query: + prozent_bev = datensatz.confirmed / bev_zahl[land] * 100 + prozent_bev = round(prozent_bev, 8) + print(f"Im Land {land} {prozent_bev}% infiziert") + daten.append(CoronaStatistik(datensatz.ts, quelle, land, TYP, prozent_bev)) + return daten + + def scrape_bev(land): r = requests.get(f"{URL}{land}") soup = BeautifulSoup(r.text, features="html5lib") table = soup.find("div", {"id": "cp1"}) - print(table) + try: + value = table.get_text() + except AttributeError: + value = None + else: + value = int(value.replace(",", "")) + return value -scrape_bev("Germany") +if __name__ == "__main__": + scrape_bev("Germany") diff --git a/scrape_corona.py b/scrape_corona.py index 38a4df2..1266801 100644 --- a/scrape_corona.py +++ b/scrape_corona.py @@ -11,6 +11,11 @@ https://services.arcgis.com/5T5nSi527N4F7luB/arcgis/rest/services/Cases_by_count Deutschland RKI: https://experience.arcgis.com/experience/478220a4c454480e823b17327b2bf1d4 https://services7.arcgis.com/mOBPykOjAyBO2ZKk/arcgis/rest/services/Coronaf%C3%A4lle_in_den_Bundesl%C3%A4ndern/FeatureServer/0/query?f=json&where=1%3D1&returnGeometry=false&spatialRel=esriSpatialRelIntersects&outFields=*&orderByFields=faelle_100000_EW%20desc&resultOffset=0&resultRecordCount=50&cacheHint=true + +Landkreis RKI +https://experience.arcgis.com/experience/478220a4c454480e823b17327b2bf1d4/page/page_1/ +https://services7.arcgis.com/mOBPykOjAyBO2ZKk/arcgis/rest/services/RKI_Landkreisdaten/FeatureServer/0/query?f=json&where=1%3D1&returnGeometry=false&spatialRel=esriSpatialRelIntersects&outFields=*&orderByFields=cases%20desc&resultOffset=0&resultRecordCount=1000&cacheHint=true + """ @@ -32,7 +37,8 @@ QUELLEN = { "rki": "https://services7.arcgis.com/mOBPykOjAyBO2ZKk/arcgis/rest/services/Coronaf%C3%A4lle_in_den_Bundesl%C3" "%A4ndern/FeatureServer/0/query?f=json&where=1%3D1&returnGeometry=false&spatialRel" "=esriSpatialRelIntersects&outFields=*&orderByFields=faelle_100000_EW%20desc&resultOffset=0" - "&resultRecordCount=50&cacheHint=true " + "&resultRecordCount=50&cacheHint=true", + "rki_landkreis": "https://services7.arcgis.com/mOBPykOjAyBO2ZKk/arcgis/rest/services/RKI_Landkreisdaten/FeatureServer/0/query?f=json&where=1%3D1&returnGeometry=false&spatialRel=esriSpatialRelIntersects&outFields=*&orderByFields=cases%20desc&resultOffset=0&resultRecordCount=1000&cacheHint=true" } @@ -110,6 +116,22 @@ def rki_daten_erstellen(quelle, daten): return daten_dict +def rki_landkreis_daten_erstellen(quelle, daten): + daten_dict = {} + for data in daten: + datum = data["attributes"] + daten_dict[datum["county"]] = (db.CoronaDaten( + datetime.datetime.strptime(datum["last_update"], "%d.%m.%Y, %H:%M Uhr"), + datum["county"].lower().capitalize(), + quelle, + datum["cases"], + datum["deaths"], + -1, + -1 + )) + return daten_dict + + def daten_erstellen(quelle, daten): if quelle == "jhu": daten_db = jhu_daten_erstellen(quelle, daten) @@ -117,6 +139,8 @@ def daten_erstellen(quelle, daten): daten_db = who_daten_erstellen(quelle, daten) elif quelle == "rki": daten_db = rki_daten_erstellen(quelle, daten) + elif quelle == "rki_landkreis": + daten_db = rki_landkreis_daten_erstellen(quelle, daten) else: raise ValueError("Quelle nicht bekannt") return daten_db diff --git a/verdoppelungsrate.py b/verdoppelungsrate.py index 0a56dfe..53e1fe4 100644 --- a/verdoppelungsrate.py +++ b/verdoppelungsrate.py @@ -4,7 +4,7 @@ import db_model as db from peewee import PostgresqlDatabase import datetime from sshtunnel import SSHTunnelForwarder -import sys +from corona_statistik import CoronaStatistik def config_laden(): @@ -16,6 +16,16 @@ def config_laden(): SKRIPTPFAD = os.path.abspath(os.path.dirname(__file__)) CONFIG = config_laden() QUELLEN = ["jhu", "who", "rki"] +TYP = "verdoppelungsrate" + + +def letzten_eintrag_ermitteln(quelle, country_region): + ts = db.CoronaStatistik.select( + db.CoronaStatistik.ts).where((db.CoronaStatistik.typ == TYP) & + (db.CoronaStatistik.quelle == quelle) & + (db.CoronaStatistik.country_region == country_region) + ).order_by(db.CoronaStatistik.ts.desc()).limit(1).scalar() + return ts def verdoppelungsrate_ermitteln(quelle, land, confirmed, confirmed_ts): @@ -30,26 +40,24 @@ def verdoppelungsrate_ermitteln(quelle, land, confirmed, confirmed_ts): return verdoppelungstage -def letzte_verdopplungsraten(quelle, land, ts): +def letzte_verdopplungsraten_berechnen(quelle, land, ts): + kein_passender_eintrag = False + daten = [] query = db.CoronaDaten.select(db.CoronaDaten.ts, db.CoronaDaten.confirmed).where( (db.CoronaDaten.quelle == quelle) & (db.CoronaDaten.country_region == land) & (db.CoronaDaten.ts > ts)).dicts() for datensatz in query: verdoppelungsrate = verdoppelungsrate_ermitteln(quelle, land, datensatz["confirmed"], datensatz["ts"]) if verdoppelungsrate is not None: verdoppelungsrate_tage = round(verdoppelungsrate_in_tagen(verdoppelungsrate), 3) - print(f"Verdoppelungsrate am {datensatz['ts']}: {verdoppelungsrate_tage}") - print("Verlasse letzte_Verdoppelungsraten") - - -def jede_verdoppelungsrate(quelle, land): - query = db.CoronaDaten.select(db.CoronaDaten.ts, db.CoronaDaten.confirmed).where( - (db.CoronaDaten.quelle == quelle) & (db.CoronaDaten.country_region == land) - ).order_by(db.CoronaDaten.ts.desc()).namedtuples() - for datensatz in query: - verdoppelungsrate = verdoppelungsrate_ermitteln(quelle, land, datensatz.confirmed, datensatz.ts) - if verdoppelungsrate is not None: - verdoppelungsrate_tage = round(verdoppelungsrate_in_tagen(verdoppelungsrate), 3) - print(f"Verdoppelungsrate am {datensatz.ts}: {verdoppelungsrate_tage}") + print(f"Verdoppelungsrate für {land} ({quelle}) am {datensatz['ts']}: {verdoppelungsrate_tage}") + daten.append(CoronaStatistik(datensatz["ts"], quelle, land, TYP, verdoppelungsrate_tage)) + else: + print(f"Kein passender Eintrag gefunden für {land} ({quelle})") + kein_passender_eintrag = True + if kein_passender_eintrag: + print(f"Setze -1 für {land} ({quelle})") + daten.append(CoronaStatistik(datetime.datetime.now(), quelle, land, TYP, -1)) + return daten def verdoppelungsrate_in_tagen(verdoppelungsrate): @@ -76,9 +84,8 @@ def main(): ).group_by(db.CoronaDaten.country_region) for datensatz in query_country: if data.quelle == "jhu" and datensatz.country_region == "Germany": - letzte_verdopplungsraten(data.quelle, datensatz.country_region, datetime.datetime(2020, 4, 3)) - print("Verlasse schleife") - sys.exit(0) + letzte_verdopplungsraten_berechnen(data.quelle, datensatz.country_region, datetime.datetime(2020, 4, 3)) + db.Database.close(db.database) if __name__ == "__main__":