Imported from archive.

* Release 1.5.

* (all): Updated copyright notices for 2010.

* FAQ, INSTALL, LICENSE, README: Reformatted as ReStructuredText.

* FAQ: Updated to mention alternative sources for NOAA's stations
list, in case the recommended one is unavailable (thanks Celejar!).

* NEWS: Renamed to ChangeLog and refactored into GNU format.

* weather: Added some comment padding between the shebang line and
the copyright, so that distributions wishing to carry patches which
modify the interpreter path don't have to refresh them every year
when the copyright line changes in their context.

* weather, weather.1, weatherrc.5, weather.py: Added experimental
alert, atypes, aurl and zones options to support retrieval,
filtering and formatting of unexpired NWS severe weather advisories.

* weather.1, weatherrc.5: Minor cosmetic fixes to option
descriptions.

* weather.1, weatherrc.5, weather.py: Added imperial and metric
options to filter/convert display units (thanks to Andrew Carter for
this suggestion!).

* weather.py: Fixed a METAR parsing error which would trigger an
IndexError exception if the NWS didn't have a station description on
file (thanks to Celejar for reporting the bug!). Fixed METAR title
line parsing to look for human-readable city and state in the first
line--previous code stopped showing the city name after NWS made
slight format mods. Upped the version to 1.5.

* weatherrc: Additional PIE (Saint Petersburg, FL), PNC (Ponca City,
OK), and PNS (Pensacola, FL) aliases.
This commit is contained in:
Jeremy Stanley
2010-03-19 13:30:22 +00:00
parent 8349654b7c
commit 4d25a49d5a
9 changed files with 522 additions and 197 deletions

111
FAQ
View File

@@ -1,71 +1,68 @@
FREQUENTLY ASKED QUESTIONS ABOUT THE WEATHER UTILITY ======================================================
Frequently Asked Questions About the Weather Utility
======================================================
Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>. :Copyright: (c) 2006-2010 Jeremy Stanley <fungi@yuggoth.org>. Permission to
Permission to use, copy, modify, and distribute this software is use, copy, modify, and distribute this software is granted under
granted under terms provided in the LICENSE file distributed with terms provided in the LICENSE file distributed with this software.
this software.
.. contents::
Table of Contents:
1. Can I help? 1. Can I help?
2. How do I figure out my local METAR station ID? --------------
3. How do I figure out my local city name and state abbreviation? Sure! Bug reports and feature suggestions are always welcome, but fixes and
4. I live outside the USA--can this be made to work for me patches are of course preferred. Contact fungi@yuggoth.org if desired, but
anyway? please read this FAQ and the included manuals for weather(1) and weatherrc(5)
5. Why do I get the wrong forecast when specifying -i or --id? before asking questions that might be answered therein. One big way anyone can
help is to provide me with some additional mappings of METAR station ID, city
name and state abbreviation for inclusion in the default /etc/weatherrc file.
1. Can I help?
Sure! Bug reports and feature suggestions are always welcome, but
fixes and patches are of course preferred. Contact
fungi@yuggoth.org if desired, but please read this FAQ and the
included manuals for weather(1) and weatherrc(5) before asking
questions that might be answered therein. One big way anyone can
help is to provide me with some additional mappings of METAR
station ID, city name and state abbreviation for inclusion in the
default /etc/weatherrc file.
2. How do I figure out my local METAR station ID? 2. How do I figure out my local METAR station ID?
-------------------------------------------------
The list of stations is found at http://weather.noaa.gov/data/nsd_cccc.gz (it's
thousands of lines long, so I recommend keyword searching in your browser or
using grep(1) to find what you're looking for). From time to time, the
compression on their site seems to be failing, resulting in zero-byte files. If
you run into this issue, you can get uncompressed and zip-compressed versions
by replacing the "gz" suffix in the URL with "txt" or "zip" respectively. The
list can also be obtained from the following URLs in a pinch, though they are
not guaranteed to be up to date (thanks Celejar!):
The list of stations is found at * http://www.rap.ucar.edu/weather/surface/stations.txt
http://weather.noaa.gov/data/nsd_cccc.gz (it's thousands of lines * http://aviationweather.gov/adds/metars/stations.txt
long, so I recommend keyword searching in your browser or using
grep(1) to find what you're looking for).
3. How do I figure out my local city name and state abbreviation? 3. How do I figure out my local city name and state abbreviation?
-----------------------------------------------------------------
The forecasts can be located starting from The forecasts can be located starting from
http://weather.noaa.gov/pub/data/forecasts/city/ (choose the http://weather.noaa.gov/pub/data/forecasts/city/ (choose the state abbreviation
state abbreviation to get to a list of cities in that state). 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?
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
additional input from speakers of that language for how to handle
filtering and formatting of the text.
4. I live outside the USA--can this be made to work for me anyway?
------------------------------------------------------------------
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 additional input from
speakers of that language for how to handle filtering and formatting of the
text.
5. Why do I get the wrong forecast when specifying -i or --id? 5. Why do I get the wrong forecast when specifying -i or --id?
--------------------------------------------------------------
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)
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.
The -i or --id switch (or the id parameter in an alias definition), 6. Where can I get a list of the NWS advisory zones for alerts?
only tells weather(1) what current conditions to retrieve. If you ---------------------------------------------------------------
specify -f or --forecast on the command line (or forecast=True in The lists of advisory zones by region are found aggregated at
an alias) without providing a city name and state abbreviation http://weather.noaa.gov/pub/data/zonecatalog.curr.tar (it's several thousand
(-c/--city and -s/--st, or city and st in an alias) and are seeing files totalling well over a hundred thousand lines of text, so I recommend
an actual forecast, then you probably have a default city and state downloading, unpacking and using a recursive grep(1) to find what you're
abbreviation set in your config. See question 3 above for looking for).
information on figuring out what city name and state abbreviation
to use, and the manual for weatherrc(5) for information on defining
aliases.

79
INSTALL
View File

@@ -1,28 +1,28 @@
BASIC UNIX INSTALLATION INSTRUCTIONS FOR THE WEATHER UTILITY ==============================================================
Basic Unix Installation Instructions for the Weather Utility
==============================================================
Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>. :Copyright: (c) 2006-2010 Jeremy Stanley <fungi@yuggoth.org>. Permission to
Permission to use, copy, modify, and distribute this software is use, copy, modify, and distribute this software is granted under
granted under terms provided in the LICENSE file distributed with terms provided in the LICENSE file distributed with this software.
this software.
.. contents::
PREREQUISITES Prerequisites
-------------
You need the Python interpreter installed somewhere in your path (most modern
UNIX derivatives come with one already). If you need to get Python, it can be
obtained from http://www.python.org/ (but chances are your operating system at
least provides some sort of native package for it, which you should probably
install in whatever means is recommended by your OS vendor/distributor).
You need the Python interpreter installed somewhere in your path Running in Place
(most modern UNIX derivatives come with one already). If you need to ----------------
get Python, it can be obtained from http://www.python.org/ (but An easy way to try it out is to unpack the tarball and change to the resulting
chances are your operating system at least provides some sort of directory::
native package for it, which you should probably install in whatever
means is recommended by your OS vendor/distributor).
RUNNING IN PLACE
An easy way to try it out is to unpack the tarball and change to the
resulting directory:
tar xzf weather-*.tar.gz tar xzf weather-*.tar.gz
cd weather cd weather-*
./weather --version ./weather --version
./weather --help ./weather --help
man ./weather.1 man ./weather.1
@@ -30,30 +30,27 @@ resulting directory:
./weather --forecast --no-conditions --city=charlotte --st=nc ./weather --forecast --no-conditions --city=charlotte --st=nc
./weather ord sea ./weather ord sea
...and so on. The weather utility, included Python module and ...and so on. The weather utility, included Python module and documentation are
documentation are all fully functional when kept together in one all fully functional when kept together in one directory, if somewhat
directory, if somewhat inconvenient. inconvenient.
Installing the Utility
INSTALLING THE UTILITY ----------------------
The file named weather should be made executable and put somewhere in your path
The file named weather should be made executable and put somewhere (/usr/local/bin/ or ~/bin/ for example). Similarly, weather.py needs to be
in your path (/usr/local/bin/ or ~/bin/ for example). Similarly, somewhere in Python's include path. You can see your Python interpreter's
weather.py needs to be somewhere in Python's include path. You can default include path by running::
see your Python interpreter's default include path by running:
python -c "import sys ; print sys.path" python -c "import sys ; print sys.path"
Configuration
-------------
The weatherrc file should go in /etc/ or you can save it in your home directory
as a dotfile (~/.weatherrc) to support user-specific alias configuration and
overrides of the global /etc/weatherrc file.
CONFIGURATION Manuals
-------
The weatherrc file should go in /etc/ or you can save it in your Optionally, the weather.1 and weatherrc.5 files can be placed in sane locations
home directory as a dotfile (~/.weatherrc) to support user-specific for TROFF/NROFF manual files on your system (for example, /usr/local/share/man/
alias configuration and overrides of the global /etc/weatherrc file. or ~/man/).
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/).

37
LICENSE
View File

@@ -1,13 +1,28 @@
Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org> =================
Weather License
=================
Permission to use, copy, modify, and distribute this software for any The weather project employs the ISC license, a permissive free software license
purpose with or without fee is hereby granted, provided that the above written by the Internet Systems Consortium and functionally equivalent to the
copyright notice and this permission notice appear in all copies. 2-clause BSD license. Initially used for the ISC's own software releases, it
has since become the preferred license of many projects (OpenBSD, for example).
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES Copyright Notice
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ----------------
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR Copyright (c) 2006-2010 Jeremy Stanley <fungi@yuggoth.org>
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN Permission Notice
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -----------------
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 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.
Disclaimer
----------
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.

83
README
View File

@@ -1,53 +1,48 @@
GENERAL INFORMATION ABOUT THE WEATHER UTILITY ===============================================
General Information About the Weather Utility
===============================================
Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>. :Copyright: (c) 2006-2010 Jeremy Stanley <fungi@yuggoth.org>. Permission to
Permission to use, copy, modify, and distribute this software is use, copy, modify, and distribute this software is granted under
granted under terms provided in the LICENSE file distributed with terms provided in the LICENSE file distributed with this software.
this software.
.. contents::
WHAT? What?
-----
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 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.
This command-line utility is intended to provide quick access to Why?
current weather conditions and forecasts. Presently, it is ----
capable of returning data for localities throughout the USA by My girlfriend had a long commute to/from work and school, and often wanted to
retrieving and formatting decoded METARs (Meteorological check the weather both for home and her office. Unfortunately, starting a Web
Aerodrome Reports) from NOAA (the USA National Oceanic and browser, pulling up a weather site, entering multiple ZIP codes and waiting for
Atmospheric Administration) and forecasts from NWS (the USA them to load is time-consuming for the marginally-impatient. Since she tended
National Weather Service). The tool is written to function in the to stay logged into a shell server most of the time, I figured I'd install a
same spirit as other command-line informational utilities like quick command-line tool to retrieve weather info for her commute, but to my
cal(1), calendar(1) and dict(1). It can retrieve arbitrary weather surprise, a quick search turned up little that met my basic requirements:
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.
* retrieve current data on-demand
* provide both current conditions and short-term forecasts
* simple, human-readable output
* easy to configure and use
* flexible command-line switches and options
WHY? Where?
------
A tarball for the most recent version of the weather utility can be had here:
My girlfriend had a long commute to/from work and school, and * http://fungi.yuggoth.org/weather/src/
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 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
that met my basic requirements:
* retrieve current data on-demand Alternatively, Debian and Ubuntu users can install the weather-util package
* provide both current conditions and short-term forecasts
* simple, human-readable output
* easy to configure and use
* flexible command-line switches and options
WHERE?
A tarball for the most recent version of the weather utility can
be had here:
http://fungi.yuggoth.org/weather/src/
Alternatively, Debian users can install the weather-util package
from any mirror. from any mirror.

58
weather
View File

@@ -1,10 +1,10 @@
#!/usr/bin/env python #!/usr/bin/env python
# distributions may wish to edit the above to refer to a specific interpreter
# path, such as #!/usr/bin/python
# weather version 1.4, http://fungi.yuggoth.org/weather/ # Copyright (c) 2006-2010 Jeremy Stanley <fungi@yuggoth.org>. Permission to
# Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>. # use, copy, modify, and distribute this software is granted under terms
# Permission to use, copy, modify, and distribute this software is # provided in the LICENSE file distributed with this software.
# 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."""
@@ -26,22 +26,52 @@ if get_bool("list"): print weather.list_aliases(selections.config)
# normal operation # normal operation
else: else:
output = ""
for argument in selections.arguments: for argument in selections.arguments:
if get_bool("conditions", argument): if get_bool("conditions", argument) or not (
print weather.get_metar( get_bool("alert", argument) or get_bool("forecast", argument)
):
partial = weather.get_metar(
id=get("id", argument), id=get("id", argument),
verbose=get_bool("verbose", argument), verbose=get_bool("verbose", argument),
quiet=get_bool("quiet", argument), quiet=get_bool("quiet", argument),
headers=get("headers", argument), headers=get("headers", argument),
murl=get("murl", argument) murl=get("murl", argument),
) imperial=get_bool("imperial", argument),
if not get_bool("conditions", argument) \ metric=get_bool("metric", argument)
or get_bool("forecast", argument): )
print weather.get_forecast( if partial: output += partial + "\n"
if get_bool("forecast", argument) or not (
get_bool("alert", argument) or get_bool("conditions", argument)
):
partial = weather.get_forecast(
city=get("city", argument), city=get("city", argument),
st=get("st", argument), st=get("st", argument),
verbose=get_bool("verbose", argument), verbose=get_bool("verbose", argument),
quiet=get_bool("quiet", argument), quiet=get_bool("quiet", argument),
flines=get("flines", argument), flines=get("flines", argument),
furl=get("furl", argument) furl=get("furl", argument),
) imperial=get_bool("imperial", argument),
metric=get_bool("metric", argument)
)
if partial: output += partial + "\n"
if get_bool("alert", argument) or not (
get_bool("conditions", argument) or get_bool("forecast", argument)
):
alert_text = ""
for atype in get("atypes", argument).split(","):
for zone in get("zones", argument).split(","):
partial = weather.get_alert(
zone=zone,
verbose=get_bool("verbose", argument),
quiet=get_bool("quiet", argument),
atype=atype,
aurl=get("aurl", argument),
imperial=get_bool("imperial", argument),
metric=get_bool("metric", argument)
)
if partial: alert_text += partial + "\n"
if not alert_text: alert_text = "(no current alerts for your zones)\n"
output += alert_text
output = output.strip()
if output: print( output )

View File

@@ -1,5 +1,5 @@
.TH WEATHER 1 "July 13, 2008" "" \" -*- nroff -*- .TH WEATHER 1 "March 15, 2010" "" \" -*- nroff -*-
\" Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>. \" Copyright (c) 2006-2010 Jeremy Stanley <fungi@yuggoth.org>.
\" Permission to use, copy, modify, and distribute this software is \" Permission to use, copy, modify, and distribute this software is
\" granted under terms provided in the LICENSE file distributed with \" granted under terms provided in the LICENSE file distributed with
\" this software. \" this software.
@@ -29,7 +29,16 @@ show program's version number and exit
.B \-h, \-\-help .B \-h, \-\-help
show a help message and exit show a help message and exit
.TP .TP
.B \-cCITY, \-\-city=CITY .B \-a, \-\-alert
include local alert notices
.TP
.B \-\-atypes=ATYPES
alert notification types to display
.TP
.B \-\-aurl=AURL
alert URL (including %atype% and %zone%)
.TP
.B \-c CITY, \-\-city=CITY
the city name (ex: "Raleigh Durham") the city name (ex: "Raleigh Durham")
.TP .TP
.B \-\-flines=FLINES .B \-\-flines=FLINES
@@ -44,12 +53,18 @@ forecast URL (including %city% and %st%)
.B \-\-headers=HEADERS .B \-\-headers=HEADERS
the conditions headers to display the conditions headers to display
.TP .TP
.B \-iID, \-\-id=ID .B \-i ID, \-\-id=ID
the METAR station ID (ex: KRDU) the METAR station ID (ex: KRDU)
.TP .TP
.B \-\-imperial
filter/convert for US/UK units
.TP
.B \-l, \-\-list .B \-l, \-\-list
print a list of configured aliases print a list of configured aliases
.TP .TP
.B \-m, \-\-metric
filter/convert for metric units
.TP
.B \-\-murl=MURL .B \-\-murl=MURL
METAR URL (including %id%) METAR URL (including %id%)
.TP .TP
@@ -62,11 +77,14 @@ omit the local forecast (cancels \-f)
.B \-\-quiet .B \-\-quiet
skip preambles and don't indent skip preambles and don't indent
.TP .TP
.B \-sST, \-\-st=ST .B \-s ST, \-\-st=ST
the state abbreviation (ex: NC) the state abbreviation (ex: NC)
.TP .TP
.B \-v, \-\-verbose .B \-v, \-\-verbose
show full decoded feeds (cancels \-q) show full decoded feeds (cancels \-q)
.TP
.B \-z ZONES, \-\-zones=ZONES
alert zones (ex: nc/ncc183,nc/ncz041)
.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,12 +1,10 @@
# weather.py version 1.4, http://fungi.yuggoth.org/weather/ # Copyright (c) 2006-2010 Jeremy Stanley <fungi@yuggoth.org>. Permission to
# Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>. # use, copy, modify, and distribute this software is granted under terms
# Permission to use, copy, modify, and distribute this software is # provided in the LICENSE file distributed with this software.
# 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.4" version = "1.5"
class Selections: class Selections:
"""An object to contain selection data.""" """An object to contain selection data."""
@@ -46,24 +44,121 @@ def quote(words):
if words.find(" ") != -1: words = "\"" + words + "\"" if words.find(" ") != -1: words = "\"" + words + "\""
return words return words
def titlecap(words):
"""Perform English-language title capitalization."""
words = words.lower().strip()
for separator in [" ", "-", "'"]:
newwords = []
wordlist = words.split(separator)
for word in wordlist:
if word:
newwords.append(word[0].upper() + word[1:])
words = separator.join(newwords)
end = len(words)
for prefix in ["Mac", "Mc"]:
position = 0
offset = len(prefix)
while position < end:
position = words.find(prefix, position)
if position == -1:
position = end
position += offset
import string
if position < end and words[position] in string.letters:
words = words[:position] \
+ words[position].upper() \
+ words[position+1:]
return words
def filter_units(line, units="imperial"):
"""Filter or convert units in a line of text between US/UK and metric."""
import re
# filter lines with both pressures in the form of "X inches (Y hPa)" or
# "X in. Hg (Y hPa)"
dual_p = re.match(
"(.* )(\d*(\.\d+)? (inches|in\. Hg)) \((\d*(\.\d+)? hPa)\)(.*)",
line
)
if dual_p:
preamble, in_hg, i_fr, i_un, hpa, h_fr, trailer = dual_p.groups()
if units == "imperial": line = preamble + in_hg + trailer
elif units == "metric": line = preamble + hpa + trailer
# filter lines with both temperatures in the form of "X F (Y C)"
dual_t = re.match(
"(.* )(\d*(\.\d+)? F) \((\d*(\.\d+)? C)\)(.*)",
line
)
if dual_t:
preamble, fahrenheit, f_fr, celsius, c_fr, trailer = dual_t.groups()
if units == "imperial": line = preamble + fahrenheit + trailer
elif units == "metric": line = preamble + celsius + trailer
# if metric is desired, convert distances in the form of "X mile(s)" to
# "Y kilometer(s)"
if units == "metric":
imperial_d = re.match(
"(.* )(\d+)( mile\(s\))(.*)",
line
)
if imperial_d:
preamble, mi, m_u, trailer = imperial_d.groups()
line = preamble + str(int(round(int(mi)*1.609344))) \
+ " kilometer(s)" + trailer
# filter speeds in the form of "X MPH (Y KT)" to just "X MPH"; if metric is
# desired, convert to "Z KPH"
imperial_s = re.match(
"(.* )(\d+)( MPH)( \(\d+ KT\))(.*)",
line
)
if imperial_s:
preamble, mph, m_u, kt, trailer = imperial_s.groups()
if units == "imperial": line = preamble + mph + m_u + trailer
elif units == "metric":
line = preamble + str(int(round(int(mph)*1.609344))) + " KPH" + \
trailer
# if imperial is desired, qualify given forcast temperatures like "X F"; if
# metric is desired, convert to "Y C"
imperial_t = re.match(
"(.* )(High |high |Low |low )(\d+)(\.|,)(.*)",
line
)
if imperial_t:
preamble, parameter, fahrenheit, sep, trailer = imperial_t.groups()
if units == "imperial":
line = preamble + parameter + fahrenheit + " F" + sep + trailer
elif units == "metric":
line = preamble + parameter \
+ str(int(round((int(fahrenheit)-32)*5/9))) + " C" + sep + trailer
# hand off the resulting line
return line
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, ignore_fail=False):
"""Return a string containing the results of a URL GET.""" """Return a string containing the results of a URL GET."""
import urllib2 import urllib2
try: return urllib2.urlopen(url).read() try: return urllib2.urlopen(url).read()
except urllib2.URLError: except urllib2.URLError:
import sys, traceback if ignore_fail: return ""
sys.stderr.write("weather: error: failed to retrieve\n " \ else:
+ url + "\n " + \ import sys, traceback
traceback.format_exception_only(sys.exc_type, sys.exc_value)[0]) sys.stderr.write("weather: error: failed to retrieve\n " \
sys.exit(1) + url + "\n " + \
traceback.format_exception_only(sys.exc_type, sys.exc_value)[0])
sys.exit(1)
def get_metar(id, verbose=False, quiet=False, headers=None, murl=None): def get_metar(
id,
verbose=False,
quiet=False,
headers=None,
murl=None,
imperial=False,
metric=False
):
"""Return a summarized METAR for the specified station.""" """Return a summarized METAR for the specified station."""
if not id: if not id:
import sys import sys
@@ -92,20 +187,92 @@ def get_metar(id, verbose=False, quiet=False, headers=None, murl=None):
headerlist = headers.lower().replace("_"," ").split(",") headerlist = headers.lower().replace("_"," ").split(",")
output = [] output = []
if not quiet: if not quiet:
output.append("Current conditions at " \ title = "Current conditions at %s"
+ lines[0].split(", ")[1] + " (" \ place = lines[0].split(", ")
+ id.upper() +")") if len(place) > 1:
place = "%s, %s (%s)" % (titlecap(place[0]), place[1], id.upper())
else: place = id.upper()
output.append(title%place)
output.append("Last updated " + lines[1]) output.append("Last updated " + lines[1])
for header in headerlist: for header in headerlist:
for line in lines: for line in lines:
if line.lower().startswith(header + ":"): if line.lower().startswith(header + ":"):
if line.endswith(":0"): if line.endswith(":0") or line.endswith(":1"):
line = line[:-2] line = line[:-2]
if imperial: line = filter_units(line, units="imperial")
elif metric: line = filter_units(line, units="metric")
if quiet: output.append(line) if quiet: output.append(line)
else: output.append(" " + line) else: output.append(" " + line)
return "\n".join(output) return "\n".join(output)
def get_forecast(city, st, verbose=False, quiet=False, flines="0", furl=None): def get_alert(
zone,
verbose=False,
quiet=False,
atype=None,
aurl=None,
imperial=False,
metric=False
):
"""Return alert notice for the specified zone and type."""
if not zone:
import sys
sys.stderr.write("weather: error: zone required for alerts\n")
sys.exit(1)
if not atype: atype = "severe_weather_stmt"
if not aurl:
aurl = \
"http://weather.noaa.gov/pub/data/watches_warnings/%atype%/%zone%.txt"
aurl = aurl.replace("%ATYPE%", atype.upper())
aurl = aurl.replace("%Atype%", atype.capitalize())
aurl = aurl.replace("%atypE%", atype)
aurl = aurl.replace("%atype%", atype.lower())
aurl = aurl.replace("%ZONE%", zone.upper())
aurl = aurl.replace("%Zone%", zone.capitalize())
aurl = aurl.replace("%zonE%", zone)
aurl = aurl.replace("%zone%", zone.lower())
aurl = aurl.replace(" ", "_")
alert = get_url(aurl, ignore_fail=True).strip()
if alert:
if verbose: return alert
else:
lines = alert.split("\n")
muted = True
import calendar, re, time
valid_time = time.strftime("%Y%m%d%H%M")
#if not quiet: output = [ lines[3], lines[5] ]
#if not quiet: output = [ lines[8], lines[10] ]
#else: output = []
output = []
for line in lines:
if line.startswith("Expires:") and "Expires:"+valid_time > line:
return ""
if muted and line.find("...") != -1:
muted = False
if line == "$$" \
or line.startswith("LAT...LON") \
or line.startswith("TIME...MOT...LOC"):
muted = True
if line and not (
muted \
or line == "&&"
or re.match("^/.*/$", line) \
or re.match("^"+zone.split("/")[1][:3].upper()+".*", line)
):
if quiet: output.append(line)
else: output.append(" " + line)
return "\n".join(output)
def get_forecast(
city,
st,
verbose=False,
quiet=False,
flines="0",
furl=None,
imperial=False,
metric=False
):
"""Return the forecast for a specified city/st combination.""" """Return the forecast for a specified city/st combination."""
if not city or not st: if not city or not st:
import sys import sys
@@ -131,6 +298,8 @@ def get_forecast(city, st, verbose=False, quiet=False, flines="0", furl=None):
flines = int(flines) flines = int(flines)
if not flines: flines = len(lines) - 5 if not flines: flines = len(lines) - 5
for line in lines[5:flines+5]: for line in lines[5:flines+5]:
if imperial: line = filter_units(line, units="imperial")
elif metric: line = filter_units(line, units="metric")
if line.startswith("."): if line.startswith("."):
if quiet: output.append(line.replace(".", "", 1)) if quiet: output.append(line.replace(".", "", 1))
else: output.append(line.replace(".", " ", 1)) else: output.append(line.replace(".", " ", 1))
@@ -149,6 +318,50 @@ def get_options(config):
import optparse import optparse
option_parser = optparse.OptionParser(usage=usage, version=verstring) option_parser = optparse.OptionParser(usage=usage, version=verstring)
# the -a/--alert option
if config.has_option("default", "alert"):
default_alert = bool(config.get("default", "alert"))
else: default_alert = False
option_parser.add_option("-a", "--alert",
dest="alert",
action="store_true",
default=default_alert,
help="include local alert notices")
# the --atypes option
if config.has_option("default", "atypes"):
default_atypes = config.get("default", "atypes")
else:
default_atypes = \
"flash_flood/statement," \
+ "flash_flood/warning," \
+ "flash_flood/watch," \
+ "flood/coastal," \
+ "flood/statement," \
+ "flood/warning," \
+ "non_precip," \
+ "severe_weather_stmt," \
+ "special_weather_stmt," \
+ "thunderstorm," \
+ "tornado," \
+ "urgent_weather_message"
option_parser.add_option("--atypes",
dest="atypes",
default=default_atypes,
help="alert notification types to display")
# the --aurl option
if config.has_option("default", "aurl"):
default_aurl = config.get("default", "aurl")
else:
default_aurl = \
"http://weather.noaa.gov/pub/data/watches_warnings/%atype%/%zone%.txt"
option_parser.add_option("--aurl",
dest="aurl",
default=default_aurl,
help="alert URL (including %atype% and %zone%)")
# separate options object from list of arguments and return both
# the -c/--city option # the -c/--city option
if config.has_option("default", "city"): if config.has_option("default", "city"):
default_city = config.get("default", "city") default_city = config.get("default", "city")
@@ -213,6 +426,16 @@ def get_options(config):
default=default_id, default=default_id,
help="the METAR station ID (ex: KRDU)") help="the METAR station ID (ex: KRDU)")
# the --imperial option
if config.has_option("default", "imperial"):
default_imperial = bool(config.get("default", "imperial"))
else: default_imperial = False
option_parser.add_option("--imperial",
dest="imperial",
action="store_true",
default=default_imperial,
help="filter/convert for US/UK units")
# the -l/--list option # the -l/--list option
option_parser.add_option("-l", "--list", option_parser.add_option("-l", "--list",
dest="list", dest="list",
@@ -220,6 +443,16 @@ def get_options(config):
default=False, default=False,
help="print a list of configured aliases") help="print a list of configured aliases")
# the -m/--metric option
if config.has_option("default", "metric"):
default_metric = bool(config.get("default", "metric"))
else: default_metric = False
option_parser.add_option("-m", "--metric",
dest="metric",
action="store_true",
default=default_metric,
help="filter/convert for metric units")
# the --murl option # the --murl option
if config.has_option("default", "murl"): if config.has_option("default", "murl"):
default_murl = config.get("default", "murl") default_murl = config.get("default", "murl")
@@ -277,7 +510,15 @@ def get_options(config):
default=default_verbose, default=default_verbose,
help="show full decoded feeds (cancels -q)") help="show full decoded feeds (cancels -q)")
# separate options object from list of arguments and return both # the -z/--zones option
if config.has_option("default", "zones"):
default_zones = config.get("default", "zones")
else: default_zones = ""
option_parser.add_option("-z", "--zones",
dest="zones",
default=default_zones,
help="alert zones (ex: nc/ncc183,nc/ncz041)")
options, arguments = option_parser.parse_args() options, arguments = option_parser.parse_args()
return options, arguments return options, arguments

View File

@@ -1,7 +1,6 @@
# Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>. # Copyright (c) 2006-2010 Jeremy Stanley <fungi@yuggoth.org>. Permission to
# Permission to use, copy, modify, and distribute this software is # use, copy, modify, and distribute this software is granted under terms
# granted under terms provided in the LICENSE file distributed with # provided in the LICENSE file distributed with this software.
# this software.
[ABE] [ABE]
City = Allentown City = Allentown
@@ -838,6 +837,11 @@ City = Peoria
ID = KPIA ID = KPIA
St = IL St = IL
[PIE]
City = Saint Petersburg
ID = KPIE
St = FL
[PIH] [PIH]
City = Pocatello City = Pocatello
ID = KPIH ID = KPIH
@@ -848,6 +852,16 @@ City = Pittsburgh
ID = KPIT ID = KPIT
St = PA St = PA
[PNC]
City = Ponca City
ID = KPNC
St = OK
[PNS]
City = Pensacola
ID = KPNS
St = FL
[PQI] [PQI]
City = Presque Isle City = Presque Isle
ID = KPQI ID = KPQI

View File

@@ -1,5 +1,5 @@
.TH WEATHERRC 5 "July 13, 2008" "" \" -*- nroff -*- .TH WEATHERRC 5 "March 15, 2010" "" \" -*- nroff -*-
\" Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>. \" Copyright (c) 2006-2010 Jeremy Stanley <fungi@yuggoth.org>.
\" Permission to use, copy, modify, and distribute this software is \" Permission to use, copy, modify, and distribute this software is
\" granted under terms provided in the LICENSE file distributed with \" granted under terms provided in the LICENSE file distributed with
\" this software. \" this software.
@@ -19,6 +19,15 @@ Multi-word values do not need quoting.
.SH PARAMETERS .SH PARAMETERS
These parameters are supported... These parameters are supported...
.TP .TP
.B alert
include local alert notices
.TP
.B atypes
alert notification types to display
.TP
.B aurl
alert URL (including %atype% and %zone%)
.TP
.B city .B city
the city name (ex: Raleigh Durham) the city name (ex: Raleigh Durham)
.TP .TP
@@ -40,6 +49,12 @@ the conditions headers to display (ex: temperature,wind)
.B id .B id
the METAR station ID (ex: KRDU) the METAR station ID (ex: KRDU)
.TP .TP
.B imperial
filter/convert for US/UK units
.TP
.B metric
filter/convert for metric units
.TP
.B murl .B murl
METAR URL (ex: http://metar.org/%id%.txt) METAR URL (ex: http://metar.org/%id%.txt)
.TP .TP
@@ -51,6 +66,9 @@ 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)
.TP
.B zones
alert zones (ex: nc/ncc183,nc/ncz041)
.SH URL FORMAT .SH URL FORMAT
The placeholders %city% and %st% in the furl URL and %id% in the murl URL 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 will be replaced with the city, st and id definitions respectively. If the