From 9cfed171b69dbcac37d798780fa08be086721e03 Mon Sep 17 00:00:00 2001 From: Katharine Berry Date: Fri, 29 Jun 2018 05:17:35 -0700 Subject: [PATCH] Initial IBM-based weather proxy. No auth yet. --- .gitignore | 2 + weather/__init__.py | 103 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 .gitignore create mode 100644 weather/__init__.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..14a6c0c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.pyenv +.idea diff --git a/weather/__init__.py b/weather/__init__.py new file mode 100644 index 0000000..14db3f3 --- /dev/null +++ b/weather/__init__.py @@ -0,0 +1,103 @@ +import datetime +import os +import time + +import requests +from flask import Flask, request, jsonify +from werkzeug.routing import FloatConverter + +app = Flask(__name__) + +ibm_root = os.environ['IBM_API_ROOT'] + +# For some reason, the standard float converter rejects negative numbers +# (and also integers without a decimal point). +class SignedFloatConverter(FloatConverter): + regex = r'-?\d+(\.\d+)?' + + +app.url_map.converters['float'] = SignedFloatConverter + + +def format_date(date): + return date.strftime("%Y-%m-%dT%H:%M:%S") + + +@app.route('/api/v1/geocode///') +def geocode(latitude, longitude): + units = request.args.get('units', 'h') + language = request.args.get('language', 'en-US') + + forecast_req = requests.get(f"{ibm_root}/geocode/{latitude}/{longitude}/forecast/daily/7day.json?language={language}&units={units}") + forecast_req.raise_for_status() + forecast = forecast_req.json() + + current_req = requests.get(f"{ibm_root}/geocode/{latitude}/{longitude}/observations.json?language={language}&units={units}") + current_req.raise_for_status() + current = current_req.json() + observation = current['observation'] + + old_style_conditions = { + 'metadata': current['metadata'], + 'observation': { + 'class': observation['class'], + 'expire_time_gmt': observation['expire_time_gmt'], + 'obs_time': observation['valid_time_gmt'], + # 'obs_time_local': we don't know. + 'wdir': observation['wdir'], + 'icon_code': observation['wx_icon'], + 'icon_extd': observation['icon_extd'], + # sunrise: we don't know these, but we could yank them out of the forecast for today. + # sunset + 'day_ind': observation['day_ind'], + 'uv_index': observation['uv_index'], + # uv_warning: I don't even know what this is. Apparently numeric. + # wxman: ??? + 'obs_qualifier_code': observation['qualifier'], + 'ptend_code': observation['pressure_tend'], + 'dow': datetime.datetime.utcfromtimestamp(observation['expire_time_gmt']).strftime('%A'), + 'wdir_cardinal': observation['wdir_cardinal'], # sometimes this is "CALM", don't know if that's okay + 'uv_desc': observation['uv_desc'], + # I'm just guessing at how the three phrases map. + 'phrase_12char': observation['blunt_phrase'] or observation['wx_phrase'], + 'phrase_22char': observation['terse_phrase'] or observation['wx_phrase'], + 'phrase_32char': observation['wx_phrase'], + 'ptend_desc': observation['pressure_desc'], + # sky_cover: we don't seem to get a description of this? + 'clds': observation['clds'], + 'obs_qualifier_severity': observation['qualifier_svrty'], + # vocal_key: we don't get one of these + {'e': 'imperial', 'm': 'metric', 'h': 'uk_hybrid'}[units]: { + 'wspd': observation['wspd'], + 'gust': observation['gust'], + 'vis': observation['vis'], + # mslp: don't know what this is but it doesn't map to anything + 'altimeter': observation['pressure'], + 'temp': observation['temp'], + 'dewpt': observation['dewPt'], + 'rh': observation['rh'], + 'wc': observation['wc'], + 'hi': observation['heat_index'], + 'feels_like': observation['feels_like'], + # temp_change_24hour, temp_max_24hour, temp_min_24hour, pchange: don't get any of these + # {snow,precip}_{{1,6,24}hour,mtd,season,{2,3,7}day}: don't get these either + # ceiling, obs_qualifier_{100,50,32}char: or these. + # these are all now in their own request that you can pay extra to retrieve. + }, + } + } + + return jsonify( + fcstdaily7={ + 'errors': False, + 'data': forecast, + }, + conditions={ + 'errors': False, + 'data': old_style_conditions, + }, + metadata={ + 'version': 2, + 'transaction_id': str(int(time.time())), + }, + )