Flo

Valid XHTML 1.1
Valid CSS
optimized for firefox
never anoyed me
i use jabber
100%
just say NO!
website by Florian Schmidt
mvv is the "Münchner Verkehrs- und Trafiverbund" and i hate to use their webapp to query bus- and subway schedules. it is too slow and bloated, so i wrote a small command line interface with configurable short names:
$ mvv flo titanic
from Götheplatz to Kurfürstenplatz starting at 2006-10-25 23:05
connect... download... parse... done.

------------------------------------------------------------------------------------------------------------
23:04->23:06  Goetheplatz -> Sendlinger Tor         U6      Richtung Fröttmaning (dann zu Fuß ca. 2 Min.)
23:10->23:21  Sendlinger Tor -> Kurfürstenplatz     Tram27  Richtung Petuelring

------------------------------------------------------------------------------------------------------------
23:09->23:11  Goetheplatz -> Sendlinger Tor         U3      Richtung Olympiazentrum (dann zu Fuß ca. 2 Min.)
23:13->23:20  Sendlinger Tor -> Hohenzollernplatz   U2      Richtung Feldmoching (dann zu Fuß ca. 3 Min.)
23:23->23:25  Hohenzollernplatz -> Kurfürstenplatz  Tram27  Richtung Schwanseestraße

------------------------------------------------------------------------------------------------------------
23:09->23:18  Goetheplatz -> Münchner Freiheit      U3      Richtung Olympiazentrum (dann zu Fuß ca. 3 Min.)
23:22->23:26  Münchner Freiheit -> Kurfürstenplatz  Bus53   Richtung Aidenbachstraße

------------------------------------------------------------------------------------------------------------
23:19->23:21  Goetheplatz -> Sendlinger Tor         U3      Richtung Olympiazentrum (dann zu Fuß ca. 2 Min.)
23:23->23:30  Sendlinger Tor -> Hohenzollernplatz   U2      Richtung Feldmoching (dann zu Fuß ca. 3 Min.)
23:36->23:37  Hohenzollernplatz -> Kurfürstenplatz  Tram12  Richtung Scheidplatz
it shows all needed information in a nicely formatted manner. here the python source:
python mvv
#!/usr/bin/python2.4 -u
# -*- coding: latin1 -*-
#
version = "0.2"
# schmidt_florian@gmx.de
# changes:
# 2006-03-26 fs
# recognize times at the next day
# show hints like time to walk, or other additinal information
# be more tolerant about argument order
# automatically adjust column width
# possibility to limit output width (maxterminallength)
# origin and destination can be prefixed by desired place
# default origin_place = "m" means that origin Goetheplatz
# is the same as m:Goetheplatz

origin = "Götheplatz"
origin_place = "m"
destination_place = "m"

show_hints = True
mode = "dep"
format = "pretty"
maxterminallength = 80 # funktioniert auch, ist aber oft nicht huebsch
maxterminallength = 100         # sollte gutes mittel sein ohne hints
maxterminallength = 111         # sollte gutes mittel sein mit hints
maxterminallength = 130         # luxus
later = 0

import datetime
d = datetime.datetime.now()

import urllib
import cgi
from urllib2 import *
from HTMLParser import HTMLParser
import sys

#if __name__ == "__main__":
#print cgi.parse_qs("target_url=%2Fde%2Fhome%2Ffahrgastinformation%2Fefa%2Ffahrtauskunft%2Findex.html&sessionID=0&language=de&ptOptionsActive=0&execInst=normal&placeInfo_origin=invalid&nameInfo_origin=invalid&typeInfo_origin=invalid&placeInfo_destination=invalid&nameInfo_destination=invalid&typeInfo_destination=invalid&requestID=0&command=&itdLPxx_help=&itOptionsActive=&hiddendate=&itdDateYear=2006&place_origin=m&type_origin=stop&name_origin=Goetheplatz&place_destination=m&type_destination=stop&name_destination=Rotkreuzplatz&itdTripDateTimeDepArr=arr&itdTimeHour=19&itdTimeMinute=30&itdDateDay=23&itdDateMonth=03&x=39&y=8")


def usage():
        print """pymvv v%s by Florian Schmidt 2006-03-25
usage:
        Rotkreuzplatz start from default to Rotkreuzplatz now
        Goetheplatz Rotkreuzplatz start from Goetheplatz
        Rotkreuzplatz +5 start in 5 minutes from now
        Rotkreuzplatz arrive 17:30 arrive at 17:30
        Goetheplatz Rotkreuzplatz arrive 17:30 arrive at 17:30
        Goetheplatz Rotkreuzplatz start 17:30 start at 17:30
        Goetheplatz Rotkreuzplatz start 17:30 csv start at 17:30 output as csv
        Goetheplatz Rotkreuzplatz nohints to Rotkreuzplatz, without hints
        """ % version
        sys.exit(0)
        
if len(sys.argv) < 2:
        usage()
        
startlen = 0
while len(sys.argv) != startlen:
        startlen = len(sys.argv)
        if sys.argv[-1] == "csv":
                format = "csv"
                del sys.argv[-1]
        if sys.argv[-1] == "nohints":
                show_hints = False
                del sys.argv[-1]
        if sys.argv[-1][0] == '+' or sys.argv[-1][0] == '-':
                later = int(sys.argv[-1])
                del sys.argv[-1]
        
        if len(sys.argv) > 2 and sys.argv[-2] == "arrive":
                t = map(int, sys.argv[-1].split(":"))
                d = d.replace(hour=t[0], minute=t[1])
                mode = "arr"
                del sys.argv[-1]
                del sys.argv[-1]

        if len(sys.argv) > 2 and sys.argv[-2] == "start":
                t = map(int, sys.argv[-1].split(":"))
                d = d.replace(hour=t[0], minute=t[1])
                mode = "dep"
                del sys.argv[-1]
                del sys.argv[-1]

if len(sys.argv) == 3:
        origin = sys.argv[1].strip()
        destination = sys.argv[2].strip()
        del sys.argv[-1]
        del sys.argv[-1]
elif len(sys.argv) == 2:
        destination = sys.argv[1].strip()
        del sys.argv[-1]
else:
        usage()

if d < datetime.datetime.now():
        d += datetime.timedelta(days=1)
        
if later != 0:
        td = datetime.timedelta(minutes=later)
        d = d + td

mappings = {}
try:
        fp = file(os.path.expanduser("~/.mvv"), "rb")
        for line in fp.readlines():
                line.strip()
                if not line or line.find("=") == -1:
                        continue
                m = line.split("=", 1)
                mappings[m[0].strip()] = m[1].strip()
        fp.close()
except:
        pass # no mappings
        
if origin in mappings:
        origin = mappings[origin]
if destination in mappings:
        destination = mappings[destination]
        
if format == "pretty":
        if mode == "arr":
                print "from %s to %s arriving at %.16s" % (origin, destination, d)
        else:
                print "from %s to %s starting at %.16s" % (origin, destination, d)

dest = destination.split(":", 1)
if len(dest) > 1:
        destination_place, destination = dest

org = origin.split(":", 1)
if len(org) > 1:
        origin_place, origin = org
        
params = {
        'execInst': 'normal',
        'itdDateDay': d.day,
        'itdDateMonth': d.month,
        'itdDateYear': d.year,
        'itdTimeHour': d.hour,
        'itdTimeMinute': d.minute,
        'itdTripDateTimeDepArr': mode,
        'language': 'de',
        'name_destination': destination,
        'nameInfo_destination': 'invalid',
        'nameInfo_origin': 'invalid',
        'name_origin': origin,
        'place_destination': destination_place,
        'placeInfo_destination': 'invalid',
        'placeInfo_origin': 'invalid',
        'place_origin': origin_place,
        'ptOptionsActive': '0',
        'requestID': '0',
        'sessionID': '0',
        'target_url': '/de/home/fahrgastinformation/efa/fahrtauskunft/index.html',
        'type_destination': 'stop',
        'typeInfo_destination': 'invalid',
        'typeInfo_origin': 'invalid',
        'type_origin': 'stop',
        'x': '39',
        'y': '8',
}
#http://efa.mvv-muenchen.de/mvv/XSLT_TRIP_REQUEST2?&nameInfo_destination=invalid&itdDateDay=23&typeInfo_destination=invalid&x=39&typeInfo_origin=invalid&y=8&type_origin=stop&placeInfo_destination=invalid&sessionID=0&target_url=%2Fde%2Fhome%2Ffahrgastinformation%2Fefa%2Ffahrtauskunft%2Findex.html&name_destination=Rotkreuzplatz&itdTripDateTimeDepArr=arr&requestID=0&itdTimeMinute=30&place_destination=m&language=de&placeInfo_origin=invalid&ptOptionsActive=0&place_origin=m&type_destination=stop&itdDateMonth=03&execInst=normal&itdDateYear=2006&itdTimeHour=19&nameInfo_origin=invalid&name_origin=Goetheplatz&language=de
params = urllib.urlencode(params)
#url = "http://www.mvv-muenchen.de/cgi-bin/utf82iso?%s" % params
url = "http://efa.mvv-muenchen.de/mvv/XSLT_TRIP_REQUEST2?%s" % params
request = Request(url)
if format == "pretty":
        print "connect...",
f = urlopen(request)
if format == "pretty":
        print "download...",
data = f.read()
if format == "pretty":
        print "parse...",

import re
#file("output.html", "wb").write(data)

from HTMLParser import HTMLParser

class mvv_parser(HTMLParser):
        rows = []
        row = []
        have_details = False
        def handle_starttag(self, tag, attrs):
                if self.have_details:
                        if tag == "tr":
                                self.row = []

        def handle_endtag(self, tag):
                if self.have_details and tag == "tr":
                        self.rows.append(self.row)
                
        def handle_data(self, data):
                data = data.strip()
                if not data:
                        return
                if not self.have_details:
                        if data == "Details":
                                self.have_details = True
                        else:
                                return
                self.row.append(data)
                
p = mvv_parser()
p.feed(data)

def filter_transport(input):
        input = input.replace("U-Bahn U", "U")
        input = input.replace("MetroBus ", "Bus")
        input = input.replace("S-Bahn S", "S")
        input = input.replace("Tram ", "Tram")
        return input

etappe = []
etappen = []
trips = []
for row in p.rows:
        if len(row) < 1:
                if len(etappen):
                        if len(etappe):
                                etappen[-1].extend(etappe)
                        trips.append(etappen)
                        etappen = []
                        etappe = []
                continue
        if row[0] == "Details":
                continue
                
        if len(etappe) == 0:
                if row[0] == "\xa0":
                        continue
                etappe = row
        else:
                if row[0] != "\xa0":
                        etappe.extend(row)
                if len(etappe) == 6:
                        etappen.append([etappe[0], etappe[3], etappe[1][3:], etappe[4][3:], filter_transport(etappe[2]), etappe[5]])
                elif len(etappe) == 7:
                        etappen.append([etappe[0], etappe[4], etappe[1][3:], etappe[5][3:], filter_transport(etappe[2]), etappe[6]])
                elif len(etappe) == 2:
                        t = etappe[0]
                        if t[0:3] == "ca.":
                                t = "dann zu Fuß %s" % t.replace("Minuten", "Min.")
                        if len(etappen):
                                etappen[-1].append(t)
                elif len(etappe) == 1:
                        etappen[-1].append(etappe[0])
                etappe = []

if format == "pretty":
        print "done.\n"

station_length = 20
transport_length = 2
max_output_len = 0
if format == "pretty":
        for trip in trips:
                for e in trip:
                        if len(e) >= 6:
                                station = "%s -> %s" % (e[2], e[3])
                                if len(station) > station_length:
                                        station_length = len(station)
                                transport = e[4]
                                if len(transport) > transport_length:
                                        transport_length = len(transport)
                                
                                output = "%s->%s %-*.*s %-*.*s Richtung %s" % (
                                        e[0], e[1],
                                        station_length, station_length,
                                        "%s -> %s" % (e[2], e[3]),
                                        transport_length, transport_length,
                                        e[4],
                                        e[5]
                                )
                                if show_hints and len(e) > 6:
                                        output += " (%s)" % (", ".join(e[6:]))
                                if len(output) > max_output_len:
                                        max_output_len = len(output)
if max_output_len < maxterminallength:
        line_length = max_output_len
else:
        line_length = maxterminallength
for trip in trips:
        if format == "pretty":
                print "-" * line_length
        for e in trip:
                if format == "pretty":
                        if len(e) >= 6:
                                output = "%s->%s %-*.*s %-*.*s Richtung %s" % (
                                        e[0], e[1],
                                        station_length, station_length,
                                        "%s -> %s" % (e[2], e[3]),
                                        transport_length, transport_length,
                                        e[4],
                                        e[5]
                                )
                                if show_hints and len(e) > 6:
                                        output += " (%s)" % (", ".join(e[6:]))
                                                
                                if len(output) <= maxterminallength:
                                        print output
                                else:
                                        while True:
                                                if len(output) > maxterminallength:
                                                        p1 = output.rfind(" ", 0, maxterminallength)
                                                        p2 = output.rfind("(", 0, maxterminallength)
                                                        p3 = output.rfind(",", 0, maxterminallength)
                                                        p3 = output.rfind("-", 0, maxterminallength)
                                                        p = max(p1, p2, p3)
                                                        print output[0:p+1]
                                                        output = "%s%s" % (" " * 14, output[p+1:])
                                                else:
                                                        print output
                                                        break
                        else:
                                print [e]
                elif format == "csv":
                        if len(e) >= 6:
                                print "%s;%s;%s;%s;%s;%s" % (
                                        e[0], e[1],
                                        e[2], e[3],
                                        e[4], e[5]
                                )
        print
written in 8.4ms
an optional config file could look like this:
$ cat ~/.mvv
titanic = Kurfürstenplatz
flo = Götheplatz
fhm = Lothstrasse
abseitz = Münchner Freiheit
braeustueberl = Holzapfelstrasse
dlr = Wessling:Wessling