Skip to content

Instantly share code, notes, and snippets.

@ardangelo
Created May 15, 2018 19:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ardangelo/20b43ef5527bb290dd8e524e45597c60 to your computer and use it in GitHub Desktop.
Save ardangelo/20b43ef5527bb290dd8e524e45597c60 to your computer and use it in GitHub Desktop.
#!/usr/bin/python3
import sys
key = 'API_KEY'
if len(sys.argv) > 1 and sys.argv[1] == 'cli':
zipcode = sys.argv[2]
else:
import cgi, cgitb, os, codecs
form = cgi.FieldStorage()
password = form.getvalue('secret')
zipcode = form.getvalue('zipcode')
if password != 'pv5wbQqo41Nqyb':
print('Status: 403 Forbidden\r\n')
print('403 Forbidden')
exit()
# begin script
from darksky import forecast
from datetime import datetime as dt
from datetime import timedelta
import calendar
import io
# graph display
class clicolors:
BOLD = '\033[1m'
ENDBOLD = '\033[0m'
UNDERLINE = '\033[4m'
ENDUNDERLINE = '\033[0m'
BLUE = '\033[34m'
CYAN = '\033[36m'
LIGHTBLUE = '\033[94m'
DARKGRAY = '\033[90m'
LIGHTGRAY = '\033[91m'
WHITE = '\033[97m'
BLACK = '\033[30m'
ENDC = '\033[0m'
BLUEBG = '\033[44m'
CYANBG = '\033[46m'
LIGHTBLUEBG = '\033[104m'
WHITEBG = '\033[107m'
BLACKBG = '\033[40m'
DARKGRAYBG = '\033[1000m'
LIGHTGRAYBG = '\033[47m'
ENDBG = '\033[0m'
ENDALL = '\033[0m'
def horizontal_graph(lst):
MAX = 12
levels = {0: ' ', 1: '▄', 2: '░', 3: '█'}
labels = {0: 'Lgt', 4: 'Med', 8: 'Hvy'}
lines = []
for rg in [range(8, 12), range(4, 8), range(0, 4)]:
line = clicolors.CYAN
for x in lst:
if x >= rg.stop:
line += levels[3]
elif x in rg:
line += levels[x - rg.start]
else:
line += levels[0]
line += clicolors.ENDC + labels[rg.start]
lines.append(line)
return '\n'.join(lines)
# zipcode search
from uszipcode import ZipcodeSearchEngine
zip_engine = ZipcodeSearchEngine()
zip_info = zip_engine.by_zipcode(zipcode)
# weather info
fc = forecast(key, zip_info['Latitude'], zip_info['Longitude'])
# processing
low_temp = min([fch.temperature for fch in fc.hourly])
high_temp = max([fch.temperature for fch in fc.hourly])
# display
def rd(f):
return int(f + .5)
def b(s):
return clicolors.BOLD + s + clicolors.ENDBOLD
def ul(s):
return clicolors.UNDERLINE + s + clicolors.ENDUNDERLINE
bul = lambda s: b(ul(s))
# currently
title = bul('%sF - %s.' % (rd(fc.temperature), fc.summary))
subtitle = '%s %sF. %s %sF. %s %sF.' % (
b('Feels Like:'), rd(fc.currently.apparentTemperature),
b('Low:'), rd(low_temp),
b('High:'), rd(high_temp))
# hourly
MAX_HOURS = 24
SLICE_WIDTH = 2
matches = {
'Humid and Partly Cloudy': 'Partly Cloudy',
'Possible Light Rain': 'Light Rain',
}
cm = {
'Clear': clicolors.WHITEBG + clicolors.BLACK,
'Partly Cloudy': clicolors.LIGHTGRAYBG + clicolors.BLACK,
'Mostly Cloudy': clicolors.DARKGRAYBG + clicolors.WHITE,
'Overcast': clicolors.BLACKBG + clicolors.WHITE,
'Light Rain': clicolors.LIGHTBLUEBG + clicolors.WHITE,
'Rain': clicolors.CYANBG + clicolors.WHITE,
'Heavy Rain': clicolors.CYANBG + clicolors.WHITE,
}
abbrev = {
'Clear': 'Cl',
'Partly Cloudy': 'PC',
'Mostly Cloudy': 'MC',
'Overcast': 'Oc',
'Light Rain': 'LR',
'Rain': 'Rn',
'Heavy Rain': 'HR',
}
default_c = '\033[101m' + clicolors.BLACK
hour_ranges = []
range_start = 0
range_sum = None
hourly_graph_labels = []
hourly_graph_temps = []
if int(dt.fromtimestamp(fc.hourly[0].time).strftime("%H")) % 2 == 1:
hourly_graph_labels.append(ul(' ' * SLICE_WIDTH))
hourly_graph_temps.append(' ' * SLICE_WIDTH)
for i, fch in enumerate(fc.hourly):
if i > MAX_HOURS:
break
summary = matches.get(fch.summary, fch.summary)
if summary != range_sum:
if range_sum is not None:
hour_ranges.append((range(range_start, i), range_sum))
range_start = i
range_sum = summary
# graph label
hour = dt.fromtimestamp(fch.time).strftime("%H")
if (int(hour) % 2 == 0):
hourly_graph_labels.append(ul(hour + ' ' * (2 * SLICE_WIDTH - len(hour))))
temp_str = str(rd(fch.temperature)) + 'F'
hourly_graph_temps.append(temp_str + ' ' * (2 * SLICE_WIDTH - len(temp_str)))
hourly_graph_labels.append('|')
hourly_graph_temps.append('|')
if range_sum is not None:
hour_ranges.append((range(range_start, min(MAX_HOURS, len(fc.hourly))), range_sum))
hourly_graph = ''
for hrange, summary in hour_ranges:
hourly_graph += cm.get(summary, default_c)
bar_width = (hrange.stop - hrange.start) * SLICE_WIDTH
if bar_width >= len(summary):
text = summary
else:
text = abbrev.get(summary, 'XX')
hourly_graph += text + (' ' * (bar_width - len(text))) + clicolors.ENDC
# minutely
MAX_MINS = 55
any_rain = any([fcm.precipIntensity > 0 for fcm in fc.minutely[:MAX_MINS]])
scaled_intensities = [rd(fcm.precipIntensity * 30) for fcm in fc.minutely]
minutely_graph = horizontal_graph(scaled_intensities[:MAX_MINS])
minutely_graph_labels = ''
for i in range(0, MAX_MINS, 10):
minutely_graph_labels += (str(i) + ' ' * (10 - len(str(i))))
minutely_graph_labels += '|'
# output
print('Content-Type: text/plain;charset=utf-8\r\n\r\n')
print(title)
print(subtitle)
print(fc.hourly.summary)
if any_rain:
print()
print(minutely_graph)
print(minutely_graph_labels)
print('%s %s' % (ul('Next hour:'), fc.minutely.summary))
print()
print(hourly_graph)
print(''.join(hourly_graph_labels))
print(''.join(hourly_graph_temps))
print()
print(fc.daily.summary.replace('\xb0', ''))
# forecast
for fcd in fc.daily:
if dt.fromtimestamp(fcd.time) < dt.today():
continue
elif dt.fromtimestamp(fcd.time).day == dt.today().day:
dow = 'Today'
else:
dow = calendar.day_name[dt.fromtimestamp(fcd.time).weekday()]
day_summary = '%s: %s' % (
b(dow),
fcd.summary)
day_temps = '%s %sF. %s %sF.' % (
b('Low:'), ul(str(rd(fcd.temperatureLow))),
b('High:'), ul(str(rd(fcd.temperatureHigh))))
if fcd.precipProbability > 0:
day_precip = '%s%% chance of %s.' % (
ul(str(rd(100.0 * fcd.precipProbability))),
fcd.precipType)
else:
day_precip = 'No precipitation.'
print(day_summary)
print('%s %s' % (day_precip, day_temps))
print()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment