Doris Lam
From UrbanWiki
Contents |
[edit] Contact
dlamoris@ucla.edu
[edit] WSGI
- WSGI (Web Server Gateway Interface) is a Python standard for communication between web servers and applications
- spec: http://www.python.org/dev/peps/pep-0333/
- a simple echo server code sample:
# wsgiref is built-in
from wsgiref import simple_server
# a callable (function in this case) to handle http requests
# environ is a dictionary that contains details about the request
# start_response is called to set http response status and headers
# the function returns the actual response as an iterable (list)
# the echo_app assumes some text has been posted to the server and simply
# echo them back
def echo_app(environ, start_response):
content_length = environ["CONTENT_LENGTH"]
# environ['wsgi.input'] is input stream to the raw post data
# MUST supply the amount to read indicated by content_length
content = environ['wsgi.input'].read(int(content_length))
headers = [('content-type', 'text/plain'),
('content-length', str(len(content))]
start_response('200 OK', headers)
return [content]
# server will listen on port 8000, using the echo_app callback to handle incoming requests
# each request spawns a new thread, done by the wsgi implementation
server = simple_server.make_server('', 8000, echo_app)
server.serve_forever()
- Instead of functions, the callback can be any callable object in python
- sample echo_app using an echo class:
from wsgiref import simple_server
class Echo(object):
def __init__(self, prepend):
self.prepend = prepend
# implement __call__ to make this class callable
def __call__(self, environ, start_response):
content_length = environ["CONTENT_LENGTH"]
content = environ['wsgi.input'].read(int(content_length))
body = self.prepend + ' ' + content
headers = [('content-type', 'text/plain'),
('content-length', str(len(body))]
start_response('200 OK', headers)
return [body]
# instantiate an Echo object and pass as the callable
# now requests to the server will receive back their posted
# message with "Echoing back" prepended
server = simple_server.make_server('', 8000, Echo("Echoing back"))
server.serve_forever()
- You can create chains of wsgi middleware to process each request
- example http basic auth middleware:
import base64
from wsgiref import simple_server
class HTTPBasicAuth(object):
# this class is a wsgi middleware that does http basic authentication
# using a supplied auth function
# calls the actual app to handle requests if user has been authenticated
def __init__(self, app, auth_func):
self.app = app
self.check_password = auth_func
# if not authorized, send back a 401 response
# else pass the request along
def __call__(self, environ, start_response):
if not self.authorized(environ):
return self.auth_required(environ, start_response)
return self.app(environ, start_response)
# checks if user is authorized
# environ['PATH_INFO'] contains the url after domain name
# e.g. www.yahoo.com/check --> environ['PATH_INFO'] = '/check'
def authorized(self, environ):
if not self.path_needs_auth(environ['PATH_INFO']):
return True
auth_header = environ.get('HTTP_AUTHORIZATION')
if not auth_header:
return False
auth_type, encoded_info = auth_header.split(None, 1)
assert auth_type.lower() == 'basic'
decoded_info = base64.b64decode(encoded_info)
username, password = decoded_info.split(':', 1)
if not self.check_password(username, password):
return False
# middleware can modify environ before passing requests on
environ['REMOTE_USER'] = username
del environ['HTTP_AUTHORIZATION']
return True
def path_needs_auth(self, path):
#do url checks here
if path == "/restricted":
return True
return False
def auth_required(self, environ, start_response):
headers = [('Content-type', 'text/html'),
('WWW-Authenticate', 'Basic realm="cens"')]
start_response('401 Authentication Required', headers)
return ["""
<html>
<head><title>Authentication Required</title></head>
<body>
<h1>Authentication Required</h1>
Bad username or password
</body>
</html>"""]
# the actual request handler sends back a greeting and the url path
def app(environ, start_response):
user = environ.get('REMOTE_USER')
if not user:
user = "random person"
content = "Welcome %s! Path is %s." % (user, environ['PATH_INFO'])
start_response('200 OK', [('content-type', 'text/plain'),
('content-length', str(len(content)))])
return [content]
# this naive password checker only accepts an user with username and password of 'cens'
# a more realistic checker will perhaps open a database connection and check user info
def check_password(username, password):
if username != 'cens' or password != 'cens':
return False
return True
server = simple_server.make_server('', 8080, HTTPBasicAuth(app, check_password))
server.serve_forever()
- there can be any number of middleware chains like this, each processing a different part of a request
- notable/useful environ variables:
- REQUEST_METHOD - GET or POST
- PATH_INFO - The remainder of the request URL's "path", designating the virtual "location" of the request's target within the application. This may be an empty string, if the request URL targets the application root and does not have a trailing slash
- QUERY_STRING - The portion of the request URL that follows the "?", if any. May be empty or absent.
- CONTENT_LENGTH - The contents of any Content-Length fields in the HTTP request. May be empty or absent
- CONTENT_TYPE - The contents of any Content-Type fields in the HTTP request. May be empty or absent.
- HTTP_ variables - Variables corresponding to the client-supplied HTTP request headers (i.e., variables whose names begin with "HTTP_"). The presence or absence of these variables should correspond with the presence or absence of the appropriate HTTP header in the request
- url reconstruction method - reconstruct full original url requested (contributed by Ian Bicking)
from urllib import quote
url = environ['wsgi.url_scheme']+'://'
if environ.get('HTTP_HOST'):
url += environ['HTTP_HOST']
else:
url += environ['SERVER_NAME']
if environ['wsgi.url_scheme'] == 'https':
if environ['SERVER_PORT'] != '443':
url += ':' + environ['SERVER_PORT']
else:
if environ['SERVER_PORT'] != '80':
url += ':' + environ['SERVER_PORT']
url += quote(environ.get('SCRIPT_NAME',''))
url += quote(environ.get('PATH_INFO',''))
if environ.get('QUERY_STRING'):
url += '?' + environ['QUERY_STRING']
- python paste is a wsgi implementation/server/library that provides useful middleware and other fancy stuff
- http://pythonpaste.org/do-it-yourself-framework.html go here for more examples
[edit] JSON
- Javascript Object Notation - a lightweight data interchange format
- http://www.json.org/ this site covers it pretty well and provides the full grammar
- PERFF uses this format extensibly
- JSON strings can be mapped directly to python dictionaries and lists
- http://pypi.python.org/pypi/simplejson use simplejson library to convert between python objects and JSON strings
import simplejson
jsonString = """ {"a":1, "b":2, "c":"Cat", "list":[1, 23, "blah", [34, "z"], {"dict":"nested"}, 70] } """
pyDict = simplejson.loads(jsonString)
# pyDict is now the dictionary that corresponds to jsonString, nested lists and all
# note that keys and string elements will be converted to python unicode strings
pyDict['c']
# this gives u'Cat'
print pyDict['c']
# this prints Cat
print simplejson.dumps(pyDict)
# this prints '{"a":1, "b":2, "c":"Cat", "list":[1, 23, "blah", [34, "z"], {"dict":"nested"}, 70] }'
- sample client to post json data to a server:
import httplib, simplejson
json = {"startProb":None,"trace":[{"updated": "2007-11-21 19:29:51", "created":"2007-11-21 19:29:51", "sbid": "1142",
"phone_time": "2007-10-09 16:30:11", "lon":"-118.246063333", "collection":"6b84ce7c-9867-11dc-b11c-001c42ec2666",
"gpsSpeed": "0", "freeway": 0, "havSpeed": "0", "lat": "34.1483683333", "id":"20e37962-9868-11dc-b11c-001c42ec2666",
"mapActivityClass": "still"},{"updated":"2007-11-21 19:29:51", "created":"2007-11-21 19:29:51", "sbid":"1143","phone_time":
"2007-10-09 16:30:16", "lon": "-118.246063333", "collection":"6b84ce7c-9867-11dc-b11c-001c42ec2666", "gpsSpeed": "0",
"freeway": 0,"havSpeed": "0", "lat": "34.1483683333", "id": "2125907c-9868-11dc-b11c-001c42ec2666", "mapActivityClass":
"still"}]}
jsonString = simplejson.dumps(json)
conn = httplib.HTTPConnection("localhost:8000")
headers = {"Content-type": "text/plain", "Content-length":str(len(jsonString)) }
conn.request("POST", "/activity", jsonString, headers)
response = conn.getresponse()
print response.status, response.reason
print response.read()
conn.close()
[edit] Connecting to PostgreSQL database from Python
- psycopg2 - http://www.initd.org/tracker/psycopg/wiki/PsycopgTwo
- python dbapi2 spec - http://www.python.org/dev/peps/pep-0249/
import psycopg2
try:
conn = psycopg2.connect("dbname='template1' user='dbuser' host='localhost' password='dbpass'");
except:
print "I am unable to connect to the database"
# use cursor objects to execute queries and get back results
# psycopg2 is thread safe level 2, which means threads can share the module and connection, but not cursors
cur = conn.cursor()
# execute a sql statement
cur.execute("""SELECT a, b from table""")
# cur.rowcount specifies the number of rows that the last executeXXX() produced
if cur.rowcount > 0:
# fetch one row, or next row as a sequence (e.g. tuple or list)
row = cur.fetchone()
a1 = row[0]
b1 = row[1]
# fetch all remaining rows as list of lists
rows = cur.fetchall()
a2 = rows[0][0]
b2 = rows[0][1]
cur.close()
conn.close()
[edit] PostGIS sample queries
- PostGIS documentation: http://postgis.refractions.net/documentation/
- PostGIS quick guide: http://www.bostongis.com/postgis_quickguide.bqg
- creating a table with a geometry column by normally creating a table, and then use AddGeometryColumn:
CREATE TABLE sample(
sbid INTEGER PRIMARY KEY,
imei_phone VARCHAR(20),
lat NUMERIC,
lon NUMERIC,
phone_timestamp TIMESTAMP,
exposure FLOAT,
);
-- SELECT AddGeometryColumn([schema_name], [table_name], [column_name_to_add], [SRID], [geometry_type], [dimension])
SELECT AddGeometryColumn('test', 'sample', 'the_geom', 4326, 'POINT', 2)
CREATE TABLE exposures(
exposure FLOAT
);
SELECT AddGeometryColumn('test', 'exposures', 'the_geom', 4326, 'POLYGON', 2)
-- AddGeometryColumn ensures that metadata info about the geometry column added (e.g. srid, dimension, type, table) will be
inserted into the postgis 'geometry_columns' table (if using current schema, schema_name can be omitted)
-- likewise, use DropGeometryColumn or DropGeometryTable when deleting
-- srid of 4326 is usually right for gps data
- Creating index on geometry columns:
-- CREATE INDEX [indexname] ON [tablename] USING GIST ( [geometryfield] ); CREATE INDEX polygon_index ON exposures USING GIST (the_geom ); -- Collect statistics after making index: VACUUM ANALYZE [table_name] [column_name]
- Sample SQL queries:
- distance between two points:
SELECT distance_sphere(GeomFromText('POINT(lon1 lat1)', 4326), GeomFromText('POINT(lon2 lat2)', 4326));
-- lon1, lat1, lon2, lat2 are longitude latitude values, return value is in meters
-- GeomFromText (or PointFromText) takes a Well Known Text representation of a geometry and returns the geometry specified
- polygon that contains a point:
SELECT AsText(the_geom), attributes
FROM polygon_table
WHERE the_geom && GeomFromText('POINT(lon lat)', 4326) AND Contains(the_geom, GeomFromText('POINT(lon lat)', 4326));
-- AsText function returns Well Known Text representation of geometry (here the geometry is from the geomtry column named
'the_geom' in table 'polygon_table'
-- the && operator makes use of geometry index if one was created on the_geom, uses bounding box tests to filter out
- updating points with exposure info:
UPDATE TABLE sample
SET exposure = (SELECT exposure
FROM exposures
WHERE exposures.the_geom && sample.the_geom AND
Contains(exposures.the_geom, sample.the_geom))
WHERE sample.exposure IS NULL;
-- here we're setting the exposure info in the sample table according to which polygon in the exposures table contains the
lat lon point
- closest roads/points from a point (e.g. within 100 meters):
SELECT s.road, s.distance FROM (SELECT road, distance(transform(GeomFromText('POINT(lon lat)', 4326), 26911),
transform(the_geom, 26911)) AS distance
FROM a_streets_table
WHERE expand(transform(GeomFromText('POINT(lon lat)', 4326), 26911), 100) &&
transform(the_geom, 26911)) AS s
WHERE s.distance < 100 ORDER BY s.distance;
-- the transform function transforms a geometry from one projection to another so we can compare distance in meters using
srid 26911
-- use function ST_DWithin if using PostGIS 1.3.1+
SELECT road, distance(transform(GeomFromText('POINT(lon lat)', 4326), 26911),
transform(the_geom, 26911)) as distance
FROM streets_table
WHERE ST_DWithin(transform(GeomFromText('POINT(lon lat)', 4326), 26911), transform(the_geom, 26911), 100)
- other potentially useful postgis functions:
- line_locate_point and line_interpolate_point - can be used to map a point to the closest point to a nearby road
- centroid - returns the center of a polygon or multipolygon
- intersects - tests if two geometries spatially intersect
- see postgis documentation chapter 6 for full list of functions
[edit] Python for Symbian S60
- http://sourceforge.net/projects/pys60/
- download PythonForS60_x_x_x_3rdEd.SIS and PythonScriptShell_x_x_x_3rdEd_unsigned_freedevcert.SIS
- sign PythonScriptShell using svn code signing
- install both on phone
- copy python scripts to E:\Python dir on the phone and run it by choosing 'run script' in Python
- to make an installable sis file from python script, use ensymble: http://www.nbl.fi/~nbl928/ensymble.html, get the squeezed version
- python ensymble_python.py py2sis --caps=[capability1]+[capability2]+... script.py installable.sis
- see pys60 doc from sourceforge for differences from desktop python and extensions
- more extensions to get wlan, gps, cell tower info: http://www-rp.lip6.fr/~berger/pys60extensions.html
- to create python wrappers around symbian c++ code: http://wiki.research.nokia.com/index.php/PythonWrappers
[edit] From Summer
[edit] Using Geoserver web admin tool to set up stuff
- create table with geometry in postgis
- login to geoserver web admin site (http://localhost:8080/geoserver)
- Config->Data
- Namespace: new namespace (can be anything, eg. cens, uri = http://www.cens.ucla.edu
- Datastore: new PostGIS datastore, fill in required stuff, default port for database is 5432
- Featuretype: add new tables in database, make sure tables' geometry columns have been added with SELECT AddGeometryColumn(...) default Style can be 'poi', SRS should already been filled in (4326), either click generate bounding box or use -180 min long, 180 max long, -90 min lat, 90 max lat if table doesn't have anything yet.
- submit, apply, save (in that order, apply and save are on left column)
- Styles: can put output styles and filters in here, see included styles for examples or see tutorial here
- for more stuff see geoserver user's guide
- code for HTTP POST to automatically add feature types, see here for original (scroll to bottom for php), modify datastore, layername, and post destination in last two lines from geov2 to geoserver
- mock up code of user submitting table, creating table and geometry in postgis, register featuretype with geoserver, use WFS GetCapabilities to see list of featuretypes in Geoserver - drop down menu for google maps
- code of receiving data from campaignr
[edit] Getting stuff onto Google Earth
- set up featuretypes in Geoserver
- in Google Earth, add a network link, put http://server_name:8080/geoserver/wms/kml_reflect?layers=namespace:table_name
- see here for bells and whistles
[edit] Google Maps
- get google maps api key
- get wms-gs.js from here
- look at example on that page
- code for peir pilot 2
- code for peir pilot 1
[edit] PostGIS Stuff
- PostGIS cheatsheet here
- to add a geometry column to a table: SELECT AddGeometryColumn([schema_name], [table_name], [column_name_to_add], [SRID], [geometry_type], [dimension])
- eg. SELECT AddGeometryColumn('public','table', 'the_geom', 4326, 'POINT', 2);
- drop geometry column: DropGeometryColumn(<schema_name>, <table_name>, <column_name>)
- see view geometry_columns for a list of tables with geometry type columns, this should be reflective of what's actually in the database
- building index on geometry columns: CREATE INDEX [indexname] ON [tablename] USING GIST ( [geometryfield] GIST_GEOMETRY_OPS );
- get geometry from well known text: eg. GeomFromText('POINT(lon lat)', 4326) where lon and lat are longitude latitude values
- updating table with geometries from non geometric fields: UPDATE table SET the_geom = GeomFromText('POINT(' || longitude || ' ' || latitude || ')',4326) assuming longitude and latitude are separate columns in the table
- converting ESRI shapefile into PostGIS (for zip code shapefiles here or boundary files here: use shp2pgsql (type shp2pgsql to see all options)
- converting tiger shapefiles: use ogr2ogr from here, ogr2ogr -t_srs EPSG:4326 -f "PostgreSQL" PG:dbname=test <tiger_shapefile> or see tutorial here (a bit of overkill)
- ogr2ogr cheatsheet here
- find zip code that contains a point: SELECT zcta FROM calizip WHERE Contains(the_geom, GeomFromText('POINT(lon lat)', 4326))
- to use index in query: do a bounding box check first: SELECT zcta FROM calizip WHERE the_geom && GeomFromText('POINT(lon lat)', 4326) AND Contains(the_geom, GeomFromText('POINT(lon lat)', 4326)) assuming there's a GiST index on the_geom
- updating table with zip codes: UPDATE table SET zip_code = (SELECT zcta FROM calizip WHERE Contains(calizip.the_geom, table.the_geom)) WHERE table.the_geom IS NOT NULL
- querying: use AsText(the_geom) to display geometry data as readable text
[edit] twisted stuff
- http://twistedmatrix.com/projects/core/documentation/howto/enterprise.html using adbapi with blocking database drivers
- http://jamwt.com/pgasync/ twisted based postgresql driver
- http://www.initd.org/tracker/psycopg/wiki/PsycopgTwo python postgres interface
[edit] mostly useful links
- http://www.python.org/dev/peps/pep-0249/ python database api spec
- http://www.python.org/dev/peps/pep-0333/ wsgi spec
- http://pythonpaste.org/ python paste
- http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html http 1.1 header field list
- http://postgis.refractions.net/docs/ postgis doc
- http://www.amk.ca/python/code/crypto python crypto toolkit
- http://www.amk.ca/python/writing/pycrypt/pycrypt.html
- http://www.freenet.org.nz/ezPyCrypto/
- http://www.econym.demon.co.uk/googlemaps/ google maps code examples
- http://www.google.com/apis/maps/documentation/ official google maps doc
- http://www.dsm.fordham.edu/~mathai/openssl.html about public keys
- http://cryptosys.net/pki/rsakeyformats.html rsa key formats
- http://marc.info/?l=openssl-users&m=109101224217079&w=2
- http://3sp.com/content/developer/maverick/docs/com/sshtools/publickey/SshPublicKeyFileFactory.html
- to generate self signed cert from private key: openssl req -key rsa1024.key -new -x509 -out x509.cert
- http://www.faqs.org/rfcs/rfc4716.html ssh2 public key
- http://www.faqs.org/rfcs/rfc4253.html section 6.6
- http://linux.die.net/man/1/puttygen puttygen
- http://www.openssh.org/txt/draft-ietf-secsh-publickeyfile-02.txt secsh public key format
- http://ubuntuforums.org/showthread.php?p=3643354 PyLucene JCC vs GCJ
- http://lucene.apache.org/java/2_2_0/api/overview-summary.html#overview_description Lucene API
- motivation

