#!/usr/local/bin/python ########################################################################## # # Copyright (c) 2005 Imaginary Landscape LLC and Contributors. # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ########################################################################## """ A translation of the PHP version of MapQuest's Site Advantage. """ ## ## Configure these values: ## ## You will still be unauthorized until your IP address is registered ## with MapQuest. The password is always empty. client_id = 'REPLACEME' client_password = '' ## This is where we look for all the .xsl files; relative paths ## are resolved relative to this file #template_path = 'xslt/' template_path = 'REPLACEME' ## Shouldn't change: advantage_server = 'xml.sa.mapquest.com' ############################################################ ## Program ############################################################ import cgi import os from urllib import quote as url_quote import urllib from Ft.Xml.Xslt import Processor from Ft.Xml import InputSource from Ft.Lib import Uri import cgitb cgitb.enable() template_path = os.path.join(__file__, template_path) class InvalidForm(Exception): """ Exception raised when the client-side processing fails for some reason. """ def __init__(self, code, message): self.code = code Exception.__init__(self, message) # make sure we have some required variables setup assert advantage_server and client_id, ( "advantage_server and client_id must both be set") def run(): """ Runs the process, as a CGI script. Creates a 'form' object, which is passed around many places. This is a dictionary of the query parameters. """ form = parse_form() template = find_template(form) try: params = find_url_params(form) if client_password: params.append(('password', client_password)) params.append(('clientId', client_id)) url = 'http://%s/?%s' % (advantage_server, pack_form(params)) f = urllib.urlopen(url) xml = f.read() f.close() #assert 0, xml if not xml: raise InvalidForm(9001, "No response body") except InvalidForm, e: xml = generate_xml(e.code, form) response = process_xml(template, xml) print 'Content-type: text/html\n' print response def parse_form(): """ Takes the cgi FieldStorage object, and turns it into a dictionary of strings (lists for names that show up multiple times). """ field_storage = cgi.FieldStorage() form = {} for name in field_storage.keys(): use_name = name.lower() if isinstance(field_storage[name], list): form[use_name] = [f.value for f in field_storage[name]] else: form[use_name] = field_storage[name].value return form def find_template(form): """ Finds the name of the template we'll use. You can pass the template in with the 'template' variable, or it defaults to 'map'. """ template = form.get('template', 'map') assert '/' not in template template = os.path.join(__file__, template_path, template + '.xsl') return template def find_url_params(form): """ Finds the parameters in the query variables that we'll pass through to MapQuest. Returns a list of (name, value), which will be sent via a GET query to MapQuest. """ transaction = 'locmap' url = form.get('url', '') if not url: if (form.has_key('transaction') and form['transaction'] != 'poimap' and form.get('identifyicon')): # form has been submitted and needs to be checked if form['transaction'] == 'route': check_route(form) else: check_other(form) params = [] for name, value in unpack_form(form): if name == 'template': continue elif name == 'transaction': if value: transaction = value else: params.append((name, value)) params.append(('transaction', transaction)) return params def check_route(form): """ Check that the necessary variables are provided for route requests. """ if ((not form.get('origcity') or not form.get('origstateprovince')) and not form.get('origpostalcode') and not form.get('origrecordid') and not form.get('origlatitude') and not form.get('origlongitude')): raise InvalidForm(9000, 'Missing origination fields') if ((not form.get('destcity') or not form.get('deststateprovince')) and not form.get('destpostalcode') and not form.get('destrecordid') and not form.get('destlatitude') and not form.get('destlongitude')): raise InvalidForm(9000, 'Missing destination fields') def check_other(form): """ Check that the necessary variables are provided for normal requests. """ if ((not form.get('city') or not form.get('stateprovince')) and not form.get('postalcode') and not form.get('recordid') and not form.get('latitude') and not form.get('longitude')): raise InvalidForm(9000, 'Missing location fields') def unpack_form(form): """ Takes a dictionary (where some values may be lists) and returns a list of (name, value). Unlike .items(), this returns a flat list. """ params = [] for name, value in form.items(): if isinstance(value, list): for v in value: params.append((name, v)) else: params.append((name, value)) return params def pack_form(lst): """ Takes a list of (name, value) where names might show up more than once, and returns a dictionary that may have some list values. """ return '&'.join(['%s=%s' % (url_quote(name), url_quote(value)) for name, value in lst]) def process_xml(template_fn, input): """ template_fn is the filename of our XSL template. input is the XML response from MapQuest. This processes the response with the XSL templates (also called 'stylesheets') and returns the result. """ processor = Processor.Processor( stylesheetAltUris=[Uri.OsPathToUri(template_path, attemptAbsolute=1)]) transform = InputSource.DefaultFactory.fromUri( Uri.OsPathToUri(template_fn, attemptAbsolute=1)) source = InputSource.DefaultFactory.fromString( input, 'http://spam.com/doc.xml') processor.appendStylesheet(transform) result = processor.run(source) return result def generate_xml(error_code, form): """ Generates the kind of error that MapQuest would generate. We use this for errors that occur before we get to MapQuest (missing variables, can't connect to remote host, etc). """ xml = '\n\n' locs = {'origin': '', 'destination': '', 'locations': ''} input = '' paramNode = '' if not error_code: xml += '\t\n' else: xml += '\t\n\t\t%s\n' % error_code for key, value in unpack_form(form): paramNode += '\t\t\t<%s>%s\n' % ( key, value, key) if key.startswith('orig'): name = key[len('orig'):] locs['origin'] += '\t\t\t\t\t<%s>%s' % ( name, value, name) elif key.startswith('dest'): name = key[len('dest'):] locs['destination'] += '\t\t\t\t\t<%s>%s\n' % ( name, vaule, name) elif (key.startswith('loc_') or key in ('address', 'city', 'stateprovince', 'postalcode', 'country')): locs['locations'] += '\t\t\t\t\t<%s>%s\n' % ( key, value, key) else: # @@: I don't like this pass for key, value in locs.items(): if not value: input += ('\t\t\t<%s count="1">\n\t\t\t\t\n' '%s\t\t\t\t\n\t\t\t\n' % (key, value, key)) xml += ('\t\t\n%s\t\t\n' '\t\t\n%s\t\t\n' % (paramNode, input)) if not error_code: xml += '\t\n' else: xml += '\t\n' xml += '\n' return xml if __name__ == '__main__': run()