[FIX] unittests
[pywienerlinien] / iTip.py
1 #!/usr/bin/env python
2 # -*- coding: UTF-8 -*-
3
4 from BeautifulSoup import BeautifulSoup
5 from urllib2 import urlopen
6 import settings
7 from datetime import time
8 import argparse
9 import re
10
11 class iParser:
12
13     def __init__(self):
14         self._stations = {}
15         self._lines = {}
16
17     def get_stations(self, name):
18         """ Get station by direction
19         {'Directionname': [('Station name', 'url')]}
20         """
21         if not self._stations.has_key(name):
22             st = {}
23             
24             if not self.lines.has_key(name):
25                 return None
26             
27             bs = BeautifulSoup(urlopen(self.lines[name]))
28             tables = bs.findAll('table', {'class': 'text_10pix'})
29             for i in range(2):
30                 dir = tables[i].div.contents[-1].strip(' ')
31                 
32                 sta = []
33                 for tr in tables[i].findAll('tr', {'onmouseout': 'obj_unhighlight(this);'}):
34                     if tr.a:
35                         sta.append((tr.a.text, settings.line_overview + tr.a['href']))
36                     else:
37                         sta.append((tr.text.strip(' '), None))
38                     
39                 st[dir] = sta
40             self._stations[name] = st
41
42         return self._stations[name]
43
44     @property
45     def lines(self):
46         """ Dictionary of Line names with url as value
47         """
48         if not self._lines:
49             bs = BeautifulSoup(urlopen(settings.line_overview))
50             # get tables
51             lines = bs.findAll('td', {'class': 'linie'})
52             
53             for line in lines:
54                 if line.a:
55                     href = settings.line_overview + line.a['href']
56                     if line.text:
57                         self._lines[line.text] = href
58                     elif line.img:
59                         self._lines[line.img['alt']] = href
60                         
61         return self._lines
62
63     def get_departures(self, url):
64         """ Get list of next departures
65         integer if time until next departure
66         time if time of next departure
67         """
68         
69         #TODO parse line name and direction for station site parsing
70         
71         if not url:
72             # FIXME prevent from calling this method with None
73             return []
74
75         bs = BeautifulSoup(urlopen(url))
76         result_lines = bs.findAll('table')[-1].findAll('tr')
77         
78         dep = []
79         for tr in result_lines[1:]:
80             th = tr.findAll('th')
81             if len(th) < 2:
82                 #TODO replace with logger
83                 print "[DEBUG] Unable to find th in:\n%s" % str(tr)
84                 continue
85             
86             # parse time
87             time = th[-2].text.split(' ')
88             if len(time) < 2:
89                 print 'Invalid time: %s' % time
90                 continue
91             
92             time = time[1]
93             
94             if time.find('rze...') >= 0:
95                     dep.append(0)
96             elif time.isdigit():
97                 # if time to next departure in cell convert to int
98                 dep.append(int(time))
99             else:
100                 # check if time of next departue in cell
101                 t = time.strip('&nbsp;').split(':')
102                 if len(t) == 2 and all(map(lambda x: x.isdigit(), t)):
103                     t = map(int, t)
104                     dep.append(time(*t))
105                 else:
106                     # Unexpected content
107                     #TODO replace with logger
108                     print "[DEBUG] Invalid data:\n%s" % time
109                 
110         return dep
111     
112 if __name__ == '__main__':
113     parser = argparse.ArgumentParser(description='Get realtime public transport information for Vienna')
114     parser.add_argument('-l', metavar='name', type=str, help='line name')
115     parser.add_argument('-s', metavar='name', type=str, help='station name')   
116
117     args = parser.parse_args()
118     
119     itip = iParser()
120     lines = itip.lines
121     if args.l:
122         l = args.l.upper()
123     else:
124         l = None
125     if args.s:
126         s = args.s.decode('UTF-8')
127     else:
128         s = ''
129     
130     if l and l in lines:
131         stations = itip.get_stations(l)
132         for key in stations.keys():
133             if not s:
134                 print '* %s:' % key
135             for station in stations[key]:
136                 if s:
137                     if s.startswith(station[0]) or station[0].startswith(s):
138                         # FIXME
139                         print '* %s\n  %s .....' % (key, station[0]), itip.get_departures(station[1])
140                 else:
141                     print '    %s' % station[0]
142     
143     elif not l:
144         line = {'U-Bahn': '|', 'Strassenbahn': '|', 'Bus': '|', 'Andere': '|', 'Nightline': '|'}
145         lines_sorted = lines.keys()
146         lines_sorted.sort()
147         for li in lines_sorted:
148             if li.isdigit():
149                 type = 'Strassenbahn'
150             elif li.endswith('A') or li.endswith('B') and li[1].isdigit():
151                 type = 'Bus'
152             elif li.startswith('U'):
153                 type = 'U-Bahn'
154             elif li.startswith('N'):
155                 type = 'Nightline'
156             else:
157                 type = 'Andere'
158                 
159             line[type] += ' %s |' % li
160         for kv in line.items():
161             print "%s:\n%s" % kv