From 4d25a49d5a5ec5415f8e83ba26fea5adf4e5512a Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Fri, 19 Mar 2010 13:30:22 +0000 Subject: [PATCH 1/1] 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. --- FAQ | 117 +++++++++++++------------ INSTALL | 81 +++++++++--------- LICENSE | 41 ++++++--- README | 99 ++++++++++----------- weather | 58 ++++++++++--- weather.1 | 28 ++++-- weather.py | 279 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----- weatherrc | 22 ++++- weatherrc.5 | 22 ++++- 9 files changed, 536 insertions(+), 211 deletions(-) diff --git a/FAQ b/FAQ index d8cc569..54a44d8 100644 --- a/FAQ +++ b/FAQ @@ -1,71 +1,68 @@ -FREQUENTLY ASKED QUESTIONS ABOUT THE WEATHER UTILITY +====================================================== + Frequently Asked Questions About the Weather Utility +====================================================== -Copyright (c) 2006-2008 Jeremy Stanley . -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: - -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? -4. I live outside the USA--can this be made to work for me - anyway? -5. Why do I get the wrong forecast when specifying -i or --id? +:Copyright: (c) 2006-2010 Jeremy Stanley . Permission to + use, copy, modify, and distribute this software is granted under + terms provided in the LICENSE file distributed with this software. +.. contents:: 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. - +-------------- +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? - -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). - +------------------------------------------------- +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!): + + * http://www.rap.ucar.edu/weather/surface/stations.txt + * http://aviationweather.gov/adds/metars/stations.txt 3. How do I figure out my local city name and state abbreviation? - +----------------------------------------------------------------- The forecasts can be located starting from -http://weather.noaa.gov/pub/data/forecasts/city/ (choose the -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? - -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. - +http://weather.noaa.gov/pub/data/forecasts/city/ (choose the 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? +------------------------------------------------------------------ +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? - -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), 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. + +6. Where can I get a list of the NWS advisory zones for alerts? +--------------------------------------------------------------- +The lists of advisory zones by region are found aggregated at +http://weather.noaa.gov/pub/data/zonecatalog.curr.tar (it's several thousand +files totalling well over a hundred thousand lines of text, so I recommend +downloading, unpacking and using a recursive grep(1) to find what you're +looking for). diff --git a/INSTALL b/INSTALL index da831b6..2d341ab 100644 --- a/INSTALL +++ b/INSTALL @@ -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 . -Permission to use, copy, modify, and distribute this software is -granted under terms provided in the LICENSE file distributed with -this software. +:Copyright: (c) 2006-2010 Jeremy Stanley . Permission to + use, copy, modify, and distribute this software is granted under + terms provided in the LICENSE file distributed with 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 -(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). - - -RUNNING IN PLACE - -An easy way to try it out is to unpack the tarball and change to the -resulting directory: +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 - cd weather + cd weather-* ./weather --version ./weather --help man ./weather.1 @@ -30,30 +30,27 @@ resulting directory: ./weather --forecast --no-conditions --city=charlotte --st=nc ./weather ord sea -...and so on. The weather utility, included Python module and -documentation are all fully functional when kept together in one -directory, if somewhat inconvenient. - - -INSTALLING THE UTILITY +...and so on. The weather utility, included Python module and documentation are +all fully functional when kept together in one directory, if somewhat +inconvenient. -The file named weather should be made executable and put somewhere -in your path (/usr/local/bin/ or ~/bin/ for example). Similarly, -weather.py needs to be somewhere in Python's include path. You can -see your Python interpreter's default include path by running: +Installing the Utility +---------------------- +The file named weather should be made executable and put somewhere in your path +(/usr/local/bin/ or ~/bin/ for example). Similarly, weather.py needs to be +somewhere in Python's include path. You can see your Python interpreter's +default include path by running:: 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. - - -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/). +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. + +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/). diff --git a/LICENSE b/LICENSE index 87142ff..22c1666 100644 --- a/LICENSE +++ b/LICENSE @@ -1,13 +1,28 @@ -Copyright (c) 2006-2008 Jeremy Stanley - -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. +================= + Weather License +================= + +The weather project employs the ISC license, a permissive free software license +written by the Internet Systems Consortium and functionally equivalent to the +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). + +Copyright Notice +---------------- +Copyright (c) 2006-2010 Jeremy Stanley + +Permission Notice +----------------- +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. diff --git a/README b/README index a564e31..8ed74c1 100644 --- a/README +++ b/README @@ -1,53 +1,48 @@ -GENERAL INFORMATION ABOUT THE WEATHER UTILITY - -Copyright (c) 2006-2008 Jeremy Stanley . -Permission to use, copy, modify, and distribute this software is -granted under terms provided in the LICENSE file distributed with -this software. - - -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. - - -WHY? - -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 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 - * 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 +=============================================== + General Information About the Weather Utility +=============================================== + +:Copyright: (c) 2006-2010 Jeremy Stanley . Permission to + use, copy, modify, and distribute this software is granted under + terms provided in the LICENSE file distributed with this software. + +.. contents:: + +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. + +Why? +---- +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 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 + * 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 and Ubuntu users can install the weather-util package from any mirror. diff --git a/weather b/weather index 5e620b8..dc0601c 100755 --- a/weather +++ b/weather @@ -1,10 +1,10 @@ #!/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-2008 Jeremy Stanley . -# Permission to use, copy, modify, and distribute this software is -# granted under terms provided in the LICENSE file distributed with -# this software. +# Copyright (c) 2006-2010 Jeremy Stanley . 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.""" @@ -26,22 +26,52 @@ if get_bool("list"): print weather.list_aliases(selections.config) # normal operation else: + output = "" for argument in selections.arguments: - if get_bool("conditions", argument): - print weather.get_metar( + if get_bool("conditions", argument) or not ( + get_bool("alert", argument) or get_bool("forecast", argument) + ): + partial = 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( + murl=get("murl", argument), + imperial=get_bool("imperial", argument), + metric=get_bool("metric", argument) + ) + 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), st=get("st", argument), verbose=get_bool("verbose", argument), quiet=get_bool("quiet", 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 ) diff --git a/weather.1 b/weather.1 index 171a5c4..bbffa8c 100644 --- a/weather.1 +++ b/weather.1 @@ -1,5 +1,5 @@ -.TH WEATHER 1 "July 13, 2008" "" \" -*- nroff -*- -\" Copyright (c) 2006-2008 Jeremy Stanley . +.TH WEATHER 1 "March 15, 2010" "" \" -*- nroff -*- +\" Copyright (c) 2006-2010 Jeremy Stanley . \" Permission to use, copy, modify, and distribute this software is \" granted under terms provided in the LICENSE file distributed with \" this software. @@ -29,7 +29,16 @@ show program's version number and exit .B \-h, \-\-help show a help message and exit .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") .TP .B \-\-flines=FLINES @@ -44,12 +53,18 @@ forecast URL (including %city% and %st%) .B \-\-headers=HEADERS the conditions headers to display .TP -.B \-iID, \-\-id=ID +.B \-i ID, \-\-id=ID the METAR station ID (ex: KRDU) .TP +.B \-\-imperial +filter/convert for US/UK units +.TP .B \-l, \-\-list print a list of configured aliases .TP +.B \-m, \-\-metric +filter/convert for metric units +.TP .B \-\-murl=MURL METAR URL (including %id%) .TP @@ -62,11 +77,14 @@ omit the local forecast (cancels \-f) .B \-\-quiet skip preambles and don't indent .TP -.B \-sST, \-\-st=ST +.B \-s ST, \-\-st=ST the state abbreviation (ex: NC) .TP .B \-v, \-\-verbose show full decoded feeds (cancels \-q) +.TP +.B \-z ZONES, \-\-zones=ZONES +alert zones (ex: nc/ncc183,nc/ncz041) .SH FILES .B weather may additionally obtain configuration data from a system\-wide diff --git a/weather.py b/weather.py index 20c9d5c..4920a3a 100644 --- a/weather.py +++ b/weather.py @@ -1,12 +1,10 @@ -# weather.py version 1.4, http://fungi.yuggoth.org/weather/ -# Copyright (c) 2006-2008 Jeremy Stanley . -# Permission to use, copy, modify, and distribute this software is -# granted under terms provided in the LICENSE file distributed with -# this software. +# Copyright (c) 2006-2010 Jeremy Stanley . 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.4" +version = "1.5" class Selections: """An object to contain selection data.""" @@ -46,24 +44,121 @@ def quote(words): if words.find(" ") != -1: words = "\"" + 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): """Return a sorted copy of a list.""" new_copy = data[:] new_copy.sort() return new_copy -def get_url(url): +def get_url(url, ignore_fail=False): """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) + if ignore_fail: return "" + else: + 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, 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.""" if not id: import sys @@ -92,20 +187,92 @@ def get_metar(id, verbose=False, quiet=False, headers=None, murl=None): headerlist = headers.lower().replace("_"," ").split(",") output = [] if not quiet: - output.append("Current conditions at " \ - + lines[0].split(", ")[1] + " (" \ - + id.upper() +")") + title = "Current conditions at %s" + place = lines[0].split(", ") + 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]) for header in headerlist: for line in lines: if line.lower().startswith(header + ":"): - if line.endswith(":0"): + if line.endswith(":0") or line.endswith(":1"): line = line[:-2] + if imperial: line = filter_units(line, units="imperial") + elif metric: line = filter_units(line, units="metric") 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): +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.""" if not city or not st: import sys @@ -131,6 +298,8 @@ def get_forecast(city, st, verbose=False, quiet=False, flines="0", furl=None): flines = int(flines) if not flines: flines = len(lines) - 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 quiet: output.append(line.replace(".", "", 1)) else: output.append(line.replace(".", " ", 1)) @@ -149,6 +318,50 @@ def get_options(config): import optparse 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 if config.has_option("default", "city"): default_city = config.get("default", "city") @@ -213,6 +426,16 @@ def get_options(config): default=default_id, 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 option_parser.add_option("-l", "--list", dest="list", @@ -220,6 +443,16 @@ def get_options(config): default=False, 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 if config.has_option("default", "murl"): default_murl = config.get("default", "murl") @@ -277,7 +510,15 @@ def get_options(config): default=default_verbose, 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() return options, arguments diff --git a/weatherrc b/weatherrc index 60e7342..3fa719b 100644 --- a/weatherrc +++ b/weatherrc @@ -1,7 +1,6 @@ -# Copyright (c) 2006-2008 Jeremy Stanley . -# Permission to use, copy, modify, and distribute this software is -# granted under terms provided in the LICENSE file distributed with -# this software. +# Copyright (c) 2006-2010 Jeremy Stanley . 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 @@ -838,6 +837,11 @@ City = Peoria ID = KPIA St = IL +[PIE] +City = Saint Petersburg +ID = KPIE +St = FL + [PIH] City = Pocatello ID = KPIH @@ -848,6 +852,16 @@ City = Pittsburgh ID = KPIT St = PA +[PNC] +City = Ponca City +ID = KPNC +St = OK + +[PNS] +City = Pensacola +ID = KPNS +St = FL + [PQI] City = Presque Isle ID = KPQI diff --git a/weatherrc.5 b/weatherrc.5 index e5be871..d490d76 100644 --- a/weatherrc.5 +++ b/weatherrc.5 @@ -1,5 +1,5 @@ -.TH WEATHERRC 5 "July 13, 2008" "" \" -*- nroff -*- -\" Copyright (c) 2006-2008 Jeremy Stanley . +.TH WEATHERRC 5 "March 15, 2010" "" \" -*- nroff -*- +\" Copyright (c) 2006-2010 Jeremy Stanley . \" Permission to use, copy, modify, and distribute this software is \" granted under terms provided in the LICENSE file distributed with \" this software. @@ -19,6 +19,15 @@ Multi-word values do not need quoting. .SH PARAMETERS These parameters are supported... .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 the city name (ex: Raleigh Durham) .TP @@ -40,6 +49,12 @@ the conditions headers to display (ex: temperature,wind) .B id the METAR station ID (ex: KRDU) .TP +.B imperial +filter/convert for US/UK units +.TP +.B metric +filter/convert for metric units +.TP .B murl METAR URL (ex: http://metar.org/%id%.txt) .TP @@ -51,6 +66,9 @@ the state abbreviation (ex: NC) .TP .B verbose 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 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 -- 2.11.0