A cgi script to gather weather information from the national weather service and display it via gemini
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

92 lines
3.1 KiB

#! /usr/bin/env python3
import requests
import json
import sys
import os
import urllib.parse
GEOCODE_ROOT_URL = 'https://nominatim.openstreetmap.org/search?format=json&postalcode={}'
WEATHER_META_ROOT_URL = 'https://api.weather.gov/points/{},{}'
APPLICATION_URL = 'gemini://rawtext.club/~sloum/cgi/weather'
def error_exit(val='',query=False):
if query:
print('10 {}\r'.format(val))
else:
print('20 text/gemini\r')
print(val)
print('\n=> {} Get weather by zip code...\n'.format(APPLICATION_URL))
sys.exit()
def geocode(zip_code):
addr = GEOCODE_ROOT_URL.format(zip_code)
resp = requests.get(addr)
if resp.status_code != 200:
error_exit('Unable to geocode location from zip code')
data = json.loads(resp.content)
if not data or ('lat' not in data[0] and 'lon' not in data[0]):
error_exit('Unable to geocode location from zip code')
return data[0]
def weather_meta(location):
addr = WEATHER_META_ROOT_URL.format(location['lat'], location['lon'])
header = {'User-Agent': '(Gemiweather)'}
resp = requests.get(addr, headers=header)
if resp.status_code != 200:
error_exit('Unable to get meta from geocode location')
data = json.loads(resp.content)
if 'properties' not in data:
error_exit('Unable to retrieve forecase from meta')
meta = {
'forecast': data['properties']['forecast'],
'city': data['properties']['relativeLocation']['properties']['city'],
'state': data['properties']['relativeLocation']['properties']['state'],
'timezone': data['properties']['timeZone'],
'station': data['properties']['radarStation']
}
return meta
def weather_forecast(meta):
addr = meta['forecast']
header = {'User-Agent': '(Gemiweather)'}
resp = requests.get(addr, headers=header)
if resp.status_code != 200:
error_exit('Unable to geocode location from zip code')
data = json.loads(resp.content)
if 'properties' not in data and 'periods' not in data['properties']:
error_exit('No forecast available')
return data['properties']['periods']
def format_single_forecast_point(point):
out = '''### {}
* Date: {}
* Temp: {}°{}
* Wind: {}, {}
* Desc: {}
'''
return out.format(point['name'], point['startTime'], point['temperature'], point['temperatureUnit'], point['windSpeed'], point['windDirection'], point['detailedForecast'])
def pretty_print(zipcode, meta, forecast):
print('20 text/gemini\r')
print('# Weather for: {}, {} {}'.format(meta['city'], meta['state'], zipcode))
print('\n* Source: US National Weather Service')
print('* Station: {}\n\n## Forecast\n'.format(meta['station']))
for point in forecast:
print(format_single_forecast_point(point))
def main():
qs = os.environ.get('QUERY_STRING')
if not qs or not qs.strip():
error_exit('Enter US zip code', True)
qs = urllib.parse.unquote_plus(qs).strip()
location = geocode(qs)
meta = weather_meta(location)
forecast = weather_forecast(meta)
pretty_print(qs, meta, forecast)
# Run the thing
main()