2 # vi: set softtabstop=4 shiftwidth=4 tabstop=8 expandtab:
4 """A frontend for the OC Transpo Travel Planner."""
12 from PlannerExceptions
import *
14 def plan(start
, end
, time
):
15 """Plans a route between two Locations at a certain PlanTime."""
16 planner
= TravelPlannerClient()
17 planner
.feedStartLocation(start
)
18 planner
.feedEndLocation(end
)
19 html
= planner
.feedTime(time
)
20 return Itinerary
.Itinerary(start
, end
, time
, html
)
22 class TravelPlannerClient
:
24 # Set up a cookie-aware client.
25 self
.cj
= cookielib
.CookieJar()
26 self
.opener
= urllib2
.build_opener(urllib2
.HTTPCookieProcessor(self
.cj
))
28 # Initialize the session.
29 r
= self
._sendRequest
(self
.START_PAGE
, None)
31 def feedStartLocation(self
, loc
):
32 self
._sendRequest
(loc
.getPage(True), loc
.toPlannerParams(True))
34 def feedEndLocation(self
, loc
):
35 self
._sendRequest
(loc
.getPage(False), loc
.toPlannerParams(False))
37 def feedTime(self
, time
):
38 # name="tp_time" action="SelectTime.oci"
39 params
= time
.toPlannerParams()
40 r
= self
._sendRequest
("SelectTime.oci", params
)
44 def _sendRequest(self
, page
, params
):
45 url
= self
.URL_BASE
+ page
46 if params
is not None:
47 url
+= "?" + urllib
.urlencode(params
)
49 response
= self
.opener
.open(url
)
50 self
._checkObviousBadness
(response
)
52 html
= self
._grabLimitedResponse
(response
)
53 self
._scanForError
(html
)
56 def _checkObviousBadness(self
, response
):
57 if response
.code
!= 200:
58 raise TravelPlannerException("Got HTTP " + response
.code
59 + " error from server")
60 if "errorPage.oci" in response
.geturl():
61 # try obtaining a specific error string
62 html
= self
._grabLimitedResponse
(response
)
63 self
._scanForError
(html
)
65 # otherwise, throw a generic one
66 raise TravelPlannerException("Redirected to error page")
68 def _grabLimitedResponse(self
, response
):
74 if (count
<= self
.RESPONSE_SIZE_LIMIT
):
82 # Regular expressions: probably the worst way to parse HTML
83 # Probably the best thing to put in your breakfast cereal.
85 def _scanForError(self
, text
):
86 match
= _error_rx
.search(text
)
88 raise TravelPlannerException(match
.group("msg"))
90 URL_BASE
= "http://www.octranspo.com/tps/jnot/"
91 START_PAGE
= "startEN.oci"
92 RESPONSE_SIZE_LIMIT
= 100000
97 # <table cellpadding="0" cellspacing="0" summary="Warning message" class="warning" width="85%">
99 # <td><img src="tripPlanning/images/imgWarning.gif"></td>
100 # <td>The address you specified was not found. Please enter another.</td>
103 _error_re
= ('<table[^>]*class="(?:warning|error)[^>]*>\s*'
104 '<tr>(?:\s*<td>\s*<img[^>]*>\s*</td>)?'
105 # ?s: DOTALL: . matches \n
107 '\s*<td>(?s)\s*(?P<msg>[\d\D]*?)\s*</td>')
108 _error_rx
= re
.compile(_error_re
)