Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial commit of the Django site for Open Transit Data. It has an ex…

…ample form for getting petition submissions. Neat.
  • Loading branch information...
commit 26cf95c716a940d829a0ca3b3345523d718e5295 1 parent 4933457
Dave Peck authored
View
21 .gitignore
@@ -0,0 +1,21 @@
+.DS_Store
+*~
+*.pyc
+*.o
+*.so
+*.mode1
+*.mode1v3
+*.pbxuser
+*.perspectivev3
+*.orig
+*#*#
+.build/
+build/
+Debug-aspentsimulator/
+Debug-iphonesimulator/
+Debug-iphoneos/
+Debug-toolchain/
+Release-iphonesimulator/
+Release-toolchain/
+Release-iphoneos/
+.sass-cache/
View
1  README
@@ -1 +0,0 @@
-Code powering opentransitdata.org
View
71 README.markdown
@@ -0,0 +1,71 @@
+OpenTransitData.org
+===================
+
+The following repository contains all the code to power the [Open Transit Data](http://opentransitdata.org/) web site.
+
+It is an AppEngine application running Django 1.1.
+
+
+HOW TO GET RUNNING LOCALLY
+--------------------------
+
+If you want to run this application locally, follow these easy steps:
+
+1. Make sure you have Python 2.5 installed
+
+ If you use a Mac with MacPorts, you can use python_select to choose python 2.5. Unforuntately, as of this writing, AppEngine does not support Python 2.6.
+
+2. Install Django 1.1.
+
+ Unfortunately, the AppEngine development server does not like virtualenv, etc. So you'll have to install django 1.1 as a major package. easy_install is a good way to go.
+
+3. Install the [App Engine Python SDK](http://code.google.com/appengine/downloads.html#Google_App_Engine_SDK_for_Python).
+
+4. `cd` into this directory and run `dev_appserver.py .` to get the local server running.
+
+5. Test by visiting `http://localhost:8080/`
+
+
+
+
+A BIT ABOUT DJANGO ON APPENGINE
+-------------------------------
+
+If you look at `bootstrap.py` you'll see that it loads the Django 1.1 library (which is pre-installed on the AppEngine servers) and does a few things to load in `settings.py`.
+
+The `opentransit` directory is the django "application." It is where all the *real* work of this application is going to get done. Chances are, if you want to write some code, you want to write it here.
+
+The top-level `urls.py` file simply loads URLs from `opentransit/urls.py`. Any time you add a view (in `opentransit/views.py`) you should also add a new url in `opentransit/urls.py`.
+
+We also have top-level `static` and `templates` directories. The `static` directory is for truly static files, like images, javascript, and CSS. You can reference these cleanly in your django templates with `{% static_url /images/foo.png %}`. The `templates` directory contains Django 1.1-syntax templates. I put in an example `base.html` and `home.html` file which makes use of Django's `{% extends ... %}` template tag. This is the way most people do things with Django but it is by no means required. These are example files only!
+
+So, again, everything interesting code-wise is going to be under the `opentransit/` subdirectory. Most interesting are `views.py`, where you put your view functions, and `models.py`, where you put your AppEngine model classes. Easy, right? `forms.py` might also be interesting we decide to use Django forms to handle our petition, etc.
+
+I have also added a bunch of "helper" code in case we want to do more interesting things. Included in the `opentransit` directory are:
+
+1. Some new template tags:
+
+ `static_url` makes it easy to reference stuff in our `static` top level directory
+
+ `partial` is just like Django 1.1's include, except you can pass a state dictionary down to the included file.
+
+2. Some code to help us with login/logout of users.
+
+ My guess is that we will not need this code, which is spread across `utils.py`, `middleware.py` and `context.py`. You can happily ignore these files unless we decide we want users on AppEngine.
+
+3. A simple, example Django form.
+
+ Again, we don't have to use Django forms. But it may be nice to do so. So I put an example in `forms.py`
+
+4. A simple, example AppEngine mdoel.
+
+ This really is an AppEngine model, not a Django model. We can't use Django models when running on AppEngine.
+
+5. Two simple, example views.
+
+ The corresponding templates link to one another using the `{% url ... %}` template tag.
+
+Let me know if you have any questions. -- Dave
+
+
+
View
0  __init__.py
No changes.
View
17 app.yaml
@@ -0,0 +1,17 @@
+application: opentransitdata
+version: 1
+runtime: python
+api_version: 1
+
+handlers:
+- url: /images
+ static_dir: static/images
+
+- url: /js
+ static_dir: static/js
+
+- url: /css
+ static_dir: static/css
+
+- url: /(.*)
+ script: bootstrap.py
View
64 bootstrap.py
@@ -0,0 +1,64 @@
+# Standard Python Imports
+import os
+import sys
+import logging
+
+# Log a message each time this module get loaded.
+logging.info('Loading %s, app version = %s', __name__, os.getenv('CURRENT_VERSION_ID'))
+
+# Declare the Django version we need.
+from google.appengine.dist import use_library
+use_library('django', '1.1')
+
+# Fail early if we can't import Django 1.x. Log identifying information.
+import django
+logging.info('django.__file__ = %r, django.VERSION = %r', django.__file__, django.VERSION)
+assert django.VERSION[0] >= 1, "This Django version is too old"
+
+# Custom Django configuration.
+os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
+from django.conf import settings
+settings._target = None
+
+# AppEngine imports.
+from google.appengine.ext.webapp import util
+from google.appengine.ext.webapp import template as template_registrar
+
+# Helper to enter the debugger. This passes in __stdin__ and
+# __stdout__, because stdin and stdout are connected to the request
+# and response streams. You must import this from __main__ to use it.
+# (I tried to make it universally available via __builtin__, but that
+# doesn't seem to work for some reason.)
+def BREAKPOINT():
+ import pdb
+ p = pdb.Pdb(None, sys.__stdin__, sys.__stdout__)
+ p.set_trace()
+
+# Import various parts of Django.
+import django.core.handlers.wsgi
+import django.core.signals
+import django.db
+import django.dispatch.dispatcher
+import django.forms
+
+# Work-around to avoid warning about django.newforms in djangoforms.
+django.newforms = django.forms
+
+# Django signal handler to log an exception
+def log_exception(*args, **kwds):
+ cls, err = sys.exc_info()[:2]
+ logging.exception('Exception in request: %s: %s', cls.__name__, err)
+
+# Log all exceptions detected by Django.
+django.core.signals.got_request_exception.connect(log_exception)
+
+# Unregister Django's default rollback event handler.
+django.core.signals.got_request_exception.disconnect(django.db._rollback_on_exception)
+
+def main():
+ application = django.core.handlers.wsgi.WSGIHandler()
+ template_registrar.register_template_library('opentransit.tags')
+ util.run_wsgi_app(application)
+
+if __name__ == "__main__":
+ main()
View
11 index.yaml
@@ -0,0 +1,11 @@
+indexes:
+
+# AUTOGENERATED
+
+# This index.yaml is automatically updated whenever the dev_appserver
+# detects that a new type of query is run. If you want to manage the
+# index.yaml file manually, remove the above marker line (the line
+# saying "# AUTOGENERATED"). If you want to manage some indexes
+# manually, move them above the marker line. The index.yaml file is
+# automatically uploaded to the admin console when you next deploy
+# your application using appcfg.py.
View
3  opentransit/__init__.py
@@ -0,0 +1,3 @@
+from django.utils import simplejson as json
+
+
View
13 opentransit/context.py
@@ -0,0 +1,13 @@
+# NOTE DAVEPECK
+#
+# I wrote this code to help you get sessions and users on django +appengine applications.
+# It is NOT AT ALL INTERESTING if you don't need users. In fact, for opentransit, I've
+# turned if off in the settings.py file. But I kept it here just in case...
+#
+
+def appengine_user(request):
+ if request.user:
+ return {'user': request.user, 'user_is_valid':request.user.is_valid}
+ else:
+ return {'user': None, 'user_is_valid':False}
+
View
12 opentransit/forms.py
@@ -0,0 +1,12 @@
+from django import forms
+from .models import ExamplePetitionModel
+
+# NOTE DAVEPECK: this is an example only. Ignore as you see fit!
+
+class ExamplePetitionForm(forms.Form):
+ name = forms.CharField(max_length=128, min_length=6, label=u"Name")
+ email = forms.EmailField(label=u"Email")
+ city = forms.CharField(max_length=64, min_length=3, label=u"City")
+ state = forms.CharField(max_length=16, min_length=2, label=u"State")
+ country = forms.CharField(max_length=16, min_length=2, label=u"Country")
+
View
66 opentransit/middleware.py
@@ -0,0 +1,66 @@
+# NOTE DAVEPECK
+#
+# I wrote this code to help you get sessions and users on django +appengine applications.
+# It is NOT AT ALL INTERESTING if you don't need users. In fact, for opentransit, I've
+# turned if off in the settings.py file. But I kept it here just in case...
+#
+
+from .models import User
+from .utils import serialize_dictionary, deserialize_dictionary
+
+SESSION_COOKIE_KEY = "_session"
+USER_KEY_SESSION_KEY = "_user_key"
+
+class AppEngineSecureSessionMiddleware(object):
+ def process_request(self, request):
+ def get_session(name, default=None):
+ return request._session.get(name, default)
+
+ def set_session(name, value):
+ request._session[name] = value
+ request._session_dirty = True
+
+ def del_session(name):
+ del request._session[name]
+ request._session_dirty = True
+
+ def session_has(name):
+ return name in request._session
+
+ session_value = request.COOKIES.get(SESSION_COOKIE_KEY, None)
+ if session_value is not None:
+ request._session = deserialize_dictionary(session_value)
+ if request._session is None:
+ request._session = {}
+ else:
+ request._session = {}
+ request._session_dirty = False
+ setattr(request, 'get_session', get_session)
+ setattr(request, 'set_session', set_session)
+ setattr(request, 'del_session', del_session)
+ setattr(request, 'session_has', session_has)
+
+ def process_response(self, request, response):
+ if request._session_dirty:
+ # TODO max_age and expires?
+ response.set_cookie(SESSION_COOKIE_KEY, serialize_dictionary(request._session))
+ return response
+
+class AppEngineGenericUserMiddleware(object):
+ def process_request(self, request):
+ user_key = request.get_session(USER_KEY_SESSION_KEY)
+ if user_key:
+ request.user = User.get(user_key)
+ else:
+ request.user = None
+ request._original_user = request.user
+
+ def process_response(self, request, response):
+ if request._original_user != request.user:
+ if request.user is None:
+ request.del_session(USER_KEY_SESSION_KEY)
+ else:
+ request.set_session(USER_KEY_SESSION_KEY, request.user.key())
+ return response
+
+
View
10 opentransit/models.py
@@ -0,0 +1,10 @@
+from google.appengine.ext import db
+# from django.contrib.auth.models import get_hexdigest, check_password
+
+class ExamplePetitionModel(db.Model):
+ # This is just like any other appengine model that you've ever written!
+ name = db.StringProperty()
+ email = db.EmailProperty()
+ city = db.StringProperty()
+ state = db.StringProperty()
+ country = db.StringProperty()
View
120 opentransit/nameddict.py
@@ -0,0 +1,120 @@
+"""nameddict - Modifiable namedtuple, with named indexing
+
+Original idea and help from Dave Peck <davepeck@davepeck.org> <http://davepeck.org/>.
+
+Supports obj.a to access field named "a". Named indexing means ["name"] instead of [0].
+
+Defaults are stored as _defaults, which is modifiable. Fields are stored as _fields, which should be treated readonly.
+
+TODO:
+- document interface more thoroughly
+- write tests, cleanup
+- see if it's better to go the exec route (as namedtuple does)
+- decide if the field ordering is expected or implementation detail
+ - currently the given ordering is kept
+"""
+
+import re
+import sys
+from keyword import iskeyword
+
+
+def validate_names(names):
+ seen = set()
+ for name in names:
+ if iskeyword(name) or not re.match("^[a-zA-Z][a-zA-Z0-9_]*$", name):
+ raise ValueError("invalid: %r" % name)
+ elif name in seen:
+ raise ValueError("duplicate: %r" % name)
+ else:
+ seen.add(name)
+
+
+def nameddict(name=None, keys=(), defaults=None, module=None):
+ if defaults is None:
+ defaults = {}
+ keys = list(keys) # handles all iterables
+ keys.extend(k for k in defaults.keys() if k not in keys)
+ validate_names(keys)
+
+ class nameddict(object):
+ __slots__ = keys
+ _fields = keys
+ _defaults = defaults
+
+ @classmethod
+ def from_dict(cls, d):
+ clean_d = {}
+ for k, v in d.iteritems():
+ clean_d[str(k)] = v
+ return cls(**clean_d)
+
+ def __init__(self, **values):
+ for k, v in defaults.iteritems():
+ setattr(self, k, v)
+ for k, v in values.iteritems():
+ setattr(self, k, v)
+
+ def __getitem__(self, attr):
+ return getattr(self, attr)
+
+ def __setitem__(self, attr, value):
+ setattr(self, attr, value)
+
+ def __delitem__(self, attr):
+ delattr(self, attr)
+
+ def __iter__(self):
+ return iter(self._fields)
+
+ def _items(self, default=None):
+ for k in self:
+ yield k, getattr(self, k, default)
+
+ def to_dict(self, default=None):
+ return dict((k, getattr(self, k, default)) for k in self)
+
+ def __repr__(self):
+ # nameddict.__module__ instead of self.__module__ or type(self).__module__
+ # not sure, but I think this is the least fragile
+ # of couse, __repr__ may be overridden in a derived class
+ return "%s.%s(%s)" % (nameddict.__module__, nameddict.__name__,
+ ", ".join("%s=%r" % (k, getattr(self, k)) for k in self if hasattr(self, k)))
+ # using hasattr instead of getattr(self, k, None)
+ # which is slightly different: missing attributes are missing, instead of =None
+ # e.g. (a=1) instead of (a=1, b=None)
+
+ nameddict.__name__ = name or "<nameddict>"
+ if module is not None:
+ nameddict.__module__ = module
+ elif hasattr(sys, '_getframe'):
+ # if no sys._getframe, pass module=__name__
+ nameddict.__module__ = sys._getframe(1).f_globals.get("__name__", "__main__")
+ else:
+ nameddict.__module__ = "<unknown>"
+
+ return nameddict
+
+
+def main():
+ D = nameddict("D", ["a"], {"b": 3})
+ d = D()
+ print type(d).__name__, repr(d), repr(type(d))
+ d.a = 42
+ print d["a"]
+ print d.b
+ print d.to_dict()
+ print list(d._items())
+ print d._fields, d._defaults, d.__slots__
+ print
+ print nameddict(keys="a b c".split(), module=__name__)
+ print nameddict(keys="a b c".split(), module=__name__)()
+
+
+if __name__ == "__main__":
+ import pdb
+ try:
+ main()
+ except Exception, e:
+ print "%s: %s" % (type(e).__name__, e)
+ pdb.post_mortem(sys.exc_info()[2])
View
114 opentransit/tags.py
@@ -0,0 +1,114 @@
+# NOTE davepeck
+#
+# These may or may not be handy. I tend to think that static_url is _very_ handy.
+#
+
+import django.template as djangot
+from google.appengine.ext.webapp import template
+
+register = template.create_template_register()
+
+
+#------------------------------------------------------------------------------
+# "Static URLs." -- mostly interesting for the future...
+#------------------------------------------------------------------------------
+
+# Pointer to static media _for our site_ (like our js/css/images)
+@register.tag(name="static_url")
+def static_url(parser, token):
+ try:
+ tag_name, relative_path = token.split_contents()
+ except ValueError:
+ raise djangot.TemplateSyntaxError, "static_url tag expects a relative path (like /css/foo.css)"
+ return StaticUrlNode(relative_path)
+
+class StaticUrlNode(djangot.Node):
+ def __init__(self, relative_path):
+ self.relative_path = relative_path
+
+ def render(self, context):
+ return self.relative_path
+
+
+#------------------------------------------------------------------------------
+# "Partial" template; currently VERY HACKNOLOGICAL, but it works. Should be hella cleaned.
+#------------------------------------------------------------------------------
+
+@register.tag(name="partial")
+def partial(parser, token):
+ try:
+ contents = token.split_contents()
+ template_path = contents[1].strip()
+ if template_path[0] in '"\'':
+ template_path = template_path[1:-1]
+ dictionary_string = ' '.join(contents[2:])
+ dictionary_info = parse_dictionary_string(dictionary_string)
+ except djangot.TemplateSyntaxError:
+ raise
+ except:
+ raise djangot.TemplateSyntaxError, "partial tag expects a dictionary, like {% partial designer/wild.txt {'hello': neato, 'goodbye': dolly} %}"
+ return PartialNode(template_path, dictionary_info)
+
+class PartialNode(djangot.Node):
+ def __init__(self, template_path, dictionary_info):
+ self.template_path = template_path
+ self.dictionary_info = dictionary_info
+
+ def render(self, context):
+ new_context = {}
+
+ # Run through the dictionary and evaluate it
+ for key, value_name in self.dictionary_info.iteritems():
+ new_context_value = None
+
+ # Try it as a variable first
+ try:
+ v = djangot.Variable(value_name)
+ new_context_value = v.resolve(context)
+ except:
+ new_context_value = None
+
+ # And as an actual python value second
+ if new_context_value is None:
+ try:
+ new_context_value = eval(value_name)
+ except:
+ new_context_value = None
+
+ new_context[key] = new_context_value
+
+ # Render the template
+ final_template = template.load(self.template_path)
+ return final_template.render(template.Context(new_context))
+
+def parse_dictionary_string(s):
+ s = s.strip()
+
+ # NOTE this is first-pass code only -- complete hack; use regexps, etc.
+
+ # test dictionary syntax
+ if s[0] != '{' or s[-1] != '}':
+ raise djangot.TemplateSyntaxError, "Malformed dictionary in partial tag. Must start and end with {}"
+ s = s[1:-1]
+
+ # split key/value pairs
+ kvp_strings = [kvp.strip() for kvp in s.split(',')]
+ kvps = {}
+
+ for kvp_string in kvp_strings:
+ split = kvp_string.split(':')
+ key = split[0].strip()
+ value_name = split[1].strip()
+ if key[0] == '\'':
+ if key[-1] != '\'':
+ raise djangot.TemplateSyntaxError, "Malformed dictionary in partial tag. A key is improperly quoted."
+ else:
+ key = key[1:-1]
+ if key[0] == '"':
+ if key[-1] != '"':
+ raise djangot.TemplateSyntaxError, "Malformed dictionary in partial tag. A key is improperly quoted."
+ else:
+ key = key[1:-1]
+ kvps[key] = value_name
+
+ return kvps
View
11 opentransit/urls.py
@@ -0,0 +1,11 @@
+# NOTE: Must import *, since Django looks for things here, e.g. handler500.
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('')
+
+urlpatterns += patterns(
+ 'opentransit.views',
+ url('^$', 'home', name='home'),
+ url('^example_petition_form$', 'example_petition_form', name='example_petition_form'),
+ url('^example_petition_success$', 'example_petition_success', name='example_petition_success'),
+)
View
242 opentransit/utils.py
@@ -0,0 +1,242 @@
+# NOTE DAVEPECK
+#
+# I wrote this code to help clients get sessions and users on django + appengine applications.
+# It is NOT AT ALL INTERESTING if you don't need users. In fact, for opentransit, I've
+# turned if off in the settings.py file. But I kept it here just in case...
+#
+# ...this code is based on werkzeug's secure cookie stuff... but heavily modified...
+#
+
+
+import os
+
+from hashlib import sha1
+import cPickle as pickle
+from hmac import new as hmac
+import urllib
+
+from django.core.urlresolvers import reverse
+from django.http import HttpResponseRedirect
+from django.utils.http import urlquote
+from django.template import RequestContext
+import django.shortcuts
+from django.conf import settings
+
+try:
+ from functools import update_wrapper
+except ImportError:
+ from django.utils.functional import update_wrapper # Python 2.3, 2.4 fallback.
+
+
+#------------------------------------------------------------------------------
+# URL Quoting Helpers
+#------------------------------------------------------------------------------
+
+def url_quote(s, charset='utf-8', safe='/:'):
+ if isinstance(s, unicode):
+ s = s.encode(charset)
+ elif not isinstance(s, str):
+ s = str(s)
+ return urllib.quote(s, safe=safe)
+
+def url_quote_plus(s, charset='utf-8', safe=''):
+ if isinstance(s, unicode):
+ s = s.encode(charset)
+ elif not isinstance(s, str):
+ s = str(s)
+ return urllib.quote_plus(s, safe=safe)
+
+def url_unquote(s, charset='utf-8', errors='ignore'):
+ return urllib.unquote(s).decode(charset, errors)
+
+def url_unquote_plus(s, charset='utf-8', errors='ignore'):
+ return urllib.unquote_plus(s).decode(charset, errors)
+
+
+#------------------------------------------------------------------------------
+# Django View Helpers
+#------------------------------------------------------------------------------
+
+def render_to_response(request, template_name, dictionary={}, **kwargs):
+ """
+ Similar to django.shortcuts.render_to_response, but uses a RequestContext
+ with some site-wide context.
+ """
+ response = django.shortcuts.render_to_response(
+ template_name,
+ dictionary,
+ context_instance=RequestContext(request),
+ **kwargs
+ )
+
+ return response
+
+def redirect_to(view, *args, **kwargs):
+ """
+ Similar to urlresolvers.reverse, but returns an HttpResponseRedirect for the
+ URL.
+ """
+ url = reverse(view, *args, **kwargs)
+ response = HttpResponseRedirect(url)
+
+ return response
+
+def not_implemented(request):
+ return render_to_response(request, "not_implemented.html")
+
+
+#------------------------------------------------------------------------------
+# Helpers for login redirection
+#------------------------------------------------------------------------------
+
+class _CheckLogin(object):
+ """
+ Class that checks that the user passes the given test, redirecting to
+ the log-in page if necessary. If the test is passed, the view function
+ is invoked. The test should be a callable that takes the user object
+ and returns True if the user passes.
+
+ We use a class here so that we can define __get__. This way, when a
+ _CheckLogin object is used as a method decorator, the view function
+ is properly bound to its instance.
+ """
+ def __init__(self, view_func, test_func, login_url=None, redirect_field_name=settings.REDIRECT_FIELD_NAME):
+ if not login_url:
+ from django.conf import settings
+ login_url = settings.LOGIN_URL
+ self.view_func = view_func
+ self.test_func = test_func
+ self.login_url = login_url
+ self.redirect_field_name = redirect_field_name
+
+ # We can't blindly apply update_wrapper because it udpates __dict__ and
+ # if the view function is already a _CheckLogin object then
+ # self.test_func and friends will get stomped. However, we also can't
+ # *not* update the wrapper's dict because then view function attributes
+ # don't get updated into the wrapper. So we need to split the
+ # difference: don't let update_wrapper update __dict__, but then update
+ # the (parts of) __dict__ that we care about ourselves.
+ update_wrapper(self, view_func, updated=())
+ for k in view_func.__dict__:
+ if k not in self.__dict__:
+ self.__dict__[k] = view_func.__dict__[k]
+
+ def __get__(self, obj, cls=None):
+ view_func = self.view_func.__get__(obj, cls)
+ return _CheckLogin(view_func, self.test_func, self.login_url, self.redirect_field_name)
+
+ def __call__(self, request, *args, **kwargs):
+ if self.test_func(request.user):
+ return self.view_func(request, *args, **kwargs)
+ path = urlquote(request.get_full_path())
+ tup = self.login_url, self.redirect_field_name, path
+ return HttpResponseRedirect('%s?%s=%s' % tup)
+
+def user_passes_test(test_func, login_url=None, redirect_field_name=settings.REDIRECT_FIELD_NAME):
+ """
+ Decorator for views that checks that the user passes the given test,
+ redirecting to the log-in page if necessary. The test should be a callable
+ that takes the user object and returns True if the user passes.
+ """
+ def decorate(view_func):
+ return _CheckLogin(view_func, test_func, login_url, redirect_field_name)
+ return decorate
+
+def login_required(function=None, redirect_field_name=settings.REDIRECT_FIELD_NAME):
+ """
+ Decorator for views that checks that the user is logged in, redirecting
+ to the log-in page if necessary.
+ """
+ actual_decorator = user_passes_test(
+ lambda u: (u is not None) and (u.is_valid),
+ redirect_field_name=redirect_field_name
+ )
+ if function:
+ return actual_decorator(function)
+ return actual_decorator
+
+
+#------------------------------------------------------------------------------
+# Helpers for signed serialization of python objects
+#------------------------------------------------------------------------------
+
+class UnquoteError(Exception):
+ pass
+
+_timegm = None
+def _date_to_unix(arg):
+ global _timegm
+ if isinstance(arg, datetime):
+ arg = arg.utctimetuple()
+ elif isinstance(arg, (int, long, float)):
+ return int(arg)
+ if _timegm is None:
+ from calendar import timegm as _timegm
+ return _timegm(arg)
+
+def _quote(value):
+ return ''.join(pickle.dumps(value).encode('base64').splitlines()).strip()
+
+def _unquote(value):
+ try:
+ return pickle.loads(value.decode('base64'))
+ except:
+ raise UnquoteError("whoops.")
+
+def serialize_dictionary(dictionary, secret_key=settings.SERIALIZATION_SECRET_KEY, expires=None):
+ secret_key = str(secret_key)
+ if expires:
+ dictionary['_expires'] = _date_to_unix(expires)
+ mac = hmac(secret_key, None, sha1)
+ result = []
+ for key, value in dictionary.iteritems():
+ result.append('%s=%s' % (url_quote_plus(key), _quote(value)))
+ mac.update('|' + result[-1])
+ return '%s?%s' % (mac.digest().encode('base64').strip(),'&'.join(result))
+
+def deserialize_dictionary(string, secret_key=settings.SERIALIZATION_SECRET_KEY):
+ items = None
+ if isinstance(string, unicode):
+ string = string.encode('utf-8', 'ignore')
+ try:
+ base64_hash, data = string.split('?', 1)
+ except (ValueError, IndexError):
+ items = None
+ else:
+ items = {}
+ mac = hmac(secret_key, None, sha1)
+ for item in data.split('&'):
+ mac.update('|' + item)
+ if not '=' in item:
+ items = None
+ break
+ key, value = item.split('=', 1)
+ # try to make the key a string
+ key = url_unquote_plus(key)
+ try:
+ key = str(key)
+ except UnicodeError:
+ pass
+ items[key] = value
+
+ # no parsing error and the mac looks okay, we can now
+ # sercurely unpickle our cookie.
+ try:
+ client_hash = base64_hash.decode('base64')
+ except Exception:
+ items = client_hash = None
+ if items is not None and client_hash == mac.digest():
+ try:
+ for key, value in items.iteritems():
+ items[key] = _unquote(value)
+ except UnquoteError:
+ items = None
+ else:
+ if '_expires' in items:
+ if time() > items['_expires']:
+ items = None
+ else:
+ del items['_expires']
+ else:
+ items = None
+ return items
View
30 opentransit/views.py
@@ -0,0 +1,30 @@
+from .forms import ExamplePetitionForm
+from .utils import render_to_response, redirect_to, not_implemented, login_required
+from .models import ExamplePetitionModel
+from django.http import HttpResponseRedirect
+
+def home(request):
+ return render_to_response(request, 'home.html')
+
+def example_petition_form(request):
+ # This example page handles the petition form!
+ # This is how you typically handle forms in Django.
+ # Again, it is only an example!
+
+ if request.method == 'POST':
+ form = ExamplePetitionForm(request.POST)
+ if form.is_valid():
+ model = ExamplePetitionModel()
+ model.name = form.cleaned_data['name']
+ model.email = form.cleaned_data['email']
+ model.city = form.cleaned_data['city']
+ model.state = form.cleaned_data['state']
+ model.country = form.cleaned_data['country']
+ model.put()
+ return redirect_to('example_petition_success')
+ else:
+ form = ExamplePetitionForm()
+ return render_to_response(request, 'example_petition_form.html', {'form': form})
+
+def example_petition_success(request):
+ return render_to_response(request, 'example_petition_success.html')
View
47 settings.py
@@ -0,0 +1,47 @@
+import os
+
+APPEND_SLASH = False
+DEBUG = os.environ['SERVER_SOFTWARE'].startswith('Dev')
+
+INSTALLED_APPS = ['opentransit']
+
+MIDDLEWARE_CLASSES = [
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.http.ConditionalGetMiddleware',
+]
+
+# NOTE davepeck:
+#
+# Add the following two middleware classes
+# if you want support for users in this application
+# (I wrote these classes myself for another project)
+#
+# 'opentransit.middleware.AppEngineSecureSessionMiddleware',
+# 'opentransit.middleware.AppEngineGenericUserMiddleware',
+
+
+ROOT_URLCONF = 'urls'
+
+TEMPLATE_CONTEXT_PROCESSORS = []
+
+# NOTE davepeck:
+#
+# (also add the following context processor if you want user support)
+#
+# 'opentransit.context.appengine_user'
+
+TEMPLATE_DEBUG = DEBUG
+
+TEMPLATE_DIRS = [os.path.join(os.path.dirname(__file__), 'templates')]
+
+TEMPLATE_LOADERS = ['django.template.loaders.filesystem.load_template_source']
+
+FILE_UPLOAD_HANDLERS = ['django.core.files.uploadhandler.MemoryFileUploadHandler']
+
+FILE_UPLOAD_MAX_MEMORY_SIZE = 1048576 # 1 MB
+
+SERIALIZATION_SECRET_KEY = '\xcfB\xf6\xb9\xc4\xe4\xfa\x07\x8atE\xdc\xec\xf9zaR\xa4\x13\x88'
+
+LOGIN_URL = "/login/"
+
+REDIRECT_FIELD_NAME = "redirect_url"
View
0  static/css/PUT_CSS_HERE
No changes.
View
0  static/images/PUT_IMAGES_HERE
No changes.
View
BIN  static/images/otd-example-logo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
0  static/js/PUT_JAVASCRIPT_HERE
No changes.
View
26 templates/base.html
@@ -0,0 +1,26 @@
+<!-- NOTE DAEVPECK: THIS IS JUST AN EXAMPLE. THROW IT AWAY OR USE IT AS YOU SEE FIT. -->
+
+<html>
+<head>
+<title>[{% block title %}open transit{% endblock %}]</title>
+<link rel="stylesheet" href="{% static_url /css/screen.css %}" type="text/css" media="screen" />
+</head>
+<body>
+
+<div class="container">
+ <div class="header">
+ {% block header %}
+ <a href="{% url home %}">
+ <!-- here is an example use of the static_url tag -->
+ <img src="{% static_url /images/otd-example-logo.png %}" alt="Example Image" />
+ </a>
+ {% endblock %}
+ </div>
+
+ <div class="content">
+ {% block content %}&nbsp;{% endblock %}
+ </div>
+</div>
+
+</body>
+</html>
View
22 templates/example_petition_form.html
@@ -0,0 +1,22 @@
+<!-- NOTE DAEVPECK: THIS IS JUST AN EXAMPLE. THROW IT AWAY OR USE IT AS YOU SEE FIT. -->
+
+{% extends 'base.html' %}
+
+{% block title %}open transit!!! neat!!!{% endblock %}
+
+{% block content %}
+ <h2>Example petition form.</h2>
+ <p>This page is just an example showing how to use Django forms, if you want to. I did the petition form shown in the mockup.</p>
+
+ <p>
+ <form action="" method="POST">
+ <table>
+ {{ form.as_table }}
+ </table>
+
+ <input type="submit"></input>
+ </form>
+ </p>
+
+ <p>(Or, you can <a href="{% url home %}">Click to go to "home"</a>)</p>
+{% endblock %}
View
11 templates/example_petition_success.html
@@ -0,0 +1,11 @@
+<!-- NOTE DAEVPECK: THIS IS JUST AN EXAMPLE. THROW IT AWAY OR USE IT AS YOU SEE FIT. -->
+
+{% extends 'base.html' %}
+
+{% block title %}open transit!!! neat!!!{% endblock %}
+
+{% block content %}
+ <h2>Thanks for submitting the petition entry! We got it!</h2>
+ <p><a href="{% url home %}">Click to go to "home"</a></p>
+ <p>(Or, click to go to <a href="/_ah/admin/datastore?kind=ExamplePetitionModel">the appengine local development server admin page to see if something got added to the datastore</a>.)</p>
+{% endblock %}
View
9 templates/home.html
@@ -0,0 +1,9 @@
+{% extends 'base.html' %}
+
+{% block title %}open transit!!!.{% endblock %}
+
+{% block content %}
+ <!-- NOTE DAEVPECK: THIS IS JUST AN EXAMPLE. THROW IT AWAY OR USE IT AS YOU SEE FIT. -->
+ <h2>You&apos;ve found the <strong>opentransit</strong> home page!</h2>
+ <p><a href="{% url example_petition_form %}">Click to go to to an example petition form page...</a></p>
+{% endblock %}
View
5 urls.py
@@ -0,0 +1,5 @@
+# NOTE: Must import *, since Django looks for things here, e.g. handler500.
+from django.conf.urls.defaults import *
+
+# Load the opentransit application's URLs
+urlpatterns = patterns('', url(r'', include('opentransit.urls')))
Please sign in to comment.
Something went wrong with that request. Please try again.