From bb5dfcd19312932113b678d78d2d1d34c70db3c1 Mon Sep 17 00:00:00 2001
From: Mattias Andrée <maandree@operamail.com>
Date: Thu, 27 Mar 2014 23:26:06 +0100
Subject: doc + make it possible to select download command for weather
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Mattias Andrée <maandree@operamail.com>
---
 src/weather.py | 43 ++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 38 insertions(+), 5 deletions(-)

(limited to 'src')

diff --git a/src/weather.py b/src/weather.py
index 33e58ac..f0f0de3 100644
--- a/src/weather.py
+++ b/src/weather.py
@@ -18,11 +18,19 @@
 from subprocess import Popen, PIPE
 
 
-def weather(station):
+def weather(station, downloader = None):
     '''
     Get a brief weather report
     
-    @param   station:str    The station's International Civil Aviation Organization airport code
+    Airports should publish METAR (Meteorological Aerodrome Report) reports at XX:20 and XX:50,
+    it can presumable take some time before the collection server we use (weather.noaa.gov) have
+    received it. Additionally some airports do not update while closed, and updates while closed
+    are less accurate.
+    
+    @param   station:str                      The station's International Civil Aviation
+                                              Organization airport code
+    @param   downloader:(url:str)?→list<str>  A function that, with an URL as input, returns
+                                              a command to download the file at the URL to stdout
     @return  :(sky:str, visiblity:(:int, :float)?, weather:list<str>)?
                             The sky condition, visiblity and weather. Sky condition values include
                             ‘clear’, ‘mostly clear’, ‘partly cloudy’, ‘mostly cloudy’, ‘overcast’
@@ -33,42 +41,67 @@ def weather(station):
                             be `None`. The weather is a list that can, and often is, empty. `None`
                             is return if observation data cannot be downloaded.
     '''
+    ## URI of METAR
     url = 'http://weather.noaa.gov/pub/data/observations/metar/decoded/%s.TXT'
     url %= station.upper()
-    proc = Popen(['wget', url, '-O', '-'], stdout = PIPE, stderr = PIPE)
+    ## Download METAR
+    # Use wget if not specified
+    if downloader is None:
+        downloader = lambda u : ['wget', u, '-O', '-']
+    proc = Popen(downloader(url), stdout = PIPE, stderr = PIPE)
+    ## Wait for download to finish and fetch output
     output = proc.communicate()[0]
+    # Ignore output if it was not successful
     if not proc.returncode == 0:
         return None
+    ## Create field table from
     output = output.decode('utf-8', 'replace').split('\n')
     output = [line.lower().split(': ') for line in output if ': ' in line]
     output = dict([(line[0], ': '.join(line[1:])) for line in output])
+    ## Get sky condition, assume clear (although often not) if omitted
     sky_conditions = 'clear' if 'sky conditions' not in output else output['sky conditions']
+    ## Get visibility range
     visibility = None
     try:
         if 'visibility' in output:
+            # Remove tail digit
             visibility = output['visibility'].split(':')[0]
+            # Remove unit, it is always miles the decoded part
             visibility = visibility.replace(' mile(s)', '')
             visibility = visibility.replace(' miles', '')
             visibility = visibility.replace(' mile', '')
+            # Range is assumed approximate if not specified
             visibility_eq = 0
             if visibility.startswith('greater than '):
+                # Range is a lower bound
                 visibility_eq = 1
                 visibility = visibility[len('greater than '):]
             if visibility.startswith('less than '):
+                # Range is an upper bound
                 visibility_eq = -1
                 visibility = visibility[len('less than '):]
-            if len(list(filter(lambda c : not (('0' <= c <= '9') or (c in ' /')), visibility))) == 0:
+            if len(list(filter(lambda c : not (('0' <= c <= '9') or (c in ' /.')), visibility))) == 0:
+                # Parse mixed numeral or decimal form
                 visibility = sum([eval(v) for v in visibility.split(' ')])
+                # Pack boundary information and range (converted to kilometers)
                 visibility = (visibility_eq, visibility * 1.609)
             else:
                 visibility = None
     except:
+        ## `eval` failed (probably)
         visibility = None
+    ## Get weather
     weather = '' if 'weather' not in output else output['weather']
+    ## Unify conjnuctions
     weather = weather.replace(',', ';').replace(' with ', ';')
+    ## Remove undesired details
+    # Not important as we are not pilots, we are probably far away
     weather = weather.replace(' in the vicinity', '')
-    weather = weather.replace(' observed', '')
+    # Duration is not important for use either
     weather = weather.replace(' during the past hour', '')
+    # Unimportant detail
+    weather = weather.replace(' observed', '')
+    ## Split at conjunction
     weather = [w.replace(';', '').strip() for w in weather.split(';') if not w == '']
     return (sky_conditions, visibility, weather)
 
-- 
cgit v1.2.3-70-g09d2