1 # weather.py version 1.1, http://fungi.yuggoth.org/weather/
2 # Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
3 # Licensed per terms in the LICENSE file distributed with this software.
5 """Contains various object definitions needed by the weather utility."""
10 """An object to contain selection data."""
12 """Store the config, options and arguments."""
13 self.config = get_config()
14 self.options, self.arguments = get_options(self.config)
16 self.arguments = [(x.lower()) for x in self.arguments]
17 else: self.arguments = [ None ]
18 def get(self, option, argument=None):
19 """Retrieve data from the config or options."""
20 if not argument: return self.options.__dict__[option]
21 elif not self.config.has_section(argument):
23 sys.stderr.write("ERROR: no alias defined for " \
26 elif self.config.has_option(argument, option):
27 return self.config.get(argument, option)
28 else: return self.options.__dict__[option]
29 def get_bool(self, option, argument=None):
30 """Get data and coerce to a boolean if necessary."""
31 return bool(self.get(option, argument))
34 """Coerce data to a boolean value."""
36 if eval(data): return True
43 """Wrap a string in quotes if it contains spaces."""
44 if words.find(" ") != -1: words = "\"" + words + "\""
48 """Return a sorted copy of a list."""
54 """Return a string containing the results of a URL GET."""
56 return urllib.urlopen(url).read()
58 def get_metar(id, verbose=False):
59 """Return a summarized METAR for the specified station."""
61 "http://weather.noaa.gov/pub/data/observations/metar/decoded/" \
62 + id.upper() + ".TXT")
63 if verbose: return metar
65 lines = metar.split("\n")
68 "Precipitation last hour",
75 output.append("Current conditions at " \
76 + lines[0].split(", ")[1] + " (" \
78 output.append("Last updated " + lines[1])
80 for heading in headings:
81 if line.startswith(heading + ":"):
82 if line.endswith(":0"):
84 output.append(" " + line)
85 return "\n".join(output)
87 def get_forecast(city, st, verbose=False):
88 """Return the forecast for a specified city/st combination."""
89 forecast = get_url("http://weather.noaa.gov/pub/data/forecasts/city/" \
90 + st.lower() + "/" + city.lower().replace(" ", "_") \
92 if verbose: return forecast
94 lines = forecast.split("\n")
96 output.append(lines[2])
97 output.append(lines[3])
99 if line.startswith("."):
100 output.append(line.replace(".", " ", 1))
101 return "\n".join(output)
103 def get_options(config):
104 """Parse the options passed on the command line."""
106 usage = "usage: %prog [ options ] [ alias [ alias [...] ] ]"
107 verstring = "%prog " + version
108 option_parser = optparse.OptionParser(usage=usage, version=verstring)
109 if config.has_option("default", "city"):
110 default_city = config.get("default", "city")
111 else: default_city = "Raleigh Durham"
112 option_parser.add_option("-c", "--city",
114 default=default_city,
115 help="the city name (ex: \"Raleigh Durham\")")
116 if config.has_option("default", "forecast"):
117 default_forecast = bool(config.get("default", "forecast"))
118 else: default_forecast = False
119 option_parser.add_option("-f", "--forecast",
122 default=default_forecast,
123 help="include a local forecast")
124 if config.has_option("default", "id"):
125 default_id = config.get("default", "id")
126 else: default_id = "KRDU"
127 option_parser.add_option("-i", "--id",
130 help="the METAR station ID (ex: KRDU)")
131 option_parser.add_option("-l", "--list",
135 help="print a list of configured aliases")
136 if config.has_option("default", "conditions"):
137 default_conditions = bool(config.get("default", "conditions"))
138 else: default_conditions = True
139 option_parser.add_option("-n", "--no-conditions",
141 action="store_false",
142 default=default_conditions,
143 help="disable output of current conditions (forces -f)")
144 option_parser.add_option("-o", "--omit-forecast",
146 action="store_false",
147 default=default_forecast,
148 help="omit the local forecast (cancels -f)")
149 if config.has_option("default", "st"):
150 default_st = config.get("default", "st")
151 else: default_st = "NC"
152 option_parser.add_option("-s", "--st",
155 help="the state abbreviation (ex: NC)")
156 if config.has_option("default", "verbose"):
157 default_verbose = bool(config.get("default", "verbose"))
158 else: default_verbose = False
159 option_parser.add_option("-v", "--verbose",
162 default=default_verbose,
163 help="show full decoded feeds")
164 options, arguments = option_parser.parse_args()
165 return options, arguments
168 """Parse the aliases and configuration."""
170 config = ConfigParser.ConfigParser()
174 os.path.expanduser("~/.weatherrc"),
178 for rcfile in rcfiles:
179 if os.access(rcfile, os.R_OK): config.read(rcfile)
180 for section in config.sections():
181 if section != section.lower():
182 if config.has_section(section.lower()):
183 config.remove_section(section.lower())
184 config.add_section(section.lower())
185 for option,value in config.items(section):
186 config.set(section.lower(), option, value)
189 def list_aliases(config):
190 """Return a formatted list of aliases defined in the config."""
192 for section in sorted(config.sections()):
193 if section.lower() not in sections and section != "default":
194 sections.append(section.lower())
195 output = "configured aliases..."
196 for section in sorted(sections):
200 + quote(config.get(section, "id")) \
202 + quote(config.get(section, "city")) \
204 + quote(config.get(section, "st"))