Proudly ProcrasDonating

Technology Thoughts

Archive for technical

ProcrasDoCoder Conversation Starter

The ProcrasDoCoder Ring is

  • a conversation starter for my startup, ProcrasDonate
  • gives the team a warm fuzzy feeling while we’re working
  • a fun introduction to microcontrollers

The ring, or possibly a pin next to one’s name tag or attached to one’s hat, consists of a bright BlinkM RGB LED with long wires to an arduino unit hidden in one’s pocket or belt loop. The LED lights up colorfully and brilliantly for a few seconds whenever a user of ProcrasDonate donates to a charity, makes a pledge, downloads our software or participates in some user test.

Currently, the code to do all this has been written, but the hardware housing is still in prototype phase. I’ll update the following videos and pictures soon; hopefully with a real use case in the wild.

This video shows a simple demo of the WiShield and BlinkM.

How it works

The ProcrasDoCoderRing is a simple mashup that stands on the shoulders of 4 giants:

Arduino + WiShield + BlinkM + ProcrasDonate web server

In case it’s not clear: much of the WiShield and BlinkM code is copied from relevant examples.

  • Arduino – Arduino starter pack from AdaFruit, snap.
  • WiShield – from AsyncLabs

    Nice library. Needed to hack a few config lines in the C code for WEP 5 byte passcode (see section below), but otherwise comes with good documentation.

  • BlinkM – RGB LED kit from ThingM

    Comes with a nice arduino library that abstracts fading the LED to a specific RGB or HSB.

  • ProcrasDonate web server - python Django website

There are two different ways this could work:

  1. The arduino registers itself with the ProcrasDonate site, which then pushes information to the arduino using HTTP web requests to the WiShield webpage server.
  2. Instead, the arduino regularly pings a small Django app running on ProcrasDonate. The app has a single webpage, which returns the donation counts, etc.

To view the code and more detailed documentation, see the ProcrasDoCoder github repository.

Proudly ProcrasDonating,

Lucy.

Django Tricks, Part 7 – Django from shell

This post is the seventh and last in a series on Django tricks that began with automating Admin Model creation.

This is one of my favorite tricks: setup the Django environment from the Python interpreter.

(master) $ python >>> from lib.django_from_shell import start_django as sd >>> sd() >>> from procrasdonate.models import User >>> User.objects.count() 22

All we need to do is wrap a few Django calls like so:

def start_django():     import sys     #Now the nearest ancestor directory with a 'settings.py' file     settings_dir = find_file_in_ancestors("settings.py")     sys.path.append(settings_dir)         from django.core.management import setup_environ     import settings     setup_environ(settings)

By being flexible about finding the global settings.py file, we can call start_django from other scripts buried in bin or cron directories:

def find_file_in_ancestors(filename):     """     For each parent directory, check if 'filename' exists.  If found, return     the path; otherwise raise RuntimeError.     """     import os     path = os.path.realpath(os.path.curdir)     while not filename in os.listdir(path):         #if filename in os.listdir(path):         #    return path         newpath = os.path.split(path)[0]         if path == newpath:             raise RuntimeError("No file '%s' found in ancestor directories." % filename)         path = newpath     return path

Conclusion

The goal of these tricks is to reduce copy/past/alter drudgery. It’s rewarding to automate the creation of fluff code, even easy fluff that knocked my socks off when I first learned Django.

Of course, it’s hard to stay impressed with myself or Dan for long since I quickly forget that the fluff code even existed. I suppose that’s part of the point. I can forget about creating Admin Models, and new models still show up in the Admin app as I thoughtlessly expected.

Still, even if I’m not impressed, I do notice how often I get to write new, interesting code. That’s why I’m Proudly ProcrasDonating,

Lucy.

Django Tricks, Part 6 – View Utilities

This post is the sixth in a series on Django tricks.

We started with automating Admin Model creation, and continued with automating initialization and installation, model mixins, forms and app settings.

This post diverges from automation to point out some useful view utilities.

Render Response

This trick is more like standard Django practice: wrap render_to_response with your own function that uses your own RequestContext.

def render_response(request, template, dictionary=None):     """     Return render_to_response with context_instance=RequestContext(request).     """     dictionary = dictionary or {}     return render_to_response(template, dictionary, context_instance=RequestContext(request)) def render_string(request, template, dictionary=None):     dictionary = dictionary or {}     return render_to_string(template, dictionary, context_instance=RequestContext(request))

One small thing sometimes forgotten is that when you call render_response from your view, you don’t have to write out the dictionary, which is really just a copy/double-paste of local variable names. Instead, call locals().

some_view.py

def info(request):     user_count = User.objects.count()     visitor_count = Visitor.objects.count()     pledge_count = ...     donation_count = RecipientPayment.objects.count()     return render_response(request, 'procrasdocoder/info.html', locals())

Extract Parameters

Another useful convenience function is extract_parameters, which extracts expected and optional parameters from a request’s POST and GET objects, as well as reporting appropriate errors.

Here’s an example use:

def receive_data(request):     expected_parameters = ["private_key",                            "prefs"]     optional_parameters = ["foo",                            "bar"]     response = extract_parameters(request, "POST", expected_parameters)     if not response['success']:         Log.Error(response['reason'], "communication_error")         return json_failure(message)     parameters = response['parameters']     private_key = parameters['private_key']     ...

Here is the definition for extract_parameters:

def extract_parameters(request, method_type, expected_parameters, optional_parameters=None):     ret = {}         if not getattr(request, method_type):         return {'success': False,                 'reason': "Expected %s parameters" % method_type}         for p in expected_parameters:         try:             v = getattr(request, method_type).get(p, None)             if v == None:                 return {'success': False,                         'reason': "Missing expected parameter: %s" % p}             ret[p] = v         except:             return {'success': False,                     'reason': "Something unexpected happened while extracting parameters"}         optional_parameters = optional_parameters or {}     for p in optional_parameters:         try:             v = getattr(request, method_type).get(p, None)             if v:                 ret[p] = v         except:             return {'success': False,                     'reason': "Something unexpected happened while extracting optional parameters"}               return {'success': True,             'parameters': ret}

This is a marathon super-bowl blogging experience for me, and the English to Code ratio is quickly approaching zero. I’ve left the best for last, though, so check it out: Part 7 – Django from shell.

Proudly ProcrasDonating,

Lucy.

Django Tricks, Part 5 – Automatic App Settings

This post is the fifth in a series on Django tricks.

We started with automating Admin Model creation, and continued with automating initialization and installation, model mixins and forms.

This post describes how we automate incorporating new Django apps into our project.

Creating apps is a wonderful way to modularize work. Encapsulation, abstraction, re-use!

To reduce copy/paste/rewrite drudger we’ve automated our project’s settings.py file according to standard Django conventions.

APPS list

First, we need to list what apps are active in the project:

APPS = ('procrasdonate', 'crosstester', 'adwords', 'procrasdocoder')

Pathify utility

Let’s setup some convenience path functions.

path converts a path with unix path separator, /, into a path with the appropriate os-specific path separator.

pathify converts a list of path components into a string path with appropriate path separator.

Finally, PROJECT_PATH holds the projects root absolute path.

import os def path(p):     """     @param p: path with unix path separator     @return: string with os-specific path separator     """     return os.sep.join(p.split("/")) import re _space_replace = re.compile("([^\\\])( )") def pathify(lst):     """     @param lst: list of path components     @return: string with os-specific path separator between components     """     # replaces spaces with raw spaces so that spaces in file names work     repl = r"\g<1> "     return os.sep.join([_space_replace.sub(repl, el) for el in lst]) PROJECT_PATH = os.path.dirname(os.path.realpath(__file__))

Middleware

Now we’re into the core of the work, which is to iterate through each app and append the appropriate path or module to the relevant tuple.

MIDDLEWARE_CLASSES = (     'django.middleware.doc.XViewMiddleware',     'django.middleware.common.CommonMiddleware',     'django.contrib.sessions.middleware.SessionMiddleware',     'django.contrib.auth.middleware.AuthenticationMiddleware',     'ext.pagination.middleware.PaginationMiddleware',     'django.contrib.csrf.middleware.CsrfMiddleware',     'lib.ssl_middleware.SSLRedirect', ) for app in APPS:     if os.path.exists(pathify([PROJECT_PATH, app, 'middleware.py'], file_extension=True)):         MIDDLEWARE_CLASSES += (             '%s.middleware.%sMiddleware' % (app, app.capitalize()),         )

Templates

Add template directories.

Note: Do you see how we also add the template directory of our Firefox extension? This is so that our server can reuse extension templates (OMGSQUEEL).

TEMPLATE_DIRS = (     #'procrasdonate/ProcrasDonateFFExtn/content/templates',     pathify([PROJECT_PATH, path('procrasdonate/ProcrasDonateFFExtn/content/templates')]), ) for app in APPS:     if os.path.exists(pathify([PROJECT_PATH, app, 'templates'])):         TEMPLATE_DIRS += (             pathify([PROJECT_PATH, path('%s/templates' % app)]),         )

Installed Apps

INSTALLED_APPS = (     'django.contrib.contenttypes',     'django.contrib.sessions',     'django.contrib.sites',     'django.contrib.humanize',     'ext.pagination',     'django.contrib.auth',     'django.contrib.admindocs',     'django.contrib.admin', ) for app in APPS:     INSTALLED_APPS += (         app,     )

Context Processors

TEMPLATE_CONTEXT_PROCESSORS = (     'lib.context.defaults',     'django.core.context_processors.auth',     'django.core.context_processors.debug',     'django.core.context_processors.i18n',     'django.core.context_processors.media',     'django.core.context_processors.request', ) for app in APPS:     if os.path.exists(pathify([PROJECT_PATH, app, 'context.py'], file_extension=True)):         TEMPLATE_CONTEXT_PROCESSORS += (             '%s.context.defaults' % app,         )

This only works if the app includes a “context.py” file with a function called “defaults” that returns the context dictionary:

context.py

def defaults(request):     frame = {}     frame.update(useful_settings())     frame.update(header_data())     ...     return frame def useful_settings():     return { 'PDVERSION': settings.PDVERSION } def header_data():     return { ... }

Urls

The project’s base URL file is modified to automatically incorporate each app’s URLs.

For example, an app called “magic_cape” would be accessible from the URL “/magic_cape/”, assuming it contained a “urls.py” file inside a “views” directory.

To customize the urls for an app, it’s name is added to the CUSTOM_URLS_APPS tuple.

CUSTOM_URLS_APPS = ('procrasdonate',) for app in settings.APPS:     if app not in CUSTOM_URLS_APPS and os.path.exists(pathify([settings.PROJECT_PATH, app, 'views'])):         urlpatterns += patterns('',             (r'^%s/' % app, include('%s.views.urls' % app)),         )

Proudly ProcrasDonating,

Lucy.

Older entries »