@hwm: Ich weiss das Programm sieht inzwischen etwas anders aus aber im ersten gezeigten Programm ist übrigens ein Fehler: Du verwendest `sys.exit()` ohne `sys` importiert zu haben.
Die Fehlermeldung wenn eine Verbindung zur Datenbank fehlschlägt ist irreführend. Wenn die Datenbankdatei nicht existiert, dann wird sie automatisch angelegt, also kann der Grund für eine Ausnahme nicht sein das die Datei nicht gefunden wurde.
Falls die Datei nicht existierte und angelegt werden musste, dann ist die Datenank leer aber das Programm läuft weiter und dann natürlich bei der ersten Datenankanfrage in eine Ausnahme weil die Tabellen nicht existieren. Dieser Fall wird vom Programm aber nirgends behandelt. Ich würde da einfach auch die ”Fehlerbehandlung” beim `connect()` weglassen. Das Programm bricht sowieso ab und man hat mehr Informationen, zum Beispiel den Traceback und die genaue Ausnahme plus potentiell weitere Informationen.
Nochmal völlig ungetestet wie das dann mit SQLAlchemy ohne SQL in Zeichenketten aussehen könnte:
Code: Alles auswählen
#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
from datetime import datetime as DateTime
from time import sleep
from urllib2 import URLError
import lnetatmo
from sqlalchemy import (
and_, Column, create_engine, Date as SADate, Float, ForeignKey, Integer,
MetaData, select, Table, Text, DateTime as SADateTime, UniqueConstraint
)
metadata = MetaData()
Device = Table('device', metadata,
Column('id', Integer, primary_key=True, autoincrement=True),
Column('name', Text, unique=True),
)
Measurement = Table('measurement', metadata,
Column('id', Integer, primary_key=True, autoincrement=True),
Column('device_id', ForeignKey(Device.c.id), nullable=False),
Column('timestamp', SADateTime, nullable=False),
Column('temperature', Float),
Column('pressure', Float),
Column('noise', Float),
Column('co_2', Float),
Column('humidity', Float),
Column('battery_vp', Float),
)
Extrema = Table('extrema', metadata,
Column('id', Integer, primary_key=True, autoincrement=True),
Column('device_id', ForeignKey(Device.c.id), nullable=False),
Column('date', SADate, nullable=False),
Column('min_value', Float, nullable=False),
Column('max_value', Float, nullable=False),
UniqueConstraint('device_id', 'date'),
)
def main():
engine = create_engine('sqlite:///netatmo.db3', echo=True)
connection = engine.connect()
metadata.create_all(engine)
while True:
try:
devices = lnetatmo.DeviceList(lnetatmo.ClientAuth())
except URLError:
print('Problem with Netatmo server, will retry in 60 seconds')
sleep(60)
else:
for name, values in devices.lastData().items():
device_id = connection.execute(
select([Device.c.id]).where(Device.c.name == name)
).fetchone()
if device_id is None:
device_id = connection.execute(
Device.insert().values(name=name)
).inserted_primary_key
timestamp = DateTime.now()
connection.execute(
Measurement.insert().values(
device_id=device_id,
timestamp=timestamp,
temperature=values['Temperature'],
pressure=values.get('Pressure', 0),
noise=values.get('Noise', 0),
co_2=values.get('CO2', 0),
humidity=values.get('Humidity', 0),
battery_vp=values.get('battery_vp', 0)
)
)
date = timestamp.date()
extrema = connection.execute(
Extrema.select().where(
and_(
Extrema.c.device_id == device_id,
Extrema.c.date == date
)
)
).fetchone()
min_value = values.get('min_temp', 0)
max_value = values.get('max_temp', 0)
if extrema is None:
connection.execute(
Extrema.insert().values(
device_id=device_id,
date=date,
min_value=min_value,
max_value=min_value,
)
)
else:
connection.execute(
Extrema.update().values(
min_value=min_value, max_value=max_value
).where(Extrema.c.id == extrema[Extrema.c.id])
)
connection.commit()
sleep(600)
if __name__ == '__main__':
main()
Man hat hier zwar jetzt die zusätzliche Arbeit die Tabellenstruktur als Objekte zu definieren (obwohl man sich die auch von der Datenbank abfragen lassen könnte!), aber das ermöglicht es dann auch mit der Zeile nach der `connection` die Tabellen anzulegen sofern sie noch nicht existieren. Und bei den `values()`-Methodenaufrufen sieht man viel leichter welcher Wert welcher Spaltezugeordnet wird im Gegensatz zum SQL wo man Spaltennamen und Werte getrennt angibt, und erst die Positionen abzählen muss wenn man wissen will welcher Wert zu einer bestimmten Spalte gehört oder zu welcher Spalte ein bestimmter Wert gehört.
Der `create_all()`-Aufruf setzt übrigens folgendes SQL ab:
Code: Alles auswählen
CREATE TABLE device (
id INTEGER NOT NULL,
name TEXT,
PRIMARY KEY (id),
UNIQUE (name)
)
CREATE TABLE extrema (
id INTEGER NOT NULL,
device_id INTEGER NOT NULL,
date DATE NOT NULL,
min_value FLOAT NOT NULL,
max_value FLOAT NOT NULL,
PRIMARY KEY (id),
UNIQUE (device_id, date),
FOREIGN KEY(device_id) REFERENCES device (id)
)
CREATE TABLE measurement (
id INTEGER NOT NULL,
device_id INTEGER NOT NULL,
timestamp DATETIME NOT NULL,
temperature FLOAT,
pressure FLOAT,
noise FLOAT,
co_2 FLOAT,
humidity FLOAT,
battery_vp FLOAT,
PRIMARY KEY (id),
FOREIGN KEY(device_id) REFERENCES device (id)
)