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:
25
FAQ
25
FAQ
@@ -1,7 +1,9 @@
|
||||
FREQUENTLY ASKED QUESTIONS ABOUT THE WEATHER UTILITY
|
||||
|
||||
Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
|
||||
Licensed per terms in the LICENSE file distributed with this software.
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
anyway?
|
||||
|
||||
If you have any recommendations for similar forecast data in
|
||||
other countries, I will be happy to try and find a way to
|
||||
METAR station IDs can be found for cities and airports worldwide,
|
||||
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
|
||||
serious modification would be necessary given that the data is
|
||||
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
|
||||
specify -f or --forecast on the command line (or forecast=True in
|
||||
an alias) without providing a city name and state abbreviation
|
||||
(-c/--city and -s/--st, or city and st in an alias), you will
|
||||
instead see the forecast for the built-in default location (or the
|
||||
city and st defined in the default alias, if you have one). See
|
||||
question 3 above for information on figuring out what city name and
|
||||
state abbreviation to use, and the manual for weatherrc(5) for
|
||||
information on defining aliases.
|
||||
|
||||
(-c/--city and -s/--st, or city and st in an alias) and are seeing
|
||||
an actual forecast, then you probably have a default city and state
|
||||
abbreviation set in your config. See question 3 above for
|
||||
information on figuring out what city name and state abbreviation
|
||||
to use, and the manual for weatherrc(5) for information on defining
|
||||
aliases.
|
||||
|
||||
7
INSTALL
7
INSTALL
@@ -1,7 +1,9 @@
|
||||
BASIC UNIX INSTALLATION INSTRUCTIONS FOR THE WEATHER UTILITY
|
||||
|
||||
Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
|
||||
Licensed per terms in the LICENSE file distributed with this software.
|
||||
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.
|
||||
|
||||
|
||||
PREREQUISITES
|
||||
@@ -55,4 +57,3 @@ MANUALS
|
||||
Optionally, the weather.1 and weatherrc.5 files can be placed in
|
||||
sane locations for TROFF/NROFF manual files on your system (for
|
||||
example, /usr/local/share/man/ or ~/man/).
|
||||
|
||||
|
||||
31
LICENSE
31
LICENSE
@@ -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
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
- 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.
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
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
35
README
@@ -1,7 +1,9 @@
|
||||
GENERAL INFORMATION ABOUT THE WEATHER UTILITY
|
||||
|
||||
Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
|
||||
Licensed per terms in the LICENSE file distributed with this software.
|
||||
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.
|
||||
|
||||
|
||||
WHAT?
|
||||
@@ -10,24 +12,24 @@ This command-line utility is intended to provide quick access to
|
||||
current weather conditions and forecasts. Presently, it is
|
||||
capable of returning data for localities throughout the USA by
|
||||
retrieving and formatting decoded METARs (Meteorological
|
||||
Aerodrome Reports) from NOAA (the National Oceanic and
|
||||
Atmospheric Administration) and forecasts from the NWS (National
|
||||
Weather Service). The tool is written to function in the same
|
||||
spirit as other command-line informational utilities like cal(1),
|
||||
calendar(1) and dict(1). It can retrieve arbitrary weather data
|
||||
via specific command-line switches (station ID, city, state), or
|
||||
aliases can be configured system wide and on a per-user basis. It
|
||||
can be freely used and redistributed under the terms of the BSD
|
||||
License.
|
||||
Aerodrome Reports) from NOAA (the USA National Oceanic and
|
||||
Atmospheric Administration) and forecasts from NWS (the USA
|
||||
National Weather Service). The tool is written to function in the
|
||||
same spirit as other command-line informational utilities like
|
||||
cal(1), calendar(1) and dict(1). It can retrieve arbitrary weather
|
||||
data via specific command-line switches (station ID, city, state),
|
||||
or aliases can be configured system wide and on a per-user basis.
|
||||
It can be freely used and redistributed under the terms of a
|
||||
BSD-like License.
|
||||
|
||||
|
||||
WHY?
|
||||
|
||||
My girlfriend has a long commute to/from work and school, and
|
||||
often wants to check the weather both for home and her office.
|
||||
My girlfriend had a long commute to/from work and school, and
|
||||
often wanted to check the weather both for home and her office.
|
||||
Unfortunately, starting a Web browser, pulling up a weather site,
|
||||
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
|
||||
install a quick command-line tool to retrieve weather info for
|
||||
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/
|
||||
|
||||
Alternatively, Debian Etch (testing) and Sid (unstable) users can
|
||||
install the weather-util package from any mirror.
|
||||
|
||||
Alternatively, Debian users can install the weather-util package
|
||||
from any mirror.
|
||||
|
||||
45
weather
45
weather
@@ -1,16 +1,18 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# weather version 1.3, http://fungi.yuggoth.org/weather/
|
||||
# Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
|
||||
# Licensed per terms in the LICENSE file distributed with this software.
|
||||
# weather version 1.4, http://fungi.yuggoth.org/weather/
|
||||
# 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.
|
||||
|
||||
"""Wrapper utility using the weather.py module."""
|
||||
|
||||
# added so distributors can consistently specify a private module location
|
||||
private_module_path = None
|
||||
if private_module_path:
|
||||
import sys
|
||||
sys.path.insert(1, private_module_path)
|
||||
import sys
|
||||
sys.path.insert(1, private_module_path)
|
||||
|
||||
import weather
|
||||
|
||||
@@ -24,17 +26,22 @@ if get_bool("list"): print weather.list_aliases(selections.config)
|
||||
|
||||
# normal operation
|
||||
else:
|
||||
for argument in selections.arguments:
|
||||
if get_bool("conditions", argument):
|
||||
print weather.get_metar(
|
||||
get("id", argument),
|
||||
get_bool("verbose", argument)
|
||||
)
|
||||
if not get_bool("conditions", argument) \
|
||||
or get_bool("forecast", argument):
|
||||
print weather.get_forecast(
|
||||
get("city", argument),
|
||||
get("st", argument),
|
||||
get_bool("verbose", argument)
|
||||
)
|
||||
|
||||
for argument in selections.arguments:
|
||||
if get_bool("conditions", argument):
|
||||
print weather.get_metar(
|
||||
id=get("id", argument),
|
||||
verbose=get_bool("verbose", argument),
|
||||
quiet=get_bool("quiet", argument),
|
||||
headers=get("headers", argument),
|
||||
murl=get("murl", argument)
|
||||
)
|
||||
if not get_bool("conditions", argument) \
|
||||
or get_bool("forecast", 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)
|
||||
)
|
||||
|
||||
25
weather.1
25
weather.1
@@ -1,6 +1,8 @@
|
||||
.TH WEATHER 1 "March 26, 2006" "" \" -*- nroff -*-
|
||||
\" Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
|
||||
\" Licensed per terms in the LICENSE file distributed with this software.
|
||||
.TH WEATHER 1 "July 13, 2008" "" \" -*- nroff -*-
|
||||
\" 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.
|
||||
.SH NAME
|
||||
weather \- command\-line tool to obtain weather conditions and forecasts
|
||||
.SH SYNOPSIS
|
||||
@@ -30,26 +32,41 @@ show a help message and exit
|
||||
.B \-cCITY, \-\-city=CITY
|
||||
the city name (ex: "Raleigh Durham")
|
||||
.TP
|
||||
.B \-\-flines=FLINES
|
||||
maximum number of forecast lines to show
|
||||
.TP
|
||||
.B \-f, \-\-forecast
|
||||
include a local forecast
|
||||
.TP
|
||||
.B \-\-furl=FURL
|
||||
forecast URL (including %city% and %st%)
|
||||
.TP
|
||||
.B \-\-headers=HEADERS
|
||||
the conditions headers to display
|
||||
.TP
|
||||
.B \-iID, \-\-id=ID
|
||||
the METAR station ID (ex: KRDU)
|
||||
.TP
|
||||
.B \-l, \-\-list
|
||||
print a list of configured aliases
|
||||
.TP
|
||||
.B \-\-murl=MURL
|
||||
METAR URL (including %id%)
|
||||
.TP
|
||||
.B \-n, \-\-no\-conditions
|
||||
disable output of current conditions (forces \-f)
|
||||
.TP
|
||||
.B \-o, \-\-omit\-forecast
|
||||
omit the local forecast (cancels \-f)
|
||||
.TP
|
||||
.B \-\-quiet
|
||||
skip preambles and don't indent
|
||||
.TP
|
||||
.B \-sST, \-\-st=ST
|
||||
the state abbreviation (ex: NC)
|
||||
.TP
|
||||
.B \-v, \-\-verbose
|
||||
show full decoded feeds
|
||||
show full decoded feeds (cancels \-q)
|
||||
.SH FILES
|
||||
.B weather
|
||||
may additionally obtain configuration data from a system\-wide
|
||||
|
||||
485
weather.py
485
weather.py
@@ -1,206 +1,323 @@
|
||||
# weather.py version 1.3, http://fungi.yuggoth.org/weather/
|
||||
# Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
|
||||
# Licensed per terms in the LICENSE file distributed with this software.
|
||||
# weather.py version 1.4, http://fungi.yuggoth.org/weather/
|
||||
# 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.
|
||||
|
||||
"""Contains various object definitions needed by the weather utility."""
|
||||
|
||||
version = "1.3"
|
||||
version = "1.4"
|
||||
|
||||
class Selections:
|
||||
"""An object to contain selection data."""
|
||||
def __init__(self):
|
||||
"""Store the config, options and arguments."""
|
||||
self.config = get_config()
|
||||
self.options, self.arguments = get_options(self.config)
|
||||
if self.arguments:
|
||||
self.arguments = [(x.lower()) for x in self.arguments]
|
||||
else: self.arguments = [ None ]
|
||||
def get(self, option, argument=None):
|
||||
"""Retrieve data from the config or options."""
|
||||
if not argument: return self.options.__dict__[option]
|
||||
elif not self.config.has_section(argument):
|
||||
import sys
|
||||
sys.stderr.write("ERROR: no alias defined for " \
|
||||
+ argument + "\n")
|
||||
sys.exit(1)
|
||||
elif self.config.has_option(argument, option):
|
||||
return self.config.get(argument, option)
|
||||
else: return self.options.__dict__[option]
|
||||
def get_bool(self, option, argument=None):
|
||||
"""Get data and coerce to a boolean if necessary."""
|
||||
return bool(self.get(option, argument))
|
||||
"""An object to contain selection data."""
|
||||
def __init__(self):
|
||||
"""Store the config, options and arguments."""
|
||||
self.config = get_config()
|
||||
self.options, self.arguments = get_options(self.config)
|
||||
if self.arguments:
|
||||
self.arguments = [(x.lower()) for x in self.arguments]
|
||||
else: self.arguments = [ None ]
|
||||
def get(self, option, argument=None):
|
||||
"""Retrieve data from the config or options."""
|
||||
if not argument: return self.options.__dict__[option]
|
||||
elif not self.config.has_section(argument):
|
||||
import sys
|
||||
sys.stderr.write("weather: error: no alias defined for " \
|
||||
+ argument + "\n")
|
||||
sys.exit(1)
|
||||
elif self.config.has_option(argument, option):
|
||||
return self.config.get(argument, option)
|
||||
else: return self.options.__dict__[option]
|
||||
def get_bool(self, option, argument=None):
|
||||
"""Get data and coerce to a boolean if necessary."""
|
||||
return bool(self.get(option, argument))
|
||||
|
||||
def bool(data):
|
||||
"""Coerce data to a boolean value."""
|
||||
if type(data) is str:
|
||||
if eval(data): return True
|
||||
else: return False
|
||||
else:
|
||||
if data: return True
|
||||
else: return False
|
||||
"""Coerce data to a boolean value."""
|
||||
if type(data) is str:
|
||||
if eval(data): return True
|
||||
else: return False
|
||||
else:
|
||||
if data: return True
|
||||
else: return False
|
||||
|
||||
def quote(words):
|
||||
"""Wrap a string in quotes if it contains spaces."""
|
||||
if words.find(" ") != -1: words = "\"" + words + "\""
|
||||
return words
|
||||
"""Wrap a string in quotes if it contains spaces."""
|
||||
if words.find(" ") != -1: words = "\"" + words + "\""
|
||||
return words
|
||||
|
||||
def sorted(data):
|
||||
"""Return a sorted copy of a list."""
|
||||
new_copy = data[:]
|
||||
new_copy.sort()
|
||||
return new_copy
|
||||
"""Return a sorted copy of a list."""
|
||||
new_copy = data[:]
|
||||
new_copy.sort()
|
||||
return new_copy
|
||||
|
||||
def get_url(url):
|
||||
"""Return a string containing the results of a URL GET."""
|
||||
import urllib
|
||||
return urllib.urlopen(url).read()
|
||||
"""Return a string containing the results of a URL GET."""
|
||||
import urllib2
|
||||
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):
|
||||
"""Return a summarized METAR for the specified station."""
|
||||
metar = get_url(
|
||||
"http://weather.noaa.gov/pub/data/observations/metar/decoded/" \
|
||||
+ id.upper() + ".TXT")
|
||||
if verbose: return metar
|
||||
else:
|
||||
lines = metar.split("\n")
|
||||
headings = [
|
||||
"Relative Humidity",
|
||||
"Precipitation last hour",
|
||||
"Sky conditions",
|
||||
"Temperature",
|
||||
"Weather",
|
||||
"Wind"
|
||||
]
|
||||
output = []
|
||||
output.append("Current conditions at " \
|
||||
+ lines[0].split(", ")[1] + " (" \
|
||||
+ id.upper() +")")
|
||||
output.append("Last updated " + lines[1])
|
||||
for line in lines:
|
||||
for heading in headings:
|
||||
if line.startswith(heading + ":"):
|
||||
if line.endswith(":0"):
|
||||
line = line[:-2]
|
||||
output.append(" " + line)
|
||||
return "\n".join(output)
|
||||
def get_metar(id, verbose=False, quiet=False, headers=None, murl=None):
|
||||
"""Return a summarized METAR for the specified station."""
|
||||
if not id:
|
||||
import sys
|
||||
sys.stderr.write("weather: error: id required for conditions\n")
|
||||
sys.exit(1)
|
||||
if not murl:
|
||||
murl = \
|
||||
"http://weather.noaa.gov/pub/data/observations/metar/decoded/%ID%.TXT"
|
||||
murl = murl.replace("%ID%", id.upper())
|
||||
murl = murl.replace("%Id%", id.capitalize())
|
||||
murl = murl.replace("%iD%", id)
|
||||
murl = murl.replace("%id%", id.lower())
|
||||
murl = murl.replace(" ", "_")
|
||||
metar = get_url(murl)
|
||||
if verbose: return metar
|
||||
else:
|
||||
lines = metar.split("\n")
|
||||
if not headers:
|
||||
headers = \
|
||||
"relative_humidity," \
|
||||
+ "precipitation_last_hour," \
|
||||
+ "sky conditions," \
|
||||
+ "temperature," \
|
||||
+ "weather," \
|
||||
+ "wind"
|
||||
headerlist = headers.lower().replace("_"," ").split(",")
|
||||
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):
|
||||
"""Return the forecast for a specified city/st combination."""
|
||||
forecast = get_url("http://weather.noaa.gov/pub/data/forecasts/city/" \
|
||||
+ st.lower() + "/" + city.lower().replace(" ", "_") \
|
||||
+ ".txt")
|
||||
if verbose: return forecast
|
||||
else:
|
||||
lines = forecast.split("\n")
|
||||
output = []
|
||||
output.append(lines[2])
|
||||
output.append(lines[3])
|
||||
for line in lines:
|
||||
if line.startswith("."):
|
||||
output.append(line.replace(".", " ", 1))
|
||||
return "\n".join(output)
|
||||
def get_forecast(city, st, verbose=False, quiet=False, flines="0", furl=None):
|
||||
"""Return the forecast for a specified city/st combination."""
|
||||
if not city or not st:
|
||||
import sys
|
||||
sys.stderr.write("weather: error: city and st required for forecast\n")
|
||||
sys.exit(1)
|
||||
if not furl:
|
||||
furl = "http://weather.noaa.gov/pub/data/forecasts/city/%st%/%city%.txt"
|
||||
furl = furl.replace("%CITY%", city.upper())
|
||||
furl = furl.replace("%City%", city.capitalize())
|
||||
furl = furl.replace("%citY%", city)
|
||||
furl = furl.replace("%city%", city.lower())
|
||||
furl = furl.replace("%ST%", st.upper())
|
||||
furl = furl.replace("%St%", st.capitalize())
|
||||
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):
|
||||
"""Parse the options passed on the command line."""
|
||||
import optparse
|
||||
usage = "usage: %prog [ options ] [ alias [ alias [...] ] ]"
|
||||
verstring = "%prog " + version
|
||||
option_parser = optparse.OptionParser(usage=usage, version=verstring)
|
||||
if config.has_option("default", "city"):
|
||||
default_city = config.get("default", "city")
|
||||
else: default_city = "Raleigh Durham"
|
||||
option_parser.add_option("-c", "--city",
|
||||
dest="city",
|
||||
default=default_city,
|
||||
help="the city name (ex: \"Raleigh Durham\")")
|
||||
if config.has_option("default", "forecast"):
|
||||
default_forecast = bool(config.get("default", "forecast"))
|
||||
else: default_forecast = False
|
||||
option_parser.add_option("-f", "--forecast",
|
||||
dest="forecast",
|
||||
action="store_true",
|
||||
default=default_forecast,
|
||||
help="include a local forecast")
|
||||
if config.has_option("default", "id"):
|
||||
default_id = config.get("default", "id")
|
||||
else: default_id = "KRDU"
|
||||
option_parser.add_option("-i", "--id",
|
||||
dest="id",
|
||||
default=default_id,
|
||||
help="the METAR station ID (ex: KRDU)")
|
||||
option_parser.add_option("-l", "--list",
|
||||
dest="list",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="print a list of configured aliases")
|
||||
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)")
|
||||
option_parser.add_option("-o", "--omit-forecast",
|
||||
dest="forecast",
|
||||
action="store_false",
|
||||
default=default_forecast,
|
||||
help="omit the local forecast (cancels -f)")
|
||||
if config.has_option("default", "st"):
|
||||
default_st = config.get("default", "st")
|
||||
else: default_st = "NC"
|
||||
option_parser.add_option("-s", "--st",
|
||||
dest="st",
|
||||
default=default_st,
|
||||
help="the state abbreviation (ex: NC)")
|
||||
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")
|
||||
options, arguments = option_parser.parse_args()
|
||||
return options, arguments
|
||||
"""Parse the options passed on the command line."""
|
||||
|
||||
# for optparse's builtin -h/--help option
|
||||
usage = "usage: %prog [ options ] [ alias [ alias [...] ] ]"
|
||||
|
||||
# for optparse's builtin --version option
|
||||
verstring = "%prog " + version
|
||||
|
||||
# create the parser
|
||||
import optparse
|
||||
option_parser = optparse.OptionParser(usage=usage, version=verstring)
|
||||
|
||||
# the -c/--city option
|
||||
if config.has_option("default", "city"):
|
||||
default_city = config.get("default", "city")
|
||||
else: default_city = ""
|
||||
option_parser.add_option("-c", "--city",
|
||||
dest="city",
|
||||
default=default_city,
|
||||
help="the city name (ex: \"Raleigh Durham\")")
|
||||
|
||||
# the --flines option
|
||||
if config.has_option("default", "flines"):
|
||||
default_flines = config.get("default", "flines")
|
||||
else: default_flines = "0"
|
||||
option_parser.add_option("--flines",
|
||||
dest="flines",
|
||||
default=default_flines,
|
||||
help="maximum number of forecast lines to show")
|
||||
|
||||
# the -f/--forecast option
|
||||
if config.has_option("default", "forecast"):
|
||||
default_forecast = bool(config.get("default", "forecast"))
|
||||
else: default_forecast = False
|
||||
option_parser.add_option("-f", "--forecast",
|
||||
dest="forecast",
|
||||
action="store_true",
|
||||
default=default_forecast,
|
||||
help="include a local forecast")
|
||||
|
||||
# the --furl option
|
||||
if config.has_option("default", "furl"):
|
||||
default_furl = config.get("default", "furl")
|
||||
else:
|
||||
default_furl = \
|
||||
"http://weather.noaa.gov/pub/data/forecasts/city/%st%/%city%.txt"
|
||||
option_parser.add_option("--furl",
|
||||
dest="furl",
|
||||
default=default_furl,
|
||||
help="forecast URL (including %city% and %st%)")
|
||||
|
||||
# the --headers option
|
||||
if config.has_option("default", "headers"):
|
||||
default_headers = config.get("default", "headers")
|
||||
else:
|
||||
default_headers = \
|
||||
"temperature," \
|
||||
+ "relative_humidity," \
|
||||
+ "wind," \
|
||||
+ "weather," \
|
||||
+ "sky_conditions," \
|
||||
+ "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():
|
||||
"""Parse the aliases and configuration."""
|
||||
import ConfigParser
|
||||
config = ConfigParser.ConfigParser()
|
||||
import os.path
|
||||
rcfiles = [
|
||||
"/etc/weatherrc",
|
||||
os.path.expanduser("~/.weatherrc"),
|
||||
"weatherrc"
|
||||
]
|
||||
import os
|
||||
for rcfile in rcfiles:
|
||||
if os.access(rcfile, os.R_OK): config.read(rcfile)
|
||||
for section in config.sections():
|
||||
if section != section.lower():
|
||||
if config.has_section(section.lower()):
|
||||
config.remove_section(section.lower())
|
||||
config.add_section(section.lower())
|
||||
for option,value in config.items(section):
|
||||
config.set(section.lower(), option, value)
|
||||
return config
|
||||
"""Parse the aliases and configuration."""
|
||||
import ConfigParser
|
||||
config = ConfigParser.ConfigParser()
|
||||
import os.path
|
||||
rcfiles = [
|
||||
"/etc/weatherrc",
|
||||
os.path.expanduser("~/.weatherrc"),
|
||||
"weatherrc"
|
||||
]
|
||||
import os
|
||||
for rcfile in rcfiles:
|
||||
if os.access(rcfile, os.R_OK): config.read(rcfile)
|
||||
for section in config.sections():
|
||||
if section != section.lower():
|
||||
if config.has_section(section.lower()):
|
||||
config.remove_section(section.lower())
|
||||
config.add_section(section.lower())
|
||||
for option,value in config.items(section):
|
||||
config.set(section.lower(), option, value)
|
||||
return config
|
||||
|
||||
def list_aliases(config):
|
||||
"""Return a formatted list of aliases defined in the config."""
|
||||
sections = []
|
||||
for section in sorted(config.sections()):
|
||||
if section.lower() not in sections and section != "default":
|
||||
sections.append(section.lower())
|
||||
output = "configured aliases..."
|
||||
for section in sorted(sections):
|
||||
output += "\n " \
|
||||
+ section \
|
||||
+ ": --id=" \
|
||||
+ quote(config.get(section, "id")) \
|
||||
+ " --city=" \
|
||||
+ quote(config.get(section, "city")) \
|
||||
+ " --st=" \
|
||||
+ quote(config.get(section, "st"))
|
||||
return output
|
||||
"""Return a formatted list of aliases defined in the config."""
|
||||
sections = []
|
||||
for section in sorted(config.sections()):
|
||||
if section.lower() not in sections and section != "default":
|
||||
sections.append(section.lower())
|
||||
output = "configured aliases..."
|
||||
for section in sorted(sections):
|
||||
output += "\n " \
|
||||
+ section \
|
||||
+ ": --id=" \
|
||||
+ quote(config.get(section, "id")) \
|
||||
+ " --city=" \
|
||||
+ quote(config.get(section, "city")) \
|
||||
+ " --st=" \
|
||||
+ quote(config.get(section, "st"))
|
||||
return output
|
||||
|
||||
|
||||
@@ -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]
|
||||
City = Allentown
|
||||
ID = KABE
|
||||
|
||||
39
weatherrc.5
39
weatherrc.5
@@ -1,6 +1,8 @@
|
||||
.TH WEATHERRC 5 "March 26, 2006" "" \" -*- nroff -*-
|
||||
\" Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
|
||||
\" Licensed per terms in the LICENSE file distributed with this software.
|
||||
.TH WEATHERRC 5 "July 13, 2008" "" \" -*- nroff -*-
|
||||
\" 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.
|
||||
.SH NAME
|
||||
weatherrc \- configuration file format for the
|
||||
.BR weather (1)
|
||||
@@ -20,20 +22,47 @@ These parameters are supported...
|
||||
.B city
|
||||
the city name (ex: Raleigh Durham)
|
||||
.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
|
||||
include a local forecast (possible values are False and True or 0 and 1)
|
||||
.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
|
||||
the METAR station ID (ex: KRDU)
|
||||
.TP
|
||||
.B conditions
|
||||
output current conditions (possible values are False and True or 0 and 1)
|
||||
.B murl
|
||||
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
|
||||
.B st
|
||||
the state abbreviation (ex: NC)
|
||||
.TP
|
||||
.B verbose
|
||||
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
|
||||
Following is an example
|
||||
.B ~/.weatherrc
|
||||
|
||||
Reference in New Issue
Block a user