partially merge web branch into master
authorgregor herrmann <gregoa@debian.org>
Sun, 5 Aug 2018 18:57:51 +0000 (20:57 +0200)
committergregor herrmann <gregoa@debian.org>
Sun, 5 Aug 2018 18:57:51 +0000 (20:57 +0200)
this merges everything except the webapp in the web/ directory

i.e.
- .gitignore update
- seewasser.py: small fix
- add owm.py
- add seeparklib

.gitignore
owm.py [new file with mode: 0755]
seeparklib/__init__.py [new file with mode: 0644]
seeparklib/openweathermap.py [new file with mode: 0644]

index d93bf921dd16dcb1e2d1da08ea4a37958553d0d7..89db60fa455110e4e693bb1335fd935eb63feb2a 100644 (file)
@@ -1,2 +1,6 @@
 *~
-badesaison.csv
+*.csv
+*.swp
+*.pyc
+seewasser.ini
+.idea
diff --git a/owm.py b/owm.py
new file mode 100755 (executable)
index 0000000..3265f20
--- /dev/null
+++ b/owm.py
@@ -0,0 +1,126 @@
+#!/usr/bin/python3
+
+# needs ~/seepark.ini with
+# [openweathermap]
+# apikey=...
+# cityid=..
+# 3319578 for Obsteig, AT
+
+from pprint import pprint
+import argparse
+import configparser
+import os
+import csv
+import datetime
+import math
+import json
+import warnings
+import sqlalchemy
+from sqlalchemy import create_engine
+import MySQLdb.cursors
+from seeparklib.openweathermap import openweathermap_json
+
+
+def fromtimestamp(timestamp):
+    return datetime.datetime.fromtimestamp(timestamp)
+
+
+# https://stackoverflow.com/questions/7490660/converting-wind-direction-in-angles-to-text-words
+def degToCompass(num):
+    if num is None or num is math.nan:
+        return 'N/A'
+    val=int((num/22.5)+.5)
+    arr=["N","NNO","NO","ONO","O","OSO", "SO", "SSO","S","SSW","SW","WSW","W","WNW","NW","NNW"]
+    return arr[(val % 16)]
+
+
+def extractweatherdata(w):
+    data = dict(
+        datetime = fromtimestamp(w['dt']),
+        sunrise = fromtimestamp(w['sys']['sunrise']),
+        sunset = fromtimestamp(w['sys']['sunset']),
+        temp = w['main']['temp'],
+        pressure = w['main']['pressure'],
+        humidity = w['main']['humidity'],
+        weather = w['weather'][0]['description'],
+        sky = w['weather'][0]['main'],
+        windspeed = w['wind']['speed'],
+        cloudiness = w['clouds']['all'],
+    )
+
+    data['winddegrees'] = w['wind']['deg'] if 'deg' in w['wind'] else math.nan
+    data['winddirection'] = degToCompass(data['winddegrees'])
+    data['precipitation'] = w['rain']['3h'] if 'rain' in w else math.nan
+    data['visibility'] = w.get('visibility', math.nan)
+
+    return data
+
+
+def write_csv(csv_file, weather_data):
+    """output like wetter.at.pl"""
+    with open(csv_file, "a", newline="") as file:
+        writer = csv.writer(file, dialect="excel", delimiter=';')
+        writer.writerow([
+            weather_data['datetime'].date(),
+            weather_data['datetime'].time(),
+            weather_data['sunrise'].time(),
+            weather_data['sunset'].time(),
+            "{:.2f}".format(weather_data['temp']),
+            "{:.2f} mm/h".format(weather_data['precipitation']),
+            "{:.1f} km/h {}".format(weather_data['windspeed'], weather_data['winddirection']),
+            weather_data['weather'],
+            "{}".format(weather_data['cloudiness'])
+        ])
+
+
+def write_db(config, url, weather_json, weather_data):
+    user = config.get('database', 'user')
+    pwd = config.get('database','password')
+    host = config.get('database','hostname')
+    db = config.get('database','database')
+
+    engine = create_engine('mysql+mysqldb://{}:{}@{}/{}'.format(user, pwd, host, db), echo=False)
+    conn = engine.connect()
+    row = dict(cityid=config.get('openweathermap', 'cityid'), url=url, result=json.dumps(weather_json))
+    row.update(weather_data)
+    for key, value in row.items():
+        if isinstance(value, float) and math.isnan(value):
+            row[key] = None
+    sql_columns = list(row.keys())
+    sql_values = list(row.values())
+    sql = 'insert ignore into openweathermap ({}) values ({})'.format(', '.join(sql_columns), ','.join(['%s'] * len(sql_columns)))
+    with warnings.catch_warnings():
+        # ignore _mysql_exceptions.Warning: Duplicate entry '3319578-2018-08-01 20:50:00' for key 'cityid_datetime'
+        warnings.simplefilter("ignore", category=MySQLdb.cursors.Warning)  
+        conn.execute(sql, *sql_values)
+    conn.close()
+
+
+def main(configfile, debug):
+    config = configparser.ConfigParser()
+    config.read(configfile)
+    apikey = config.get('openweathermap', 'apikey')
+    cityid = config.get('openweathermap', 'cityid')
+    csvfile = config.get("openweathermap", 'csvfilename')
+
+    url, weather_json = openweathermap_json(apikey, cityid)
+    if debug:
+        pprint(weather_json)
+    weather_data = extractweatherdata(weather_json)
+    if debug:
+        pprint(weather_data)
+
+    # write to db
+    write_db(config, url, weather_json, weather_data)
+
+    # write to csv
+    write_csv(os.path.expanduser(csvfile), weather_data)
+
+
+if __name__ == '__main__':
+    default_config_file = os.path.expanduser('~/seewasser.ini')
+    parser = argparse.ArgumentParser(description='Get OpenWeathermap data')
+    parser.add_argument('--config', default=default_config_file, help='configuration file')
+    parser.add_argument('--debug', action='store_true', default=False, help='print debug information')
+    args = parser.parse_args()
+    main(args.config, args.debug)
diff --git a/seeparklib/__init__.py b/seeparklib/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/seeparklib/openweathermap.py b/seeparklib/openweathermap.py
new file mode 100644 (file)
index 0000000..626f77e
--- /dev/null
@@ -0,0 +1,20 @@
+import requests
+
+
+class OpenWeatherMapError(RuntimeError):
+    pass
+
+
+def openweathermap_json(apikey, cityid):
+    """Returns parsed JSON as returned by openweathermap for the given cityid.
+    In case of errors, an OpenWeatherMapError is raised."""
+    baseurl = 'http://api.openweathermap.org/data/2.5/weather'
+    url = baseurl + '?units=metric&APPID={}&id={}&lang=de'.format(apikey, cityid)
+    try:
+        response = requests.get(url)
+        if response.status_code != 200:
+            raise OpenWeatherMapError('Got status code {} ({}).'.format(response.status_code, response.reason))
+        else:
+            return url, response.json()
+    except requests.exceptions.RequestException as error:
+        raise OpenWeatherMapError('Request not successful: {}'.format(error))