Math Camp Journals: How it Worked

I’ve been meaning to write a bit about how the math camp journal system eventually worked, so I suppose I’ll do so now!  (You can see the finished product here, here, here, and
here.)  I tried a couple things, but mainly relied on ImageMagick and the Python xmlrpc library written by Max Cutler.  In fact, a very nice moment mid-week involved shooting emails back and forth at 3am (Kenya time) with Max trying to figure out what was going wrong on my end.  (You’re not really developing until you’re talking to the people who wrote your libraries, I always say!)  In any case, Max was very helpful, as was his library.

So.  The system ran as follows:

  1. The students wrote journal entries describing their math camp experience each day.
  2. At morning assembly, one of the coordinators, usually Jeff, would photograph all of the journal entries that were handed in.
  3. I would then run an ImageMagick script, which would automatically crop, resize and adjust the color levels on the pictures of the journal entries.
  4. We would rename each file (like IMG-000412.JPG) to include the student’s name (like Tom_Denton.jpg), with underscores between the parts of the name.  Also, I would _try_ to rotate each image to upright, but my image-viewing programs seem to handle rotations in funny ways.  (Thus, some of the journal entries are sideways on the blog.  Urg,)  More on that below.
  5. A short python script would then do the rest of the work.  Each picture was uploaded automatically (using xmlrpc) to the WordPress site, and the script would then generate a bunch of html to form the body of the post.

I was blown away by the number of options available to ImageMagick.  It’s a really amazing program.  For what it’s worth, the bash script that did all the work on the images looked like this:

for i in *.JPG; do convert $i -resize 25% -auto-level -fuzz 50% \
-trim +repage -quality 30 cropped/$i; done

Thus, for each file in the directory, it would rescale the image to 25% of its original size and adjust color levels.  The fuzz, trim, and repage all relate to auto-cropping.  We would photograph the journals on a dark-ish table; the ‘trim’ command finds the color of the corners, then cuts as much off the sides as possible matching that color.  The ‘fuzz’ command says that the program will, instead of matching exactly the corner colors, cut off as much as possible that’s “close” to the corner colors.  Repage tidies things up slightly, and quality saves the output as a fairly low-quality jpg.  The low quality means much lower file size, while the readability of the journal entries is maintained.

The problem with rotation seems to be that the JPG files keep all of the Exif data from the camera, which describes the original dimensions of the picture, amongst other things.  So after scaling and rotating, some programs would use the Exif data and conclude that the image was rotated ‘wrong’ and correct accordingly, by unrotating the image.  I was never sure which programs were doing things right or wrong, and was too busy to track it down exactly, so some of the images on the site ended up sideways.  Oh well.

Finally, the xmlrpc library was hella useful, though it took me a while to get it up and running properly.  Xmlrpc is a protocol for making things talk to each other.  WordPress supports (to some extent) writing posts and uploading things using this protocol, which is quite nice.  It means that people can write (for example) Android apps to upload content to their WordPress site.  The Python xmlrpc library thus lets one write Python code to upload content (or write a whole python-based app to do so, if one is so motivated).  So that’s what I did.  The examples on the python xmlrpc site were very useful, but I ran into some rocky places along the way.  An upgrade of the WordPress site was needed (since some of the xmlrpc support is very recent).  I also had some issues with the code that was meant to write the actual post; Max thinks it’s due to some badly placed whitespace somewhere in the template I’m using.  As a result, I just had the script output html, which I then copy-pasted into the blog post manually.  (It wasn’t too much work, really, since I decided to go with one-big-post-per-day instead of one post per journal entry.)

Going forward, it’s easy to imagine some kind of app to do most of the work.  It would be great for uploading student work generally; take a photo, maybe have a crop tool, and then auto-upload to a predefined blog.  Could probably also include a drop-down of categories from the blog, to make it easy to specify which student submitted the work, for example.

Here’s the code, which obviously comes with no guarantees.  I know that it runs on at least one setup (my computer plus my WordPress site), and have done exactly zero further testing.

import string
from subprocess import *
from wordpress_xmlrpc import Client, WordPressPost, WordPressPage
from wordpress_xmlrpc.methods.posts import GetPosts, NewPost
from wordpress_xmlrpc.methods.users import GetUserInfo
from wordpress_xmlrpc.methods.media import UploadFile
from wordpress_xmlrpc.methods import posts
from wordpress_xmlrpc.compat import xmlrpc_client
import wordpresslib

#Run in the journal directory!

journal_entry_number=4
site_url='http://blogs.africanmathsinitiative.net/masenomathscamp/xmlrpc.php'
user='kaibutsu'
passwd=''

#Get a list of file names.
localFiles=string.split(check_output("ls"), 'n')
if localFiles[-1]=="": localFiles.pop()
#Keep only jpg's.
pictureFiles=[]
for f in localFiles:
    if f[-4:]=='.jpg' or f[-4:]=='.JPG':
        pictureFiles.append(f)

print "Found the following jpg's: "
print pictureFiles, 'n'

wp = Client(site_url, user, passwd)

obj=[]
for f in pictureFiles:
    #Get the student name from the file name.
    g=string.split(f[:-4], '_')
    name=''
    for w in g: name = name+w+' '
    name=name[:-1]

    #Prep the picture for uploading.
    data={'name':f, 'type':'image/jpg'}
    print 'Uploading: ' + f
    with open(f, 'rb') as img:
        data['bits'] = xmlrpc_client.Binary(img.read())
    #and the actual picture upload:
    response = wp.call(UploadFile(data))
    # response == {
    #       'id': 6,
    #       'file': 'picture.jpg'
    #       'url': 'http://www.example.com/wp-content/uploads/2012/04/16/picture.jpg',
    #       'type': 'image/jpg',
    # }
    print [name, response['url']]
    obj.append([name, response['url']])

post = WordPressPost()
post.post_status = 'publish'
post.title = 'MMC2012 Journal Entries Part ' + str(journal_entry_number)
post.description = ""
for o in obj:
    post.description += "<h3>"+o[0]+ "</h3>n<p><a href="" + o[1] + ""><img src="" + o[1] + ""></a></p>nn"
post.terms_names = {
   'post_tag': ['mmc2012', 'maseno maths camp', 'journals' ],
   'category': ['mmc2012 student journals']
}
post.post_status = 'publish'
print post
print post.description

#And here's the post.
post.id = wp.call(posts.NewPost(post))
print post.id

Ok, that’s probably enough for now…

Advertisements