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