From: Jeremy Stanley Date: Sun, 26 Mar 2006 17:08:52 +0000 (+0000) Subject: Imported from archive. X-Git-Tag: 1.0^0 X-Git-Url: https://www.yuggoth.org/gitweb?a=commitdiff_plain;ds=sidebyside;h=155d8574dfe4715cdf1e62afcc95cf8b871bf0e7;p=weather.git Imported from archive. * Initial release 1.0. --- 155d8574dfe4715cdf1e62afcc95cf8b871bf0e7 diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..9180903 --- /dev/null +++ b/INSTALL @@ -0,0 +1,39 @@ +BASIC UNIX INSTALLATION INSTRUCTIONS FOR THE WEATHER UTILITY + + +PREREQUISITES + +You need the Python interpreter installed somewhere in your path +(most modern UNIX derivatives come with one already). The weather +executable assumes your Python interpreter is /usr/bin/python so you +may need to edit the #! line if that is not the case. If you need +Python for some reason, 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). + + +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/). + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1f63def --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2006 Jeremy Stanley , all rights reserved. + +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. + diff --git a/weather b/weather new file mode 100755 index 0000000..ffaf2a5 --- /dev/null +++ b/weather @@ -0,0 +1,31 @@ +#!/usr/bin/python + +# Copyright (c) 2006 Jeremy Stanley , all rights reserved. +# Licensed per terms in the LICENSE file distributed with this software. + +import weather + +# initialize options and configs +selections = weather.Selections() +get = selections.get +get_boolean = selections.get_boolean + +# this mode just lists the aliases defined in the config +if get_boolean("list"): print weather.list_aliases(selections.config) + +# normal operation +else: + for argument in selections.arguments: + if get_boolean("conditions", argument): + print weather.get_metar( + get("id", argument), + get_boolean("verbose", argument) + ) + if not get_boolean("conditions", argument) \ + or get_boolean("forecast", argument): + print weather.get_forecast( + get("city", argument), + get("st", argument), + get_boolean("verbose", argument) + ) + diff --git a/weather.1 b/weather.1 new file mode 100644 index 0000000..05aa865 --- /dev/null +++ b/weather.1 @@ -0,0 +1,87 @@ +.TH WEATHER 1 "March 26, 2006" +.SH NAME +weather \- command\-line tool to obtain weather conditions and forecasts +.SH SYNOPSIS +.B weather [ options ] [ alias [ alias [...] ] ] +.SH DESCRIPTION +This utility is intended to provide quick access to current weather +conditions and forecasts. Presently, it is capable of providing data for +localities throughout the United States of America by retrieving and +processing METAR data from the National Oceanic and Atmospheric +Administration and forecasts from the National Weather Service. Behavior +can be determined by command\-line options and specification of zero or +more aliases. Aliases are defined in weatherrc files, as a convenient +means of grouping option combinations together using a short name. +Specifying multiple aliases on the command line causes the utility to +output data for each, as if it had been invoked multiple times. If no +alias is specified, then an alias of "default" is used (assuming it has +been defined) or the built\-in default values are chosen (if it has not). +.SH OPTIONS +A summary of options is included below. +.TP +.B \-\-version +show program's version number and exit +.TP +.B \-h, \-\-help +show a help message and exit +.TP +.B \-cCITY, \-\-city=CITY +the city name (ex: "Raleigh Durham") +.TP +.B \-f, \-\-forecast +include a local forecast +.TP +.B \-iID, \-\-id=ID +the METAR station ID (ex: KRDU) +.TP +.B \-l, \-\-list +print a list of configured aliases +.TP +.B \-n, \-\-no\-conditions +disable output of current conditions (implies \-\-forecast) +.TP +.B \-sST, \-\-st=ST +the state abbreviation (ex: NC) +.TP +.B \-v, \-\-verbose +show full decoded feeds +.SH FILES +.B weather +may additionally obtain configuration data from a system\-wide +configuration file, a per\-user configuration file, and a local +directory configuration file. The file format and configuration options +are described in +.BR weatherrc (5) . +They are aggregated in the following order: +.TP +.B /etc/weatherrc +the system\-wide configuration +.TP +.B ~/.weatherrc +the per\-user configuration (can be used to override the above) +.TP +.B ./.weatherrc +the local directory configuration (can be used to override the above) +.SH EXAMPLES +.TP +.B weather +View output for the defined default alias, or the built-in default values +if there is no default alias defined in the configuration files. +.TP +.B weather -i kavl +Display current conditions at the KAVL METAR station. +.TP +.B weather -n -c asheville -s nc +See a forecast for the Asheville, NC area. +.TP +.B weather -fv gso +Get the full decoded METAR for the station associated with the gso alias, +and the forecast data for the City/State associated with the gso alias, +without filtering or fancy formatting. +.TP +.B weather home work +Show current conditions for both the home and work aliases in that order. +.SH SEE ALSO +.BR weatherrc (5) +.SH AUTHOR +Utility and manual written by Jeremy Stanley . diff --git a/weather.py b/weather.py new file mode 100644 index 0000000..6073a5c --- /dev/null +++ b/weather.py @@ -0,0 +1,170 @@ +# Copyright (c) 2006 Jeremy Stanley , all rights reserved. +# Licensed per terms in the LICENSE file distributed with this software. + +version = "1.0" + +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() + 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: argument = "default" + if self.config.has_option(argument, option): + return self.config.get(argument, option) + else: return self.options.__dict__[option] + def get_boolean(self, option, argument=None): + """Get data and coerce to a boolean if necessary.""" + data = self.get(option, argument) + 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 + +def sorted(data): + """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() + +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 + ":"): + 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_options(): + """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) + option_parser.add_option("-c", "--city", + dest="city", + default="Raleigh Durham", + help="the city name (ex: \"Raleigh Durham\")") + option_parser.add_option("-f", "--forecast", + dest="forecast", + action="store_true", + default=False, + help="include forecast (needs -c and -s)") + option_parser.add_option("-i", "--id", + dest="id", + default="KRDU", + 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") + option_parser.add_option("-n", "--no-conditions", + dest="conditions", + action="store_false", + default=True, + help="disable output of current conditions (implies --forecast)") + option_parser.add_option("-s", "--st", + dest="st", + default="NC", + help="the state abbreviation (ex: NC)") + option_parser.add_option("-v", "--verbose", + dest="verbose", + action="store_true", + default=False, + help="show full decoded feeds") + 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 = [ + ".weatherrc", + os.path.expanduser("~/.weatherrc"), + "/etc/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 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 + diff --git a/weatherrc b/weatherrc new file mode 100644 index 0000000..ae09d8e --- /dev/null +++ b/weatherrc @@ -0,0 +1,15 @@ +[AVL] +City = Asheville +ID = KAVL +St = NC + +[GSO] +City = Greensboro +ID = KGSO +St = NC + +[RDU] +City = Raleigh Durham +ID = KRDU +St = NC + diff --git a/weatherrc.5 b/weatherrc.5 new file mode 100644 index 0000000..0d6b999 --- /dev/null +++ b/weatherrc.5 @@ -0,0 +1,72 @@ +.TH WEATHERRC 5 "March 26, 2006" +.SH NAME +weatherrc \- configuration file format for the +.BR weather (1) +utility +.SH DESCRIPTION +The weatherrc file format is intended to specify a set of macros +by which to group a METAR station ID for current conditions data with a +city/state combination for a forecast, but many of the other +command\-line options/flags for the weather utility can be specified as +well. The file is organized as an INI-format config, with the alias name +in [] brackets and the associated parameter/value pairs on following +lines. Parameters and their values as separated by = or : characters. +Multi-word values do not need quoting. +.SH PARAMETERS +These parameters are supported... +.TP +.B city +the city name (ex: Raleigh Durham) +.TP +.B forecast +include a local forecast (possible values are False and True or 0 and 1) +.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) +.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 EXAMPLES +Following is an example +.B ~/.weatherrc +defining the default settings to be used when running the utility with no +aliases specified, and a couple definitions for aliases named home and +work... +.P +.RS +[default] +.br +City = Asheville +.br +Forecast = True +.br +ID = KAVL +.br +St = NC +.P +[home] +.br +City = Raleigh Durham +.br +ID = KRDU +.br +St = NC +.P +[work] +.br +City = Greensboro +.br +ID = KGSO +.br +St = NC +.RE +.SH SEE ALSO +.BR weather (1) +.SH AUTHOR +Specification and manual written by Jeremy Stanley .