Pandemonium Illusion

SelectMultiple for a Google App Engine ListProperty using Django Forms

July 15, 2008 · No Comments

I just started with Google App Engine the other day. Man, who could resist a free server environment?

Being a long time Django developer (one year), I was really excited about it because it “runs django”. Now this isn’t really the case because of BigTable, which isn’t a real relational database. However, if you forgo the Django ORM and use GAE models, you can still work with the other things that make Django so awesome: templates and forms. AND, the google models are so similar to Django that you’ll barely even notice the difference.

Ok, this isn’t a full on review GAE with Django, so I’ll cut right to the chase. I’m sure you found this page when you were trying to use django forms to handle the ListProperty model field in your HTML forms. The problem is that things like ModelChoiceField and ModelMultipleChoiceField won’t work because they rely on the Django ORM. For that matter, ChoiceField and MultipleChoiceField won’t work because they return a list of strings, that although they can be the string for a db.key(), they won’t be the Key objects that your new models expect for ListProperty.

So, to get around this I just adjusted the MultipleChoiceField a bit so that it returns the list chosen fields as Key objects:

#MODELS
class Toping(BaseModel):
    name = db.StringProperty(required=True)

class Pizza(BaseModel):
    name = db.StringProperty(required=True)
    toppings = db.ListProperty(db.Key)

    def get_topings(self):
        # This returns the models themselves, not just the keys that are stored in toppings
        return Topping.get(self.toppings)

#FORMS
class PizzaForm(djangoforms.ModelForm):
    toppings = ListPropertyChoice(
                        widget=forms.CheckboxSelectMultiple(),
                        choices=[(rt.key(), rt.name) for rt in db.Query(Topping)]
                    )
    class Meta:
        model = Pizza

#CUSTOM FIELD
from django.newforms.util import ErrorList, ValidationError
from django.newforms.fields import ChoiceField
from django.newforms.widgets import MultipleHiddenInput, SelectMultiple
from appengine_django.models import BaseModel

from appengine_django.serializer.python import smart_unicode
from django.utils.translation import gettext

class ListPropertyChoice(ChoiceField):
    hidden_widget = MultipleHiddenInput

    def __init__(self, choices=(), required=True, widget=SelectMultiple, label=None, initial=None, help_text=None):
        super(ListPropertyChoice, self).__init__(choices, required, widget, label, initial, help_text)

    def clean(self, value):
        """
        Validates that the input is a list or tuple.
        """
        if self.required and not value:
            raise ValidationError(gettext(u'This field is required.'))
        elif not self.required and not value:
            return []
        if not isinstance(value, (list, tuple)):
            raise ValidationError(gettext(u'Enter a list of values.'))
        new_value = []
        for val in value:
            val = smart_unicode(val)
            new_value.append(val)
        # Validate that each value in the value list is in self.choices.
        valid_values = set([smart_unicode(k) for k, v in self.choices])
        for val in new_value:
            if val not in valid_values:
                raise ValidationError(gettext(u'Select a valid choice. %s is not one of the available choices.') % val)
        # These are the only changes to the django MultipleChoice Field
        # we just convert the list of strings to a list of keys
        key_list = []
        for k in new_value:
            key_list.append(BaseModel.get(k).key())
        return key_list

Now for a quick expaination… The ListPropertyChoice class itself is really just the MultipleChoiceField from Django V0.96. I had to use this because I couldn’t get the latest svn version to work w/ App Engine due to some SafeUnicode issues I haven’t looked into yet. The only real changes to that class are the last four lines which return the list of keys instead of key-strings from the form.

The only other thing that might need explanation is the get_toppings() method that I added to the Pizza model. This is just a shortcut to access the models instead of the keys, as they’re stored in the ListProperty.

→ No CommentsCategories: App Engine · django · webdev
Tagged: , ,

Bulk Add/Replace from Command Line

May 31, 2008 · No Comments

I had to do this like 5 times today, so I thought I’d put it out there (so I can reference it).

Note: you might want to backup your stuff, just in case you screw up your regular expression.

Basically, I have a bunch of files that need to have changes, such as email address, or include paths.

So, for example, in one I had to change a bunch of email addresses so I used `sed`. To do this on one file you would simply run the sed command directly:
sed -e s/info@olddomain.com/info@newdomain.com/g -i file.html
-e tags the regular expression you’re using
-i means we’re editing the files inline (not outputting the results to standard out)
Don’t forget g, because this will ensure that the expression is greedy and will find all the instances of this expression.

Of course, you probably want to run this on a bunch of files, so you would use `find`, because it’s awesome:
find -name "*.html" -exec sed -e s/info@olddomain.com/info@newdomain.com/g -i {} \;

→ No CommentsCategories: Uncategorized
Tagged:

Starting a new Django Project with Versioning

May 12, 2008 · No Comments

Having an efficient working environment can be essential to the success of any project and can save hours, if not days of extra work in the long run. So, I’ve put together a little “getting started guide” to putting the full package in place with Django, SVN, and Trac.

Create your django project anywhere

~/django/projects$ ../src/trunk/django/bin/django-admin.py startproject myProject

Set up your SVN
This can be done either on your local machine or on another server. I like /var/svn/repos/myProject/.

$ svnadmin create /var/svn/repos/myProject

I use svnserve to serve my svn so I update the permissions in /var/svn/repos/myProject/conf/svnserve.conf:

[general]
anon-access = none
auth-access = write
password-db = passwd

/var/svn/repos/myProject/conf/passwd

[users]
user = passwd

(re)Start the svnserve daemon

svnserve -d -r /var/svn/repos

Finally, we’re ready to check in the project tree

$ svn import myProject file:///var/svn/repos/myProject/trunk/myProject -m “Initial import”

Now just import it into Eclipse and your good to go! ;)

Oh, and you might want to read about excluding .pyc files in svn.

→ No CommentsCategories: django · webdev
Tagged: , ,

Compress a PDF with pdftk

May 7, 2008 · No Comments

Ok, so I don’t know why this works, but it does and I haven’t seen any decrease in quality. I suspect converting from PDF to PS and back just optimizes the PDF.
Requirements

The Process

  1. Convert your PDF to PS (this creates a large file
  2. Convert the new PS back to a PDF


pdf2ps large.pdf very_large.ps
ps2pdf very_large.ps small.pdf

Results
large.pdf : 6.3MB
very_large.ps : 53.4MB
small.pdf : 2.4MB
Looks like pretty good compression to me.

If anyone knows why this works so well, please let me know.

→ No CommentsCategories: Uncategorized

Ignore .pyc files in Subversion

May 7, 2008 · 2 Comments

Subversion allows you to exclude certain files using regular expressions. This needs to be applied to each directory individually. So, if you add a new directory I think you will need to run the command again. To apply the svn:ignore property to your project you just need to do two things:

Create a file (.svnignore) with the regular expressions

*.pyc
*~

Run svn propset on the project

svn -R propset svn:ignore -F .svnignore .

-R indicates that you are doing this recursively so all the directories ignore these.
-F indicates the file we just created with the regular expressions.

EDIT 5/27/08:
If you’ve already got yourself into the mess of versioning your compiled files, then you might want to do a few things.
1) Revert all the .pyc files (you can always recompile them). You don’t want there to be any local changes when you try to delete them:
find -name "*.pyc" -exec svn revert {} \;
2) Delete all the .pyc files using `svn delete`:
find -name "*.pyc" -exec svn delete {} \;
3) Finally recompile and re-run the svn ignore code above

→ 2 CommentsCategories: django · webdev
Tagged: ,

Backing up with BackupPC

May 5, 2008 · No Comments

BackupPC is an awesome tool for backing up a number of different machines. BackupPC has a web application that manages your backups and a separate backup application that runs periodically based on the settings you have in the web admin.

The design is ingenious because it can back up a variety of machines with unique settings. For windows machines I use a protected share that can only be accessed by a user called ‘backup’. The backup application then connects periodically to the share and runs incremental backups. For the linux machines I run an rsync demon on the pc. This is one of the few cases where it’s as easy on windows as linux. (In case there’s any doubt… everything is easier on linux 99% of the time.)

Given that I use linux exclusively, here’s the documentation on setting up an rsyncd connection with Backuppc.

1) Install rsyncd: $ sudo apt-get install rsync (ubuntu)
2) Configure rsyncd with a backup option (/etc/rsyncd.conf)

[backup]
path = /home/jamstooks
exclude = “/home/jamstooks/.mozilla/firefox/***”
comment = Backup
uid = jamstooks
gid = jamstooks
read only = true
auth users = backup
secrets file = /etc/rsyncd.secrets

I excluded the firefox directory, because I was getting issues with the cache being accessed during backup. It would probably be best to include the bookmarks though.
/etc/rsyncd.secrets stores the passwords in plain-text, so make sure you adjust your permissions appropriately:

backup:passwd

3) Ubuntu sets up an init.d setup file, but you want to make sure the daemon is running…
4) Finally, you are ready to set up BackupPC. In the BackupPC admin, simply select rsync for this machine and fill in the password you selected.

More details can be found in the official BackupPC documentation.

→ No CommentsCategories: Uncategorized
Tagged: , ,

Quick image resizing with python

May 4, 2008 · 2 Comments

I typically build automatic resizing into any django models that use ImageField by overriding the save function like so:

def save(self):
super(GalleryImage, self).save() # Call the “real” save() method
if self.image:
# now change the size of the image
path = settings.MEDIA_ROOT + self.image
img1 = PIL.open(path)
img2 = PIL.open(path)
img2.save(self.get_thumbnail_path())

size = 600,600
img1.thumbnail(size, PIL.ANTIALIAS)
img1.save(path)

# create the thumbnail
size = 150, 150
img2.thumbnail(size, PIL.ANTIALIAS)
img2.save(self.get_thumbnail_path())

def get_thumbnail_path(self):
path = settings.MEDIA_ROOT + self.image
return self.convert_path_to_thumbnail(path)

def get_thumbnail_url(self):
path = self.get_image_url()
return self.convert_path_to_thumbnail(path)

def convert_path_to_thumbnail(self, path):

basedir = os.path.dirname(path) + ‘/’
base, ext = os.path.splitext(os.path.basename(path))

# make thumbnail filename
th_name = base + “_tn”
th_name += ext
return urlparse.urljoin(basedir, th_name)

This ensures that all my images conform to the sizes I need. However, even if the server is automatically resizing the images for me, I don’t want to spend hours uploading huge images only to have them resized. It’s just a waste of bandwidth and time. So, I’ve written a quick python script that resizes all the images in a given directory and places the copies in a new directory called resized:


#! /usr/bin/env python

“”"
What:
Resize all the jpg images in a directory
All images will be placed inside a directory called “resized”
This directory must exist
Usage: ./resize.py
“”"

import PIL.Image as PIL
import re, os, sys, urlparse

SIZE = 600,600
JPG = re.compile(”.*\.(jpg|jpeg)”, re.IGNORECASE)

def get_new_path(path):
basedir = os.path.dirname(path) + ‘/resized/’
base, ext = os.path.splitext(os.path.basename(path))
file = base + ext

return urlparse.urljoin(basedir, file)

try:
image_dir = sys.argv[1]
except:
print “You must specify a directory”
exit(0)

files = os.listdir(image_dir)
for file in files:
if JPG.match(file):
f = image_dir.rstrip(”/”) + “/” + file
print f
img = PIL.open(f)
img.thumbnail(SIZE, PIL.ANTIALIAS)
img.save(get_new_path(f))

→ 2 CommentsCategories: django · webdev
Tagged: , ,

Server Uptime Monitoring with Python

April 21, 2008 · No Comments

We had a black out over the weekend, while I was away, so I decided to add a few more tools to keep me informed. See, when the power goes out the server can’t exactly let me know that it’s down, can it?

Fortunately, I have a slice at SliceHost which never goes down, so I decided to use that to monitor our in-house server. I put together a simple python script to do the job. It simply queries the HTTP Headers of the home page of each of our sites and emails me and sends a TXT to my cellphone if it gets anything other than a 200 status code.

The script itself is run by a cron demon every 15 minutes, but it won’t overwhelm my phone because I have a built-in Message Delay in the script.


#! /usr/bin/env python

import httplib
from urlparse import urlparse
from datetime import datetime, timedelta
import os, platform, time

# Modify your settings here
SITES = ( “http://www.site.com”, ) # Sites to monitor
MAIL_FROM = “admin@site.com”
MAIL_TO = ["Twoway.*********@messaging.nextel.com","ben@site.com"]
MESSAGE_DELAY = 24 * 60 # The time between alerts in minutes
LOCK_FILE = “remote_mon_lock” # Lock file used for alert staggering

def send_alert(status, site):
“”"
Send an email alert to MAIL_TO if the status is not 200
Use a Lock File to ensure that message don’t get sent more
frequently than the MESSAGE_DELAY, to prevent large TXT bills.
“”"
if status != 200:
if not os.path.exists(LOCK_FILE):
old_mod_date = datetime.now() - timedelta(minutes=MESSAGE_DELAY + 1)
old_timestamp = time.mktime(old_mod_date.timetuple())
f = open(LOCK_FILE, ‘w’)
f.close()
os.utime(LOCK_FILE, (old_timestamp, old_timestamp))

mod_date = datetime.fromtimestamp(os.path.getmtime(LOCK_FILE))
next_send_time = mod_date + timedelta(minutes=MESSAGE_DELAY)
if datetime.now() > next_send_time:
import smtplib
s = smtplib.SMTP()
s.connect()
s.sendmail(MAIL_FROM, MAIL_TO, “%s - %s” % (str(status), site))
s.close()
os.utime(LOCK_FILE, None)

for site in SITES:
url = urlparse(site)
error = “”
try:
conn = httplib.HTTPConnection(url[1])
# Use a HEAD request to get the status code
conn.request(”HEAD”, url[2])
status = conn.getresponse()
error = status.status
print “%d : %s : %s” % (status.status, site, datetime.now())
except:
print “Connection Failed : %s : %s” % (site, datetime.now())
error = “Connection Failed”

send_alert(error, site)

→ No CommentsCategories: Uncategorized

Nginx SSL passphrase at startup

April 21, 2008 · No Comments

I’m using nginx to serve all my media files and it is also a proxy to my apache server. I’ve been using SSL encryption for specific sections of the site, using nginx’s built in SSL support:

server {

listen 443;
server_name host.com;

access_log /var/log/nginx/register/host.access.log;

# SSL
ssl on;
ssl_certificate /home/jamstooks/ssl_certs/2008_cert/host.com.crt;
ssl_certificate_key /home/jamstooks/ssl_certs/2008_cert/host.com.key;

# Register
location ^~ /register/ {
proxy_pass http://127.0.0.1:8080/;
proxy_redirect off;

proxy_set_header Host register.host.com;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

client_max_body_size 10m;
client_body_buffer_size 128k;

proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;

proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
}

# Redirect everything else
location / {
rewrite ^(.*) http://www.host.com$1 permanent;
}

}

When I boot up Nginx it requests the passphrase for the encrypted certificate key. This is a huge problem though when there are unexpected shutdowns because the Nginx process won’t restart.

However, the problem is not with Nginx, but with the certificate itself. Because it is encrypted, Nginx can’t use it unless it until it has the pass-phrase. So, the easiest way to solve this is to provide Nginx with a decrypted version of the certificate key. The only issue is that you need to tie down the permissions on the file so that no one can access it at use it to impersonate you.

Apache details the process here:

# Remove the encryption from the RSA private key (while keeping a backup copy of the original file):

$ cp server.key server.key.org
$ openssl rsa -in server.key.org -out server.key

# Make sure the server.key file is only readable by root:

$ chmod 400 server.key

→ No CommentsCategories: webdev
Tagged: , , ,

Less is More

April 18, 2008 · No Comments

I’m a firm believer that this saying holds true ALL THE TIME.

But in this case I’m just thinking about the `less` command. It’s just better than `more`.

So, if you’re using Linux and the bash shell put the following in your .bashrc file:

alias more=’less’

→ No CommentsCategories: Uncategorized
Tagged: ,