Category Archives: Tips ‘n Tricks

Can’t start the dev server for a Google App Engine Java project on Snow Leopard?

This one was ruining my afternoon until I came across this post on the Google App Engine Issues page. I was running the command to start the dev server from the Google App Engine Java tutorial in Terminal on OS X 10.6. The process looked like it would start fine, but would then immediately stop (as evidenced by the new terminal line after the success message):

Kevins-iMac:com.google.appengine.eclipse.sdkbundle kevin$ ./appengine-java-sdk/bin/dev_appserver.sh appengine-java-sdk/demos/guestbook/war
2011-03-16 17:06:59.443 java[8217:a07] [Java CocoaComponent compatibility mode]: Enabled
2011-03-16 17:06:59.444 java[8217:a07] [Java CocoaComponent compatibility mode]: Setting timeout for SWT to 0.100000
Mar 16, 2011 9:07:00 PM com.google.apphosting.utils.jetty.JettyLogger info
INFO: Logging to JettyLogger(null) via com.google.apphosting.utils.jetty.JettyLogger
Mar 16, 2011 9:07:00 PM com.google.apphosting.utils.config.AppEngineWebXmlReader readAppEngineWebXml
INFO: Successfully processed /Applications/eclipse/plugins/com.google.appengine.eclipse.sdkbundle/appengine-java-sdk/demos/guestbook/war/WEB-INF/appengine-web.xml
Mar 16, 2011 9:07:00 PM com.google.apphosting.utils.config.AbstractConfigXmlReader readConfigXml
INFO: Successfully processed /Applications/eclipse/plugins/com.google.appengine.eclipse.sdkbundle/appengine-java-sdk-1.4.2/demos/guestbook/war/WEB-INF/web.xml
Mar 16, 2011 5:07:00 PM com.google.appengine.tools.development.DevAppServerImpl start
INFO: The server is running at http://localhost:8080/
Kevins-iMac:com.google.appengine.eclipse.sdkbundle kevin$      # this is where things start to suck

Long story short, Apple decided to stop maintaining Java on their operating systems. To fix the problem, install the latest version of OpenJDK.

  1. Download this and install it: http://openjdk-osx-build.googlecode.com/files/OpenJDK-1.7-x86_64-20110221.dmg (most recent as of 3/16/11)
  2. Open your Java Preferences by going to Applications -> Utilities -> Java Preferences
  3. Drag the new Java version (Java SE 7) up above any other Java versions listed

After that you should be able to run the server again.

If you want to fix the problem from within Eclipse, check out Comment 14.

Create and Download a CSV (or any other file type) with a Plone View

Do you have a content type that you’d like to be able to offer as Comma Separated Value (CSV)/Excel download? Python can create both formats pretty easily (using either the standard csv library, or xlwt) and you can write a view for your content type that will create the file and return it to the user as a file download.

At Plone.org, Martin Aspeli covers this process as a part of the five.grok documentation. I’d already created my product without using five.grok, and I didn’t figure this one use case was enough reason to switch. So here’s what I came up with:

First, register the view in browser/configure.zcml

...
    <browser:view
      for="my.product.interfaces.IMyContentType"
      name="csv_view"
      class=".mycontenttypeview.MyContentTypeCsvView"
      permission="zope.Public"
      />
...

I added the view to browser/myContentType.py under the default view.

...
class MyContentTypeCsvView(BrowserView):
    """
    Download the content type as a CSV file.
    """
    
    def __call__(self):
        """
	Build a CSV from the content type.
	"""
	out = StringIO()
	writer = csv.writer(out)

        ...
        # make the CSV file
        ...

	filename = "%s.csv" % context.id

	self.request.response.setHeader('Content-Type', 'text/csv')
	self.request.response.setHeader('Content-Disposition', 'attachment; filename="%s"' % filename)

        return out.getvalue()

Now you should be able to link to http://mysite.com/mycontenttype/csv_view and initiate a file download.

How to access the SD card from the Android Emulator on Mac OS

Here’s another one in the “I’m writing this down so I won’t forget it” category. When you’re working with the Android Emulator and you want to add files to the virtual SD card on the AVD, you have to mount it. To do so, hdid the the SD card image:

hdid ~/.android/avd/my-avd.avd/sdcard.img

The guts of the AVD live in a hidden folder in your user folder. Change the ‘my-avd.avd’ bit to match the name of the AVD whose SD card you want to manipulate. After that you’ll have a drive called ‘SDCARD’ on your Desktop that you can open and drop files in.

Unforunately, with this method you have to restart the Emulator every time you want to change the file. That’s not a big deal for me right now, but I’m sure it will be soon. I’ll update this post when/if I figure out how to change the files without restarting the device.

How to view the contents of the browser in ZopeTestCase

In first writing tests for Plone products, it was difficult for me to figure out what was in the browser when a failure occurred. Adding the two following lines just after the offending code will create a file ‘test.html’ that you can view in a real broswer:

>>> f = open('/Users/kevin/Desktop/test.html','w')
>>> f.write(browser.contents)

Change the path as necessary to point to your desktop, run the test, and click the file on your Desktop to figure out what the heck is going on.

Adding a new user in a ZopeTestCase doctest

One of the content types in a Plone product I’m working on needed to refer to one of the users in the site. Therefore I needed to get some users in the system in the doctest. At first I tried .click()ing through the interface to get to add a new user through Site Setup. After much wailing and gnashing of teeth, I realized I could do this in straight Python:

>>> pr = self.portal.portal_registration
>>> pr.addMember(id = 'ddastardly',
...              password = 'password',
...              roles = ["Member",],
...              properties = {'fullname': 'Dick Dastardly',
...                            'username': 'ddastardly',
...                            'email': 'ddastardly@example.com',}
...              )

This is cool because I don’t need to test the Plone “Add a New User” interface as a part of my product. I only need a few users available to test my content type. Later, if you need to do anything as this user, you can log in like this.

>>> browser.getLink('Log in').click()
>>> browser.getControl(name='__ac_name').value = 'swhiplash'
>>> browser.getControl(name='__ac_password').value = 'password'
>>> browser.getControl(name='submit').click()
>>> 'You are now logged in' in browser.contents
True

Accessing radio buttons & checkboxes in the ZopeTestCase browser

This definitely falls into the ‘reminding-myself-for-next-time’ blog category, but maybe this will help someone else out. I found myself needing to test radio buttons that didn’t have labels (don’t ask why). Those radio buttons all shared the same name attribute. Trying to access them by the name got the larger ListControl, not any of the radio buttons themselves:

>>> # here the radio buttons have the name "age_group"
>>> from Products.Five.testbrowser import Browser
>>> browser = Browser()
>>> browser.getControl(name='age_group').click()
>>> .... (continued)
>>> AttributeError: 'ListControl' object has no attribute 'click'

You can, however, access the individual radio buttons from the ListControl using the controls attribute on the ListControl. controls returns a list of the ListControl’s child controls, which you can access by index.

>>> # here the second radio button's value attribute is '20-25'
>>> browser.getControl(name='age_group').controls[1].selected = True
>>> browser.getControl(name='age_group').value
['20-25']

Date Constraint for XForms in Open Data Kit

Had some trouble Googling this little nugget, so I thought I might share it here.

If you want to constrain a date field in Open Data Kit by a specific date (i.e. “The date must be before 12/15/2000”), here’s the syntax:

<bind nodeset="/demographics/dob"
      type="date"
      constraint=". &lt; date('2000-12-15')"
      jr:constraintMsg="Date of Birth must be before 12/15/2000"
      />

Don’t forget the quotes inside the parentheses.

Accessing the new model instance with Django’s “post_save” signal

I’d been wracking my brain for a few hours before I finally went back to the Django signals documentation and looked up how to get to the newly created instance with Django’s ‘post_save’ signal. I’m not sure if johnboxall’s django-paypal signal example is out of date or simply erroneous, but it’s not the right way to do it. I’d expected the sender itself to be the new instance. Instead, this:

def notify_on_payment(sender, **kwargs):
  print "We got it"
  ipn_obj = sender
  print ipn_obj

post_save.connect(notify_on_payment, sender=PayPalIPN)

got me this:

We got it
<class 'paypal.standard.ipn.models.PayPalIPN'>

…plus a big fat error page in the browser.

Note: I couldn’t get the payment_was_successful signal to work at all, so I rolled my own.

The <class> bit is NOT the newly created instance, but the class itself. Django did not like me trying to access the ‘id’ of a Model class.

Long story short, the actual instance is passed as a keyword argument. Here’s how to get to it:

def notify_on_payment(sender, **kwargs):
  print "We got it"
  ipn_obj = kwargs['instance']
  print ipn_obj

Here’s the output:

We got it
<IPN: Recurring > #This is the get_display for this particular Model

Much better :). I can now access fields on that instance using dot notation.

I’d be interested in knowing whether the example on johnboxall’s page is outdated or wrong. It would seem reasonable to me for the sender to be the newly created instance to send the signal. I am, however, usually wrong about these things. Any ideas?

In GenericSetup, a Page isn’t a “Page”

Here’s a little gotcha I just ran into again recently. I’ve got a new folderish Plone content type that I’m working on, to which I’d like to be able to add plain-old Pages. At first, my profiles/default/types/NewFolderish.xml looked like this:

...
<property name="filter_content_types">True</property>
<property name="allowed_content_types">
  <element value="Link" />
  <element value="Page" /> <!-- WRONG -->
</property>
...

Well, that didn’t allow me to add a page to a New Folderish content type.  Here’s why: for GenericSetup, you have to identify a content type by it’s meta_type. For most default content types, it’s what you’d expect. For Page and Collection, it’s not. Here’s the correct profiles/default/types/NewFolderish.xml

...
<property name="filter_content_types">True</property>
<property name="allowed_content_types">
  <element value="Link" />
  <element value="Document" /> <!-- CORRECT -->
</property>
...

To allow a Page

  <element value="Document" />

To allow a Collection

  <element value="Topic" />

Happy Ploning!

Looking for Tiny RML2PDF?

One of the million and a half dependencies for Satchmo (the web store project for Django), Tiny RML2PDF seems to be the best kept secret on the web. Google it and you’ll get page after page of broken links to download the thing. So here’s one more working link to download it, the one that I finally found:

http://ftp.debian.org/debian/pool/main/p/python-trml2pdf/

Chris Moffitt also put one here: http://www.satchmoproject.com/snapshots/

A note on installation: it’s nothing fancy. Just unpack the tarball, cd into the first trml2pdf directory, and move the internal trml2pdf directory into your Python path.