|  | #!/usr/bin/env python | 
        
          |  |  | 
        
          |  | """ | 
        
          |  | mnr.py: Quick CLI interface with MTA-Metro North Train Time | 
        
          |  | * twitter: @oogali * github: oogali * linkedin: oogali * | 
        
          |  |  | 
        
          |  | """ | 
        
          |  |  | 
        
          |  | import os, sys, datetime, requests | 
        
          |  | from lxml import html | 
        
          |  | from collections import namedtuple | 
        
          |  |  | 
        
          |  | class Travel: | 
        
          |  | Schedule = namedtuple('Schedule', 'dtime destination track status') | 
        
          |  |  | 
        
          |  | def __init__(self, station='Grand Central Terminal', destination=None): | 
        
          |  | # initialize a http session for cookie mgmt | 
        
          |  | self.session = requests.Session() | 
        
          |  |  | 
        
          |  | # load our list of stations from mta.info | 
        
          |  | self._stations = {} | 
        
          |  | self.load_stations() | 
        
          |  |  | 
        
          |  | # set our starting and ending stations | 
        
          |  | # default to Grand Central, if start isn't specified | 
        
          |  | station = station or 'Grand Central Terminal' | 
        
          |  | self._destination = destination | 
        
          |  |  | 
        
          |  | # try to find our starting station in the list returned by mta.info | 
        
          |  | if station in self._stations.keys(): | 
        
          |  | self._station = self._stations[station] | 
        
          |  | else: | 
        
          |  | for s in self._stations.keys(): | 
        
          |  | if station.lower() in s.lower(): | 
        
          |  | self._station = self._stations[s] | 
        
          |  | break | 
        
          |  |  | 
        
          |  | def load_stations(self): | 
        
          |  | # grab list | 
        
          |  | r = self.session.get('http://as0.mta.info/mnr/mstations/default.cfm') | 
        
          |  |  | 
        
          |  | # parse html | 
        
          |  | doc = html.fromstring(r.text) | 
        
          |  |  | 
        
          |  | # use xpath to find all station options | 
        
          |  | for option in doc.xpath('//select[@name="P_AVIS_ID"]/option'): | 
        
          |  | idx, name = option.attrib.get('value').split(',', 2) | 
        
          |  | self._stations[name] = { 'idx': idx, 'name': name } | 
        
          |  |  | 
        
          |  | def station(self): | 
        
          |  | # return a title-capitalized version of the starting station name | 
        
          |  | return self._station['name'].title() | 
        
          |  |  | 
        
          |  | def schedule(self): | 
        
          |  | # get time+track information from mta.info for given station | 
        
          |  | r = self.session.post('http://as0.mta.info/mnr/mstations/station_status_display.cfm', data={ | 
        
          |  | 'P_AVIS_ID': '{},{}'.format(self._station['idx'], self._station['name']), | 
        
          |  | 'Get Train Status': 'Get Train Status', | 
        
          |  | 'refered': 'ault.cfm' | 
        
          |  | }) | 
        
          |  |  | 
        
          |  | # parse html returned from mta.info | 
        
          |  | scheduled = [] | 
        
          |  | doc = html.fromstring(r.text) | 
        
          |  |  | 
        
          |  | # loop through each result row | 
        
          |  | for row in doc.xpath('//table/tr'): | 
        
          |  | cols = row.xpath('td') | 
        
          |  | # filter out first header row | 
        
          |  | if cols[0].text == 'Scheduled Time': | 
        
          |  | continue | 
        
          |  |  | 
        
          |  | # if user has specified a destination, filter out trains that don't match | 
        
          |  | if self._destination is not None and self._destination.lower() not in cols[1].text.lower(): | 
        
          |  | continue | 
        
          |  |  | 
        
          |  | # filter out trains making their last stop | 
        
          |  | if self._station['name'].lower() == cols[1].text.lower(): | 
        
          |  | continue | 
        
          |  |  | 
        
          |  | # add this train+track to scheduled departure list | 
        
          |  | scheduled.append(Travel.Schedule(dtime=cols[0].text, destination=cols[1].text, track=cols[2].text, status=cols[3].text)) | 
        
          |  |  | 
        
          |  | return scheduled | 
        
          |  |  | 
        
          |  | def main(argv=None): | 
        
          |  | if argv is None: | 
        
          |  | argv = sys.argv | 
        
          |  | args = argv[1:] | 
        
          |  |  | 
        
          |  | t = None | 
        
          |  | if len(args) <= 0: | 
        
          |  | t = Travel() | 
        
          |  | else: | 
        
          |  | if args[0] == 'from': | 
        
          |  | args.pop(0) | 
        
          |  |  | 
        
          |  | to = None | 
        
          |  | station = None | 
        
          |  | destination = None | 
        
          |  | if 'to' in args: | 
        
          |  | to = args.index('to') | 
        
          |  | args.pop(to) | 
        
          |  |  | 
        
          |  | if to == 0: | 
        
          |  | station = None | 
        
          |  | destination = ' '.join(args) | 
        
          |  | else: | 
        
          |  | station = ' '.join(args[0:to]) | 
        
          |  | destination = ' '.join(args[to:]) | 
        
          |  | else: | 
        
          |  | station = ' '.join(args) | 
        
          |  |  | 
        
          |  | t = Travel(station=station, destination=destination) | 
        
          |  |  | 
        
          |  | print '\n*** {} ***\n'.format(datetime.datetime.now().strftime('%l:%M %p')) | 
        
          |  | print 'Leaving from {}'.format(t.station()) | 
        
          |  |  | 
        
          |  | print '\nScheduled Trains' | 
        
          |  | print '----------------' | 
        
          |  |  | 
        
          |  | for schedule in t.schedule(): | 
        
          |  | if len(schedule.track.replace(' ', '')) == 0: | 
        
          |  | track = '(no track yet)' | 
        
          |  | else: | 
        
          |  | track = 'Track {}'.format(schedule.track) | 
        
          |  |  | 
        
          |  | print '{}\tTo {}\t{}\t{}'.format(schedule.dtime, schedule.destination.ljust(20), track.ljust(10), schedule.status) | 
        
          |  | print | 
        
          |  |  | 
        
          |  | return 0 | 
        
          |  |  | 
        
          |  | if __name__ == '__main__': | 
        
          |  | sys.exit(main()) |