Imported from archive.

* Release 1.4.

* (all): Updated the copyright years for 2008 on some of the files
in the current release and added a copyright statement to any files
previously lacking one.

* LICENSE: Replaced the previous BSD-like license with the one used
by the OpenBSD project (modeled after the Internet Software
Consortium's, a two-clause BSD license removing language made
unnecessary by the Berne convention); this new license is
functionally identical to the old one, just more terse and openly
recognized.

* weather: Clarified function parameters in calls from the wrapper
script to ease future ABI changes in the underlying module.

* weather, weather.py: Some extra comments were added to the source,
indentation style was updated from tab characters to three-space,
and lines longer than 79 columns were refactored or otherwise split.

* weather.1, weather.5, weather.py: Added an flines option to allow
the maximum number of forecast output lines to be shortened. Added
furl and murl options to allow overriding of the default current
conditions and forecast data retrieval URLs. Added a headers option
to allow overriding the default list of header names for current
conditions data filtering. Added a quiet option to suppress the
preamble lines and indentation for both current conditions and
forecast output.

* weather.py: Replaced the hardcoded fallback default METAR station
ID and forecast city/state abbreviation with error messages to
minimize confusion when necessary values are omitted. Adjusted a
couple of hard-coded error message strings to be consistent with the
output format of the option_parser module. Switched from urllib to
urllib2 for retrieving data, providing a simpler means to detect and
report retrieval errors. Upped the version to 1.4.
This commit is contained in:
Jeremy Stanley
2008-07-13 07:49:02 +00:00
parent eb249a0ae2
commit 8349654b7c
9 changed files with 434 additions and 263 deletions

25
FAQ
View File

@@ -1,7 +1,9 @@
FREQUENTLY ASKED QUESTIONS ABOUT THE WEATHER UTILITY FREQUENTLY ASKED QUESTIONS ABOUT THE WEATHER UTILITY
Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved. Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>.
Licensed per terms in the LICENSE file distributed with this software. Permission to use, copy, modify, and distribute this software is
granted under terms provided in the LICENSE file distributed with
this software.
Table of Contents: Table of Contents:
@@ -44,8 +46,10 @@ state abbreviation to get to a list of cities in that state).
4. I live outside the USA--can this be made to work for me 4. I live outside the USA--can this be made to work for me
anyway? anyway?
If you have any recommendations for similar forecast data in METAR station IDs can be found for cities and airports worldwide,
other countries, I will be happy to try and find a way to but forecast data is harder to come by. If you have any
recommendations of forecast data for other countries available in a
format like NOAA's, I will be happy to try and find a way to
integrate it into the weather utility, but I suspect that some integrate it into the weather utility, but I suspect that some
serious modification would be necessary given that the data is serious modification would be necessary given that the data is
likely to be published in a non-English language, requiring some likely to be published in a non-English language, requiring some
@@ -59,10 +63,9 @@ The -i or --id switch (or the id parameter in an alias definition),
only tells weather(1) what current conditions to retrieve. If you only tells weather(1) what current conditions to retrieve. If you
specify -f or --forecast on the command line (or forecast=True in specify -f or --forecast on the command line (or forecast=True in
an alias) without providing a city name and state abbreviation an alias) without providing a city name and state abbreviation
(-c/--city and -s/--st, or city and st in an alias), you will (-c/--city and -s/--st, or city and st in an alias) and are seeing
instead see the forecast for the built-in default location (or the an actual forecast, then you probably have a default city and state
city and st defined in the default alias, if you have one). See abbreviation set in your config. See question 3 above for
question 3 above for information on figuring out what city name and information on figuring out what city name and state abbreviation
state abbreviation to use, and the manual for weatherrc(5) for to use, and the manual for weatherrc(5) for information on defining
information on defining aliases. aliases.

View File

@@ -1,7 +1,9 @@
BASIC UNIX INSTALLATION INSTRUCTIONS FOR THE WEATHER UTILITY BASIC UNIX INSTALLATION INSTRUCTIONS FOR THE WEATHER UTILITY
Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved. Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>.
Licensed per terms in the LICENSE file distributed with this software. Permission to use, copy, modify, and distribute this software is
granted under terms provided in the LICENSE file distributed with
this software.
PREREQUISITES PREREQUISITES
@@ -55,4 +57,3 @@ MANUALS
Optionally, the weather.1 and weatherrc.5 files can be placed in Optionally, the weather.1 and weatherrc.5 files can be placed in
sane locations for TROFF/NROFF manual files on your system (for sane locations for TROFF/NROFF manual files on your system (for
example, /usr/local/share/man/ or ~/man/). example, /usr/local/share/man/ or ~/man/).

31
LICENSE
View File

@@ -1,22 +1,13 @@
Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved. Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>
Redistribution and use in source and binary forms, with or without Permission to use, copy, modify, and distribute this software for any
modification, are permitted provided that the following conditions are met: purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

35
README
View File

@@ -1,7 +1,9 @@
GENERAL INFORMATION ABOUT THE WEATHER UTILITY GENERAL INFORMATION ABOUT THE WEATHER UTILITY
Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved. Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>.
Licensed per terms in the LICENSE file distributed with this software. Permission to use, copy, modify, and distribute this software is
granted under terms provided in the LICENSE file distributed with
this software.
WHAT? WHAT?
@@ -10,24 +12,24 @@ This command-line utility is intended to provide quick access to
current weather conditions and forecasts. Presently, it is current weather conditions and forecasts. Presently, it is
capable of returning data for localities throughout the USA by capable of returning data for localities throughout the USA by
retrieving and formatting decoded METARs (Meteorological retrieving and formatting decoded METARs (Meteorological
Aerodrome Reports) from NOAA (the National Oceanic and Aerodrome Reports) from NOAA (the USA National Oceanic and
Atmospheric Administration) and forecasts from the NWS (National Atmospheric Administration) and forecasts from NWS (the USA
Weather Service). The tool is written to function in the same National Weather Service). The tool is written to function in the
spirit as other command-line informational utilities like cal(1), same spirit as other command-line informational utilities like
calendar(1) and dict(1). It can retrieve arbitrary weather data cal(1), calendar(1) and dict(1). It can retrieve arbitrary weather
via specific command-line switches (station ID, city, state), or data via specific command-line switches (station ID, city, state),
aliases can be configured system wide and on a per-user basis. It or aliases can be configured system wide and on a per-user basis.
can be freely used and redistributed under the terms of the BSD It can be freely used and redistributed under the terms of a
License. BSD-like License.
WHY? WHY?
My girlfriend has a long commute to/from work and school, and My girlfriend had a long commute to/from work and school, and
often wants to check the weather both for home and her office. often wanted to check the weather both for home and her office.
Unfortunately, starting a Web browser, pulling up a weather site, Unfortunately, starting a Web browser, pulling up a weather site,
entering multiple ZIP codes and waiting for them to load is entering multiple ZIP codes and waiting for them to load is
time-consuming for the marginally-impatient. Since she tends to time-consuming for the marginally-impatient. Since she tended to
stay logged into a shell server most of the time, I figured I'd stay logged into a shell server most of the time, I figured I'd
install a quick command-line tool to retrieve weather info for install a quick command-line tool to retrieve weather info for
her commute, but to my surprise, a quick search turned up little her commute, but to my surprise, a quick search turned up little
@@ -47,6 +49,5 @@ be had here:
http://fungi.yuggoth.org/weather/src/ http://fungi.yuggoth.org/weather/src/
Alternatively, Debian Etch (testing) and Sid (unstable) users can Alternatively, Debian users can install the weather-util package
install the weather-util package from any mirror. from any mirror.

45
weather
View File

@@ -1,16 +1,18 @@
#!/usr/bin/env python #!/usr/bin/env python
# weather version 1.3, http://fungi.yuggoth.org/weather/ # weather version 1.4, http://fungi.yuggoth.org/weather/
# Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved. # Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>.
# Licensed per terms in the LICENSE file distributed with this software. # Permission to use, copy, modify, and distribute this software is
# granted under terms provided in the LICENSE file distributed with
# this software.
"""Wrapper utility using the weather.py module.""" """Wrapper utility using the weather.py module."""
# added so distributors can consistently specify a private module location # added so distributors can consistently specify a private module location
private_module_path = None private_module_path = None
if private_module_path: if private_module_path:
import sys import sys
sys.path.insert(1, private_module_path) sys.path.insert(1, private_module_path)
import weather import weather
@@ -24,17 +26,22 @@ if get_bool("list"): print weather.list_aliases(selections.config)
# normal operation # normal operation
else: else:
for argument in selections.arguments: for argument in selections.arguments:
if get_bool("conditions", argument): if get_bool("conditions", argument):
print weather.get_metar( print weather.get_metar(
get("id", argument), id=get("id", argument),
get_bool("verbose", argument) verbose=get_bool("verbose", argument),
) quiet=get_bool("quiet", argument),
if not get_bool("conditions", argument) \ headers=get("headers", argument),
or get_bool("forecast", argument): murl=get("murl", argument)
print weather.get_forecast( )
get("city", argument), if not get_bool("conditions", argument) \
get("st", argument), or get_bool("forecast", argument):
get_bool("verbose", argument) print weather.get_forecast(
) city=get("city", argument),
st=get("st", argument),
verbose=get_bool("verbose", argument),
quiet=get_bool("quiet", argument),
flines=get("flines", argument),
furl=get("furl", argument)
)

View File

@@ -1,6 +1,8 @@
.TH WEATHER 1 "March 26, 2006" "" \" -*- nroff -*- .TH WEATHER 1 "July 13, 2008" "" \" -*- nroff -*-
\" Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved. \" Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>.
\" Licensed per terms in the LICENSE file distributed with this software. \" Permission to use, copy, modify, and distribute this software is
\" granted under terms provided in the LICENSE file distributed with
\" this software.
.SH NAME .SH NAME
weather \- command\-line tool to obtain weather conditions and forecasts weather \- command\-line tool to obtain weather conditions and forecasts
.SH SYNOPSIS .SH SYNOPSIS
@@ -30,26 +32,41 @@ show a help message and exit
.B \-cCITY, \-\-city=CITY .B \-cCITY, \-\-city=CITY
the city name (ex: "Raleigh Durham") the city name (ex: "Raleigh Durham")
.TP .TP
.B \-\-flines=FLINES
maximum number of forecast lines to show
.TP
.B \-f, \-\-forecast .B \-f, \-\-forecast
include a local forecast include a local forecast
.TP .TP
.B \-\-furl=FURL
forecast URL (including %city% and %st%)
.TP
.B \-\-headers=HEADERS
the conditions headers to display
.TP
.B \-iID, \-\-id=ID .B \-iID, \-\-id=ID
the METAR station ID (ex: KRDU) the METAR station ID (ex: KRDU)
.TP .TP
.B \-l, \-\-list .B \-l, \-\-list
print a list of configured aliases print a list of configured aliases
.TP .TP
.B \-\-murl=MURL
METAR URL (including %id%)
.TP
.B \-n, \-\-no\-conditions .B \-n, \-\-no\-conditions
disable output of current conditions (forces \-f) disable output of current conditions (forces \-f)
.TP .TP
.B \-o, \-\-omit\-forecast .B \-o, \-\-omit\-forecast
omit the local forecast (cancels \-f) omit the local forecast (cancels \-f)
.TP .TP
.B \-\-quiet
skip preambles and don't indent
.TP
.B \-sST, \-\-st=ST .B \-sST, \-\-st=ST
the state abbreviation (ex: NC) the state abbreviation (ex: NC)
.TP .TP
.B \-v, \-\-verbose .B \-v, \-\-verbose
show full decoded feeds show full decoded feeds (cancels \-q)
.SH FILES .SH FILES
.B weather .B weather
may additionally obtain configuration data from a system\-wide may additionally obtain configuration data from a system\-wide

View File

@@ -1,206 +1,323 @@
# weather.py version 1.3, http://fungi.yuggoth.org/weather/ # weather.py version 1.4, http://fungi.yuggoth.org/weather/
# Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved. # Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>.
# Licensed per terms in the LICENSE file distributed with this software. # Permission to use, copy, modify, and distribute this software is
# granted under terms provided in the LICENSE file distributed with
# this software.
"""Contains various object definitions needed by the weather utility.""" """Contains various object definitions needed by the weather utility."""
version = "1.3" version = "1.4"
class Selections: class Selections:
"""An object to contain selection data.""" """An object to contain selection data."""
def __init__(self): def __init__(self):
"""Store the config, options and arguments.""" """Store the config, options and arguments."""
self.config = get_config() self.config = get_config()
self.options, self.arguments = get_options(self.config) self.options, self.arguments = get_options(self.config)
if self.arguments: if self.arguments:
self.arguments = [(x.lower()) for x in self.arguments] self.arguments = [(x.lower()) for x in self.arguments]
else: self.arguments = [ None ] else: self.arguments = [ None ]
def get(self, option, argument=None): def get(self, option, argument=None):
"""Retrieve data from the config or options.""" """Retrieve data from the config or options."""
if not argument: return self.options.__dict__[option] if not argument: return self.options.__dict__[option]
elif not self.config.has_section(argument): elif not self.config.has_section(argument):
import sys import sys
sys.stderr.write("ERROR: no alias defined for " \ sys.stderr.write("weather: error: no alias defined for " \
+ argument + "\n") + argument + "\n")
sys.exit(1) sys.exit(1)
elif self.config.has_option(argument, option): elif self.config.has_option(argument, option):
return self.config.get(argument, option) return self.config.get(argument, option)
else: return self.options.__dict__[option] else: return self.options.__dict__[option]
def get_bool(self, option, argument=None): def get_bool(self, option, argument=None):
"""Get data and coerce to a boolean if necessary.""" """Get data and coerce to a boolean if necessary."""
return bool(self.get(option, argument)) return bool(self.get(option, argument))
def bool(data): def bool(data):
"""Coerce data to a boolean value.""" """Coerce data to a boolean value."""
if type(data) is str: if type(data) is str:
if eval(data): return True if eval(data): return True
else: return False else: return False
else: else:
if data: return True if data: return True
else: return False else: return False
def quote(words): def quote(words):
"""Wrap a string in quotes if it contains spaces.""" """Wrap a string in quotes if it contains spaces."""
if words.find(" ") != -1: words = "\"" + words + "\"" if words.find(" ") != -1: words = "\"" + words + "\""
return words return words
def sorted(data): def sorted(data):
"""Return a sorted copy of a list.""" """Return a sorted copy of a list."""
new_copy = data[:] new_copy = data[:]
new_copy.sort() new_copy.sort()
return new_copy return new_copy
def get_url(url): def get_url(url):
"""Return a string containing the results of a URL GET.""" """Return a string containing the results of a URL GET."""
import urllib import urllib2
return urllib.urlopen(url).read() try: return urllib2.urlopen(url).read()
except urllib2.URLError:
import sys, traceback
sys.stderr.write("weather: error: failed to retrieve\n " \
+ url + "\n " + \
traceback.format_exception_only(sys.exc_type, sys.exc_value)[0])
sys.exit(1)
def get_metar(id, verbose=False): def get_metar(id, verbose=False, quiet=False, headers=None, murl=None):
"""Return a summarized METAR for the specified station.""" """Return a summarized METAR for the specified station."""
metar = get_url( if not id:
"http://weather.noaa.gov/pub/data/observations/metar/decoded/" \ import sys
+ id.upper() + ".TXT") sys.stderr.write("weather: error: id required for conditions\n")
if verbose: return metar sys.exit(1)
else: if not murl:
lines = metar.split("\n") murl = \
headings = [ "http://weather.noaa.gov/pub/data/observations/metar/decoded/%ID%.TXT"
"Relative Humidity", murl = murl.replace("%ID%", id.upper())
"Precipitation last hour", murl = murl.replace("%Id%", id.capitalize())
"Sky conditions", murl = murl.replace("%iD%", id)
"Temperature", murl = murl.replace("%id%", id.lower())
"Weather", murl = murl.replace(" ", "_")
"Wind" metar = get_url(murl)
] if verbose: return metar
output = [] else:
output.append("Current conditions at " \ lines = metar.split("\n")
+ lines[0].split(", ")[1] + " (" \ if not headers:
+ id.upper() +")") headers = \
output.append("Last updated " + lines[1]) "relative_humidity," \
for line in lines: + "precipitation_last_hour," \
for heading in headings: + "sky conditions," \
if line.startswith(heading + ":"): + "temperature," \
if line.endswith(":0"): + "weather," \
line = line[:-2] + "wind"
output.append(" " + line) headerlist = headers.lower().replace("_"," ").split(",")
return "\n".join(output) output = []
if not quiet:
output.append("Current conditions at " \
+ lines[0].split(", ")[1] + " (" \
+ id.upper() +")")
output.append("Last updated " + lines[1])
for header in headerlist:
for line in lines:
if line.lower().startswith(header + ":"):
if line.endswith(":0"):
line = line[:-2]
if quiet: output.append(line)
else: output.append(" " + line)
return "\n".join(output)
def get_forecast(city, st, verbose=False): def get_forecast(city, st, verbose=False, quiet=False, flines="0", furl=None):
"""Return the forecast for a specified city/st combination.""" """Return the forecast for a specified city/st combination."""
forecast = get_url("http://weather.noaa.gov/pub/data/forecasts/city/" \ if not city or not st:
+ st.lower() + "/" + city.lower().replace(" ", "_") \ import sys
+ ".txt") sys.stderr.write("weather: error: city and st required for forecast\n")
if verbose: return forecast sys.exit(1)
else: if not furl:
lines = forecast.split("\n") furl = "http://weather.noaa.gov/pub/data/forecasts/city/%st%/%city%.txt"
output = [] furl = furl.replace("%CITY%", city.upper())
output.append(lines[2]) furl = furl.replace("%City%", city.capitalize())
output.append(lines[3]) furl = furl.replace("%citY%", city)
for line in lines: furl = furl.replace("%city%", city.lower())
if line.startswith("."): furl = furl.replace("%ST%", st.upper())
output.append(line.replace(".", " ", 1)) furl = furl.replace("%St%", st.capitalize())
return "\n".join(output) furl = furl.replace("%sT%", st)
furl = furl.replace("%st%", st.lower())
furl = furl.replace(" ", "_")
forecast = get_url(furl)
if verbose: return forecast
else:
lines = forecast.split("\n")
output = []
if not quiet: output += lines[2:4]
flines = int(flines)
if not flines: flines = len(lines) - 5
for line in lines[5:flines+5]:
if line.startswith("."):
if quiet: output.append(line.replace(".", "", 1))
else: output.append(line.replace(".", " ", 1))
return "\n".join(output)
def get_options(config): def get_options(config):
"""Parse the options passed on the command line.""" """Parse the options passed on the command line."""
import optparse
usage = "usage: %prog [ options ] [ alias [ alias [...] ] ]" # for optparse's builtin -h/--help option
verstring = "%prog " + version usage = "usage: %prog [ options ] [ alias [ alias [...] ] ]"
option_parser = optparse.OptionParser(usage=usage, version=verstring)
if config.has_option("default", "city"): # for optparse's builtin --version option
default_city = config.get("default", "city") verstring = "%prog " + version
else: default_city = "Raleigh Durham"
option_parser.add_option("-c", "--city", # create the parser
dest="city", import optparse
default=default_city, option_parser = optparse.OptionParser(usage=usage, version=verstring)
help="the city name (ex: \"Raleigh Durham\")")
if config.has_option("default", "forecast"): # the -c/--city option
default_forecast = bool(config.get("default", "forecast")) if config.has_option("default", "city"):
else: default_forecast = False default_city = config.get("default", "city")
option_parser.add_option("-f", "--forecast", else: default_city = ""
dest="forecast", option_parser.add_option("-c", "--city",
action="store_true", dest="city",
default=default_forecast, default=default_city,
help="include a local forecast") help="the city name (ex: \"Raleigh Durham\")")
if config.has_option("default", "id"):
default_id = config.get("default", "id") # the --flines option
else: default_id = "KRDU" if config.has_option("default", "flines"):
option_parser.add_option("-i", "--id", default_flines = config.get("default", "flines")
dest="id", else: default_flines = "0"
default=default_id, option_parser.add_option("--flines",
help="the METAR station ID (ex: KRDU)") dest="flines",
option_parser.add_option("-l", "--list", default=default_flines,
dest="list", help="maximum number of forecast lines to show")
action="store_true",
default=False, # the -f/--forecast option
help="print a list of configured aliases") if config.has_option("default", "forecast"):
if config.has_option("default", "conditions"): default_forecast = bool(config.get("default", "forecast"))
default_conditions = bool(config.get("default", "conditions")) else: default_forecast = False
else: default_conditions = True option_parser.add_option("-f", "--forecast",
option_parser.add_option("-n", "--no-conditions", dest="forecast",
dest="conditions", action="store_true",
action="store_false", default=default_forecast,
default=default_conditions, help="include a local forecast")
help="disable output of current conditions (forces -f)")
option_parser.add_option("-o", "--omit-forecast", # the --furl option
dest="forecast", if config.has_option("default", "furl"):
action="store_false", default_furl = config.get("default", "furl")
default=default_forecast, else:
help="omit the local forecast (cancels -f)") default_furl = \
if config.has_option("default", "st"): "http://weather.noaa.gov/pub/data/forecasts/city/%st%/%city%.txt"
default_st = config.get("default", "st") option_parser.add_option("--furl",
else: default_st = "NC" dest="furl",
option_parser.add_option("-s", "--st", default=default_furl,
dest="st", help="forecast URL (including %city% and %st%)")
default=default_st,
help="the state abbreviation (ex: NC)") # the --headers option
if config.has_option("default", "verbose"): if config.has_option("default", "headers"):
default_verbose = bool(config.get("default", "verbose")) default_headers = config.get("default", "headers")
else: default_verbose = False else:
option_parser.add_option("-v", "--verbose", default_headers = \
dest="verbose", "temperature," \
action="store_true", + "relative_humidity," \
default=default_verbose, + "wind," \
help="show full decoded feeds") + "weather," \
options, arguments = option_parser.parse_args() + "sky_conditions," \
return options, arguments + "precipitation_last_hour"
option_parser.add_option("--headers",
dest="headers",
default=default_headers,
help="the conditions headers to display")
# the -i/--id option
if config.has_option("default", "id"):
default_id = config.get("default", "id")
else: default_id = ""
option_parser.add_option("-i", "--id",
dest="id",
default=default_id,
help="the METAR station ID (ex: KRDU)")
# the -l/--list option
option_parser.add_option("-l", "--list",
dest="list",
action="store_true",
default=False,
help="print a list of configured aliases")
# the --murl option
if config.has_option("default", "murl"):
default_murl = config.get("default", "murl")
else:
default_murl = \
"http://weather.noaa.gov/pub/data/observations/metar/decoded/%ID%.TXT"
option_parser.add_option("--murl",
dest="murl",
default=default_murl,
help="METAR URL (including %id%)")
# the -n/--no-conditions option
if config.has_option("default", "conditions"):
default_conditions = bool(config.get("default", "conditions"))
else: default_conditions = True
option_parser.add_option("-n", "--no-conditions",
dest="conditions",
action="store_false",
default=default_conditions,
help="disable output of current conditions (forces -f)")
# the -o/--omit-forecast option
option_parser.add_option("-o", "--omit-forecast",
dest="forecast",
action="store_false",
default=default_forecast,
help="omit the local forecast (cancels -f)")
# the -q/--quiet option
if config.has_option("default", "quiet"):
default_quiet = bool(config.get("default", "quiet"))
else: default_quiet = False
option_parser.add_option("-q", "--quiet",
dest="quiet",
action="store_true",
default=default_quiet,
help="skip preambles and don't indent")
# the -s/--st option
if config.has_option("default", "st"):
default_st = config.get("default", "st")
else: default_st = ""
option_parser.add_option("-s", "--st",
dest="st",
default=default_st,
help="the state abbreviation (ex: NC)")
# the -v/--verbose option
if config.has_option("default", "verbose"):
default_verbose = bool(config.get("default", "verbose"))
else: default_verbose = False
option_parser.add_option("-v", "--verbose",
dest="verbose",
action="store_true",
default=default_verbose,
help="show full decoded feeds (cancels -q)")
# separate options object from list of arguments and return both
options, arguments = option_parser.parse_args()
return options, arguments
def get_config(): def get_config():
"""Parse the aliases and configuration.""" """Parse the aliases and configuration."""
import ConfigParser import ConfigParser
config = ConfigParser.ConfigParser() config = ConfigParser.ConfigParser()
import os.path import os.path
rcfiles = [ rcfiles = [
"/etc/weatherrc", "/etc/weatherrc",
os.path.expanduser("~/.weatherrc"), os.path.expanduser("~/.weatherrc"),
"weatherrc" "weatherrc"
] ]
import os import os
for rcfile in rcfiles: for rcfile in rcfiles:
if os.access(rcfile, os.R_OK): config.read(rcfile) if os.access(rcfile, os.R_OK): config.read(rcfile)
for section in config.sections(): for section in config.sections():
if section != section.lower(): if section != section.lower():
if config.has_section(section.lower()): if config.has_section(section.lower()):
config.remove_section(section.lower()) config.remove_section(section.lower())
config.add_section(section.lower()) config.add_section(section.lower())
for option,value in config.items(section): for option,value in config.items(section):
config.set(section.lower(), option, value) config.set(section.lower(), option, value)
return config return config
def list_aliases(config): def list_aliases(config):
"""Return a formatted list of aliases defined in the config.""" """Return a formatted list of aliases defined in the config."""
sections = [] sections = []
for section in sorted(config.sections()): for section in sorted(config.sections()):
if section.lower() not in sections and section != "default": if section.lower() not in sections and section != "default":
sections.append(section.lower()) sections.append(section.lower())
output = "configured aliases..." output = "configured aliases..."
for section in sorted(sections): for section in sorted(sections):
output += "\n " \ output += "\n " \
+ section \ + section \
+ ": --id=" \ + ": --id=" \
+ quote(config.get(section, "id")) \ + quote(config.get(section, "id")) \
+ " --city=" \ + " --city=" \
+ quote(config.get(section, "city")) \ + quote(config.get(section, "city")) \
+ " --st=" \ + " --st=" \
+ quote(config.get(section, "st")) + quote(config.get(section, "st"))
return output return output

View File

@@ -1,3 +1,8 @@
# Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>.
# Permission to use, copy, modify, and distribute this software is
# granted under terms provided in the LICENSE file distributed with
# this software.
[ABE] [ABE]
City = Allentown City = Allentown
ID = KABE ID = KABE

View File

@@ -1,6 +1,8 @@
.TH WEATHERRC 5 "March 26, 2006" "" \" -*- nroff -*- .TH WEATHERRC 5 "July 13, 2008" "" \" -*- nroff -*-
\" Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved. \" Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>.
\" Licensed per terms in the LICENSE file distributed with this software. \" Permission to use, copy, modify, and distribute this software is
\" granted under terms provided in the LICENSE file distributed with
\" this software.
.SH NAME .SH NAME
weatherrc \- configuration file format for the weatherrc \- configuration file format for the
.BR weather (1) .BR weather (1)
@@ -20,20 +22,47 @@ These parameters are supported...
.B city .B city
the city name (ex: Raleigh Durham) the city name (ex: Raleigh Durham)
.TP .TP
.B conditions
output current conditions (possible values are False and True or 0 and 1)
.TP
.B flines
maximum number of forecast lines to show (integer value, 0 means unlimited)
.TP
.B forecast .B forecast
include a local forecast (possible values are False and True or 0 and 1) include a local forecast (possible values are False and True or 0 and 1)
.TP .TP
.B furl
forecast URL (ex: http://forecast.org/%st%/%city%.txt)
.TP
.B headers
the conditions headers to display (ex: temperature,wind)
.TP
.B id .B id
the METAR station ID (ex: KRDU) the METAR station ID (ex: KRDU)
.TP .TP
.B conditions .B murl
output current conditions (possible values are False and True or 0 and 1) METAR URL (ex: http://metar.org/%id%.txt)
.TP
.B quiet
skip preambles and don't indent (possible values are False and True or 0 and 1)
.TP .TP
.B st .B st
the state abbreviation (ex: NC) the state abbreviation (ex: NC)
.TP .TP
.B verbose .B verbose
show full decoded feeds (possible values are False and True or 0 and 1) show full decoded feeds (possible values are False and True or 0 and 1)
.SH URL FORMAT
The placeholders %city% and %st% in the furl URL and %id% in the murl URL
will be replaced with the city, st and id definitions respectively. If the
placeholder has all letters lowercased, the replacement will be forced to
all lowercase. If the placeholder has all letters uppercased, the
replacement will be forced to all uppercase. If the placeholder has its
first letter uppercased and the remainder lowercased, then all words in the
replacement will start with an uppercase letter and the rest will be
lowercase. If the placeholder has its last letter uppercased and the
remainder lowercased, then case will be preserved in the replacement. Also,
after replacement, any spaces in the resulting URL will be converted to
underscore characters prior to use.
.SH EXAMPLES .SH EXAMPLES
Following is an example Following is an example
.B ~/.weatherrc .B ~/.weatherrc