Datenbank auslesen und neue Tabelle erstellen

Du hast eine Idee für ein Projekt?
Antworten
Wetter_Kalkriese
User
Beiträge: 3
Registriert: Dienstag 12. September 2023, 15:34

Hallo,

ich bin neu hier im Forum und habe bisher noch sehr wenig bis gar keine Erfahrung mit Python.

Ich habe eine Wetterhomepage, auf dem Wetterrechner arbeitet auch eine Software für Gewitter und Blitze.
Die Software erstellt mir regelmäßig eine Datenbank-Datei (db3).
Inhalt ist eine fortlaufende ID, Zeit des Blitzes, Zeit in UTC, Koordinaten in Lat. und Long. und der Typ. (siehe Bild)

Bild

Eine Erklärung gibt es hier, ganz unten am Ende der Seite.
https://astrogenic.com/w3help/Database_storage_V3

Nun hätte ich gerne ein Python-Script das die Datenbank ausliest, filtert und mir eine Blitzstatistik in einer neuen Tabelle /Datei erstellt, die ich dann
auf der Homepage einbinden kann. Dann brauche ich nicht die Datenbank-Dateien auf den Webserver übertragen.

Dabei soll der Bereich jeweils durch die Koordinaten eingegrenzt sein, sodass ich nur die Blitze für eine bestimmte Region sehe,
der Stadt, des Bundeslandes und Gesamt-Deutschland. Und auch für bestimmte Zeiträume. Zudem soll der Typ mit gefiltert/gezählt werden.

Wie ich mir das vorstelle sieht man auf dem Bild.
Bild

Das einzige was schon geschrieben habe, ist das mir die Datei geöffnet wird.

Code: Alles auswählen

import sqlite3

db_pfad = 'Z:\\02\\NGXDS_20230909.db3'
db_verbindung = sqlite3.connect(db_pfad)
db_cursor = db_verbindung.cursor()
db_cursor.execute ('SELECT * FROM NGXLIGHTNING;')
gefundene_daten = db_cursor.fetchall()

print(str(gefundene_daten))
Viele Grüße
Norbert
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Warum steht die Zeit zweifach in der Datenbank? Die epoch_ms-Spalte ist überflüssig und kann gelöscht werde.
Statt alle Daten abzufragen, mußt Du sie halt entsprechend filtern, also nach dem Datum.
Mit einer Custom Function (https://docs.python.org/3.11/library/sq ... e_function) kannst Du einen Region-Filter implementieren, indem Du dei Umrisse von Niedersachsen z.B. als Polygon modelierst und dann prüfst, ob der Punkt innerhalb des Polygons liegt. Zuletzt mußt Du ja nur noch nach Typ gruppieren und summieren.
Das läßt sich (bis auf die Polygonprüfung) als einfachen SQL-SELECT schreiben.
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Für SQLite gibt es eine SpatiaLite-Erweiterung und kurzer Blick in den Package Index lässt vermuten, dass es da auch Python-Anbindung(en) für gibt. Dann muss man sich den Polygonfilter nicht selber programmieren.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Wetter_Kalkriese
User
Beiträge: 3
Registriert: Dienstag 12. September 2023, 15:34

__blackjack__ hat geschrieben: Dienstag 12. September 2023, 22:47 Für SQLite gibt es eine SpatiaLite-Erweiterung und kurzer Blick in den Package Index lässt vermuten, dass es da auch Python-Anbindung(en) für gibt. Dann muss man sich den Polygonfilter nicht selber programmieren.
Moin,

Danke für den Tipp, das sieht vielversprechend aus. Ich habe mir vor einigen Tagen spatialite_gui-1.7.1 von https://www.gaia-gis.it/fossil/spatialite_gui/index heruntergeladen, aber ich finde keine exe Datei o.ä. zum installieren. Oder bin ich da falsch.

Gruß Norbert
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Das sind die Quelltexte. Etwas fertig kompiliertes scheint es dort nicht zu geben.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Wetter_Kalkriese
User
Beiträge: 3
Registriert: Dienstag 12. September 2023, 15:34

Moin,

ich habe mal etwas zusammen gebastelt, es ist erst der Anfang und bevor versuche das in eine neue Datei/Tabelle schreibe, frage ich, ob das, was ich bereits
habe, überhaupt ein bißchen richtig ist. Es fehlt natürlich noch die Filterung nach den Typ und die Koordinaten.
Aber ich bin erstmal froh, wenn mit den Daten eine neue Datei für den aktuellen Tag erstellt bekomme und bevor ich da weiter mache, freu ich mich über Tipps, Anregungen, Fehler, etc.

Weitere Fragen sind noch :
1. Ist die Ausführung, das die Dateinamen erstellt werden so korrekt?

2. Wie kann ich in Zeile 47 das Datum dynamisieren, oben habe ich das aktuelle Datum definiert, bei der SQL Filterung wollte ich es einfügen (ein Beispiel habe ich hier im Forum gefunden),
das ergibt aber einen Fehler und andere Beispiele im www funktionieren auch nicht, da mach ich was falsch. Und direkte SQL Datumsbefehle liefern keine Ergebnisse, also
das Ergebnis bleibt leer.

3. Wenn ich mehrere sqllite Verbindungen habe, kann ich die auch alle mit einen Befehl (commit und close) beenden/schließen?

Ansonsten bzgl. Spatialite habe ich alle "42 Installationsschritte/Dateien herunterladen" durchgeführt, leider aber noch nicht ans laufen bekommen, die exe Datei wird nicht erstellt.

Gefunden habe ich aber zwei andere Programme, das eine Programm (eigentlich beide) hat auch eine SQLITE-Anbindung, eine präzise Karte von Niedersachsen habe ich ebenfalls gefunden und kann dort auch die Landkreise und auch Gemeindegrenzen einfügen. Ich konnte auch eine SQlite erstellen, leider war die leer, da muss ich noch weiter probieren.
Das sei nur mal am Rande erwähnt

Viele Grüße
Norbert

Code: Alles auswählen

import os
import sqlite3
import datetime
from datetime import datetime, timedelta

# Heute JahrMonatTag festlegen
aktuelles_datum = datetime.now().strftime('%Y%m%d')
print("heute", aktuelles_datum)

# Datum Vortag
heute = datetime.now()
gestern = heute - timedelta (days=1)
print ("vortag", gestern.strftime ('%Y%m%d'))

# Datum Heute JahrMonatTag NGX Datenbank Format
ngx_heute = datetime.now().strftime('%Y-%m-%d')
print("ngx_heute", ngx_heute)

# Datum Vortag NGX Datenbank Format
heute = datetime.now()
ngx_gestern = heute - timedelta (days=1)
print ("ngx_vortag", ngx_gestern.strftime ('%Y-%m-%d'))

# Dateinamen mit Datum erstellen
dateiname1 = "NGXDS_" + aktuelles_datum + ".db3"
dateiname2 = "ngx_yest.db3"
dateiname3 = "ngx_today.db3"
dateiname4 = "ngx_month.db3"
dateiname5 = "ngx_year.db3"
print("test dateiname:" , dateiname1)

# Verbindung zur Datenbank herstellen
verbindung1 = sqlite3.connect(dateiname1)
# Neue Datenbank erstellen
# für gestern
verbindung2 = sqlite3.connect(dateiname2)
# für heute
verbindung3 = sqlite3.connect(dateiname3)

print("test verbindung1:" , verbindung1 )
print("test verbindung2:" , verbindung2 )
print("test verbindung3:" , verbindung3 )

# Cursor-Objekt erstellen und SQL-Abfrage zum Auslesen der Daten aus der aktuellen Datenbank
# Daten filtern
cursor = verbindung1.cursor()
cursor.execute ('SELECT SUBSTRING (datetime_utc, 1, 10), latitude, longitude, type FROM NGXLIGHTNING WHERE datetime_utc LIKE "2023-09-23%"')
daten = cursor.fetchall()

print("test ausgabe daten:" , daten )

# Aktionen bestätigen und Verbindung schließen
verbindung1.commit()
verbindung2.commit()
verbindung3.commit()
verbindung1.close()
verbindung2.close()
verbindung3.close()
Bild
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Wetter_Kalkriese: `os` wird importiert aber nirgends verwendet und `datetime` kann gar nicht verwendet werden, weil das Modul gleich in der nächsten Zeile durch den gleichnamigen Datentyp ersetzt wird.

Wenn man etwas mit *einem* Zeitpunkt machen will, dann darf man den auch nur *einmal* ermitteln. Zwischen den Aufrufen vergeht ja Zeit, so das Ende wenn's blöd läuft auch mal das Datum anders sein kann.

Wenn man an der Uhrzeit gar nicht interessiert ist, kann man auch `date.today()` statt `datetime.now()` verwenden. Allerdings scheint das später mit Zeitangaben die in UTC sind verglichen zu werden, also will man hier gar nicht `now()` sondern `utcnow()`.

`ngx_heute`, `ngx_gestern`, und `gestern` werden nicht wirklich verwendet. Ebenso werden `dateiname4` und `dateiname5` definiert aber nicht verwendet.

Nummerierte Namen sind ein „code smell“. Entweder will man da sinnvolle Namen verwenden, oder keine Einzelwerte/-namen, sondern eine Datenstruktur. Oft eine Liste.

Zeichenketten und Werte, auch wenn es andere Zeichenketten sind, stückelt man nicht mit ``+`` zusammen. Dafür gibt es Zeichenkettenformatierung mit f-Zeichenkettenliteralen oder der `format()`-Methode. Damit kann man auch die Umwandlung eines Datumsobjekts in eine Zeichenkette vornehmen.

Auch Dateinamen sollten keine kryptischen Abkürzungen enthalten. Wenn man "yesterday" meint, sollte man nicht nur "yest" schreiben. Es sei denn man arbeitet unter DOS oder einem anderen System das einen auf 8 Zeichen im Dateinamen beschränkt. 🤓 (Wobei man selbst unter DOS heute lange Dateinmanen haben kann.)

Bei Zeitstempeln in der Datenbank weder SUBSTRING noch LIKE um an den Datumsteil zu kommen. Dass das in SQLite3 (momentan) eine Zeichenkette ist, ist ein Implementierungsdetail. Auch SQLite3 hat SQL-Funktion für so etwas.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import sqlite3
from contextlib import ExitStack, closing
from datetime import date as Date, datetime as DateTime

from loguru import logger


@logger.catch
def main():
    heute = DateTime.utcnow().date()
    logger.info("heute {}", heute)

    dateinamen = [
        f"NGXDS_{heute:%Y%m%d}.db3",
        "ngx_yesterday.db3",
        "ngx_today.db3",
    ]
    with ExitStack() as stack:
        (
            verbindung_heute,
            _verbindung_gestern,
            _verbindung_noch_mal_heute,  # ???
        ) = verbindungen = [
            stack.enter_context(closing(sqlite3.connect(dateiname)))
            for dateiname in dateinamen
        ]
        with verbindung_heute.cursor() as cursor:
            cursor.execute(
                "SELECT DATE(datetime_utc), latitude, longitude, type"
                " FROM ngxlightning WHERE DATE(datetime_utc) = ?",
                [Date(2023, 9, 23)],
            )
            daten = cursor.fetchall()

        logger.debug("test ausgabe daten: {}", daten)

        for verbindung in verbindungen:
            with logger.catch():
                verbindung.commit()


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten