Imported from archive. 1.0
authorJeremy Stanley <fungi@yuggoth.org>
Sun, 26 Mar 2006 17:08:52 +0000 (17:08 +0000)
committerJeremy Stanley <fungi@yuggoth.org>
Sun, 26 Mar 2006 17:08:52 +0000 (17:08 +0000)
* Initial release 1.0.

INSTALL [new file with mode: 0644]
LICENSE [new file with mode: 0644]
weather [new file with mode: 0755]
weather.1 [new file with mode: 0644]
weather.py [new file with mode: 0644]
weatherrc [new file with mode: 0644]
weatherrc.5 [new file with mode: 0644]

diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
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 (file)
index 0000000..1f63def
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, 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 (executable)
index 0000000..ffaf2a5
--- /dev/null
+++ b/weather
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+
+# Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, 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 (file)
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 <fungi@yuggoth.org>.
diff --git a/weather.py b/weather.py
new file mode 100644 (file)
index 0000000..6073a5c
--- /dev/null
@@ -0,0 +1,170 @@
+# Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, 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 (file)
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 (file)
index 0000000..0d6b999
--- /dev/null
@@ -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 <fungi@yuggoth.org>.