[FIX] unittests
[pywienerlinien] / scotty.py
index 80a26ec..4d22534 100644 (file)
--- a/scotty.py
+++ b/scotty.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
+
 from BeautifulSoup import BeautifulSoup, NavigableString
 from urllib2 import urlopen
 from urllib import urlencode
@@ -5,12 +8,16 @@ import settings
 from datetime import datetime, time
 from textwrap import wrap
 import argparse
+import sys
+import os.path
 
 POSITION_TYPES = ('stop', 'address', 'poi')
+TIMEFORMAT = '%H:%M'
+DEBUGLOG = os.path.expanduser('~/gotoVienna.debug')
 
-class ParserException(Exception):
+class ParserError(Exception):
     
-    def __init__(self, msg = 'Parser error'):
+    def __init__(self, msg='Parser error'):
         self.message = msg
 
 class PageType:
@@ -28,7 +35,7 @@ def search(origin_tuple, destination_tuple, dtime=None):
     destination, destination_type = destination_tuple
     if not origin_type in POSITION_TYPES or\
         not destination_type in POSITION_TYPES:
-        raise ParserException('Invalid position type')
+        raise ParserError('Invalid position type')
         
     post = settings.search_post
     post['name_origin'] = origin
@@ -38,7 +45,16 @@ def search(origin_tuple, destination_tuple, dtime=None):
     post['itdDateDayMonthYear'] = dtime.strftime('%d.%m.%Y')
     post['itdTime'] = dtime.strftime('%H:%M')
     params = urlencode(post)
-    return urlopen('%s?%s' % (settings.action, params))
+    url = '%s?%s' % (settings.action, params)
+    
+    try:
+        f = open(DEBUGLOG, 'a')
+        f.write(url + '\n')
+        f.close()
+    except:
+        print 'Unable to write to DEBUGLOG: %s' % DEBUGLOG
+    
+    return urlopen(url)
 
 
 class sParser:
@@ -46,36 +62,37 @@ class sParser:
     """
 
     def __init__(self, html):
-        self.bs = BeautifulSoup(html)
+        self.soup = BeautifulSoup(html)
     
     def check_page(self):
-        if self.bs.find('form', {'id': 'form_efaresults'}):
+        if self.soup.find('form', {'id': 'form_efaresults'}):
             return PageType.RESULT
         
-        if self.bs.find('div', {'class':'form_error'}):
+        if self.soup.find('div', {'class':'form_error'}):
             return PageType.CORRECTION
         
         return PageType.UNKNOWN
     
     def get_correction(self):
-        nlo = self.bs.find('select', {'id': 'nameList_origin'})
-        nld = self.bs.find('select', {'id': 'nameList_destination'})
+        nlo = self.soup.find('select', {'id': 'nameList_origin'})
+        nld = self.soup.find('select', {'id': 'nameList_destination'})
         
-        if not nlo or not nld:
+        if not nlo and not nld:
             raise ParserError('Unable to parse html')
         
-        origin = nlo.findAll('option')
-        destination = nld.findAll('option')
-        
-        if not origin:
+        if nlo:
+            origin = map(lambda x: x.text, nlo.findAll('option'))
+        else:
             origin = []
-        if not destination:
+        if nld:
+            destination = map(lambda x: x.text, nld.findAll('option'))
+        else:
             destination = []
         
         return (origin, destination)
     
     def get_result(self):
-        return rParser(str(self.bs))
+        return rParser(str(self.soup))
         
         
         
@@ -181,12 +198,12 @@ class rParser:
                                'date': rParser.get_date(x),
                                'time': rParser.get_time(x),
                                'duration': rParser.get_duration(x), # grab duration
-                               'change': rParser.get_change(x), 
+                               'change': rParser.get_change(x),
                                'price': rParser.get_price(x),
                            },
                            rows)
         else:
-            raise ParserError('Unable to parse details')
+            raise ParserError('Unable to parse overview')
 
         return overview
 
@@ -203,11 +220,88 @@ class rParser:
             try:
                 self._overview = self._parse_overview()
             except AttributeError:
-                f = open('DEBUG', 'w')
+                f = open(DEBUGLOG, 'w')
                 f.write(str(self.soup))
                 f.close()
 
         return self._overview
 
 if __name__ == '__main__':
-    pass
\ No newline at end of file
+    parser = argparse.ArgumentParser(description='Get public transport route for Vienna')
+    parser.add_argument('-o', metavar='name', type=str, help='origin', required=True)
+    parser.add_argument('-d', metavar='name', type=str, help='destination', required=True)
+    parser.add_argument('-ot', metavar='type', type=str, help='origin type: %s' % ' | '.join(POSITION_TYPES), default='stop', choices=POSITION_TYPES)
+    parser.add_argument('-dt', metavar='type', type=str, help='destination type: %s' % ' | '.join(POSITION_TYPES), default='stop', choices=POSITION_TYPES)
+
+    args = parser.parse_args()
+    html = search((args.o, args.ot), (args.d, args.dt)).read()
+    
+    parser = sParser(html)
+    state = parser.check_page()
+    
+    if state == PageType.CORRECTION:
+        try:
+            cor = parser.get_correction()
+            if cor[0]:
+                print
+                print '* Origin ambiguous:'
+                lo = None
+                while not lo or not lo.isdigit() or int(lo) > len(cor[0]):
+                    i = 1
+                    for c in cor[0]:
+                        print '%d. %s' % (i, c)
+                        i += 1
+                    lo = sys.stdin.readline().strip()
+                
+                args.o = cor[0][int(lo) - 1]
+                
+            if cor[1]:
+                print
+                print '* Destination ambiguous:'
+                ld = None
+                while not ld or not ld.isdigit() or int(ld) > len(cor[1]):
+                    j = 1
+                    for c in cor[1]:
+                        print '%d. %s' % (j, c)
+                        j += 1
+                    ld = sys.stdin.readline().strip()
+                    
+                args.d = cor[1][int(ld) - 1]
+            
+            html = search((args.o.encode('UTF-8'), args.ot), (args.d.encode('UTF-8'), args.dt)).read()
+    
+            parser = sParser(html)
+            state = parser.check_page()
+            
+        except ParserError:
+            print 'PANIC at correction page'
+            
+    if state == PageType.RESULT:
+        parser = rParser(html)
+        try:
+            overviews = parser.overview
+            details = parser.details
+            l = ''
+            while not l == 'q':
+                for r in range(len(overviews)):
+                    print '%d. [%s] %s-%s (%s)' % (r + 1, overviews[r]['date'], overviews[r]['time'][0], overviews[r]['time'][1], overviews[r]['duration'])
+                print 'q. Quit'
+                l = sys.stdin.readline().strip()
+                print
+                print '~' * 100
+                
+                if l.isdigit() and int(l) <= len(details):
+                    for detail in details[int(l) - 1]:
+                        if detail['time'] and detail['station']:
+                            time = '%s - %s' % (detail['time'][0].strftime(TIMEFORMAT), detail['time'][1].strftime(TIMEFORMAT))
+                            print '[%s] %s\n%s' % (time, ' -> '.join(detail['station']), '\n'.join(detail['info']))
+                        else:
+                            print '\n'.join(detail['info'])
+                        print '-' * 100
+                print
+                
+        except ParserError:
+            print 'parsererror'
+            
+    elif state == PageType.UNKNOWN:
+        print 'PANIC unknown result'