Created
May 15, 2018 19:58
-
-
Save ardangelo/20b43ef5527bb290dd8e524e45597c60 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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