6073a5ce0486bef699a0b816982b83b696f39646
[weather.git] / weather.py
1 # Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
2 # Licensed per terms in the LICENSE file distributed with this software.
3
4 version = "1.0"
5
6 class Selections:
7         """An object to contain selection data."""
8         def __init__(self):
9                 """Store the config, options and arguments."""
10                 self.config = get_config()
11                 self.options, self.arguments = get_options()
12                 if self.arguments:
13                         self.arguments = [(x.lower()) for x in self.arguments]
14                 else: self.arguments = [ None ]
15         def get(self, option, argument=None):
16                 """Retrieve data from the config or options."""
17                 if not argument: argument = "default"
18                 if self.config.has_option(argument, option):
19                         return self.config.get(argument, option)
20                 else: return self.options.__dict__[option]
21         def get_boolean(self, option, argument=None):
22                 """Get data and coerce to a boolean if necessary."""
23                 data = self.get(option, argument)
24                 if type(data) is str:
25                         if eval(data): return True
26                         else: return False
27                 else:
28                         if data: return True
29                         else: return False
30
31 def quote(words):
32         """Wrap a string in quotes if it contains spaces."""
33         if words.find(" ") != -1: words = "\"" + words + "\""
34         return words
35
36 def sorted(data):
37         """Return a sorted copy of a list."""
38         new_copy = data[:]
39         new_copy.sort()
40         return new_copy
41
42 def get_url(url):
43         """Return a string containing the results of a URL GET."""
44         import urllib
45         return urllib.urlopen(url).read()
46
47 def get_metar(id, verbose=False):
48         """Return a summarized METAR for the specified station."""
49         metar = get_url(
50                 "http://weather.noaa.gov/pub/data/observations/metar/decoded/" \
51                         + id.upper() + ".TXT")
52         if verbose: return metar
53         else:
54                 lines = metar.split("\n")
55                 headings = [
56                         "Relative Humidity",
57                         "Precipitation last hour",
58                         "Sky conditions",
59                         "Temperature",
60                         "Weather",
61                         "Wind" 
62                         ]
63                 output = []
64                 output.append("Current conditions at " \
65                         + lines[0].split(", ")[1] + " (" \
66                         + id.upper() +")")
67                 output.append("Last updated " + lines[1])
68                 for line in lines:
69                         for heading in headings:
70                                 if line.startswith(heading + ":"):
71                                         output.append("   " + line)
72                 return "\n".join(output)
73
74 def get_forecast(city, st, verbose=False):
75         """Return the forecast for a specified city/st combination."""
76         forecast = get_url("http://weather.noaa.gov/pub/data/forecasts/city/" \
77                 + st.lower() + "/" + city.lower().replace(" ", "_") \
78                 + ".txt")
79         if verbose: return forecast
80         else:
81                 lines = forecast.split("\n")
82                 output = []
83                 output.append(lines[2])
84                 output.append(lines[3])
85                 for line in lines:
86                         if line.startswith("."):
87                                 output.append(line.replace(".", "   ", 1))
88                 return "\n".join(output)
89
90 def get_options():
91         """Parse the options passed on the command line."""
92         import optparse
93         usage = "usage: %prog [ options ] [ alias [ alias [...] ] ]"
94         verstring = "%prog " + version
95         option_parser = optparse.OptionParser(usage=usage, version=verstring)
96         option_parser.add_option("-c", "--city",
97                 dest="city",
98                 default="Raleigh Durham",
99                 help="the city name (ex: \"Raleigh Durham\")")
100         option_parser.add_option("-f", "--forecast",
101                 dest="forecast",
102                 action="store_true",
103                 default=False,
104                 help="include forecast (needs -c and -s)")
105         option_parser.add_option("-i", "--id",
106                 dest="id",
107                 default="KRDU",
108                 help="the METAR station ID (ex: KRDU)")
109         option_parser.add_option("-l", "--list",
110                 dest="list",
111                 action="store_true",
112                 default=False,
113                 help="print a list of configured aliases")
114         option_parser.add_option("-n", "--no-conditions",
115                 dest="conditions",
116                 action="store_false",
117                 default=True,
118                 help="disable output of current conditions (implies --forecast)")
119         option_parser.add_option("-s", "--st",
120                 dest="st",
121                 default="NC",
122                 help="the state abbreviation (ex: NC)")
123         option_parser.add_option("-v", "--verbose",
124                 dest="verbose",
125                 action="store_true",
126                 default=False,
127                 help="show full decoded feeds")
128         options, arguments = option_parser.parse_args()
129         return options, arguments
130
131 def get_config():
132         """Parse the aliases and configuration."""
133         import ConfigParser
134         config = ConfigParser.ConfigParser()
135         import os.path
136         rcfiles = [
137                 ".weatherrc",
138                 os.path.expanduser("~/.weatherrc"),
139                 "/etc/weatherrc"
140                 ]
141         import os
142         for rcfile in rcfiles:
143                 if os.access(rcfile, os.R_OK): config.read(rcfile)
144         for section in config.sections():
145                 if section != section.lower():
146                         if config.has_section(section.lower()):
147                                 config.remove_section(section.lower())
148                         config.add_section(section.lower())
149                         for option,value in config.items(section):
150                                 config.set(section.lower(), option, value)
151         return config
152
153 def list_aliases(config):
154         """Return a formatted list of aliases defined in the config."""
155         sections = []
156         for section in config.sections():
157                 if section.lower() not in sections and section != "default":
158                         sections.append(section.lower())
159         output = "configured aliases..."
160         for section in sorted(sections):
161                 output += "\n   " \
162                         + section \
163                         + ": --id=" \
164                         + quote(config.get(section, "id")) \
165                         + " --city=" \
166                         + quote(config.get(section, "city")) \
167                         + " --st=" \
168                         + quote(config.get(section, "st"))
169         return output
170