Monday, November 08, 2010

Grading with Google Docs

I like having students submit their work electronically, rather than dealing with reams of paper. But I don't so much like dealing with weird file formats (e.g. Microsoft's docx, or Apple's extensionless documents), or the bother of downloading and uploading attachments one at a time. Fortunately, I recently came across the perfect technical solution (thanks to Andrew Cullison and Cédric Eyssette): use Google Docs with python scripts to automate the electronic transactions. In this post I'll explain the process step-by-step. It takes a little bit of set-up, but saves a heap of tedium down the line.

Overview: The general strategy is to generate a new google document for each student in your class (shared with this individual only), where they can write their essay -- or paste it in from another word processor if they prefer. After the assignment deadline, you set it so that students can no longer access their documents while you're grading them and adding comments. Then, when you're ready, each student is notified that they can view their graded document.

Note: You will need to download and install Python 2.7 and the Google Data API Python Client Library to run the subsequent python scripts. (If you use Ubuntu, python should already be installed by default. You can check by running 'python -V' in a terminal window.)

Step 0: Tell students to sign up for a (free) Google Account, if they haven't already, so they can use google docs.

Step 1: create a spreadsheet of student names and Google Account emails. The easiest way is probably to create a google docs form for your students to fill out this info, and their answers will be automatically collated into a google spreadsheet for you.

Step 2: create a google doc for each student. Once you set up the script as described below, you'll be able to implement this step by simply running the 'CreateDocs.py' script.

[E.g., in Ubuntu: open a terminal windows, navigate to the directory where the scripts are saved, and type 'python CreateDocs.py' (without the quotes). In Windows: Shift-RightClick the folder containing the scripts, and select "Open a command window here", then type 'python CreateDocs.py', or perhaps something like 'C:\Python27\python.exe CreateDocs.py']

Step 3: remove student permissions while you grade. Once you set up the script as described below, you'll be able to implement this step by running 'RemovePermits.py'.

Step 4: let students see their graded docs. Once you set up the script as described below, you'll be able to implement this step by running 'ReturnDocs.py'.

Doesn't that look easy? Okay, now for the scripts (with thanks to Cédric for permission to share his code -- I've just made some minor edits)...

How to set up the python scripts:

All three scripts start the same way. So open a plain text editor (e.g. notepad or gedit), and paste in the following code:

import gdata.docs
import gdata.docs.service
import gdata.spreadsheet.service
import re, os
import gdata.docs.client
import gdata.acl.data

# DATA
username='YOUR_GOOGLE_EMAIL'
password='YOUR_GOOGLE_PASSWORD'
students_data='GOOGLE_SPREADSHEET_TITLE' # format : "timestamp / firstname / lastname / email"
classcode='PHI_101'
assignment='ESSAY_1'

# Connect to Google-Spreadsheet
gd_client = gdata.spreadsheet.service.SpreadsheetsService()
gd_client.email = username
gd_client.password = password
gd_client.source = 'OpenSource-CreateDocs-v1'
gd_client.ProgrammaticLogin()

# Connect to Google-DocList
client = gdata.docs.client.DocsClient(source='OpenSource-CreateDocs-v1')
client.ssl = True
client.http_client.debug = False
client = gdata.docs.client.DocsClient(source='OpenSource-CreateDocs-v1')
client.ClientLogin(username, password, client.source);

#
q = gdata.spreadsheet.service.DocumentQuery()
q['title'] = students_data
q['title-exact'] = 'true'
feed = gd_client.GetSpreadsheetsFeed(query=q)
spreadsheet_id = feed.entry[0].id.text.rsplit('/',1)[1]
feed = gd_client.GetWorksheetsFeed(spreadsheet_id)
worksheet_id = feed.entry[0].id.text.rsplit('/',1)[1]
rows = gd_client.GetListFeed(spreadsheet_id, worksheet_id).entry
for row in rows:
 firstname=row.custom['firstname'].text
 lastname=row.custom['lastname'].text
 email=row.custom['email'].text
 title_doc=classcode+'-'+assignment+'-'+lastname+'-'+firstname
 print title_doc
Edit the 'DATA' section: enter your google email and password, the name of the spreadsheet with your students' data, and make sure that the described 'format' corresponds to the order of the columns in the spreadsheet (if not, correct the code so that they match!). You can play around with 'title_doc' if you don't want the document titles to be in the format 'PHI101-Essay1-Doe-Jane'.

Save this file. It'll serve as the base template for the three scripts.

(1) Append the following code (taking care to maintain the same indents as before), and save the resulting file as 'CreateDocs.py':
 # CREATE A NEW DOCUMENT FOR EACH STUDENT
 new_doc = gdata.docs.data.Resource(type='document', title=title_doc)
 new_doc = client.CreateResource(new_doc)
 print 'Created:', new_doc.title.text
 scope = gdata.acl.data.AclScope(value=email, type='user')
 role = gdata.acl.data.AclRole(value='writer')
 acl_entry = gdata.docs.data.AclEntry(scope=scope, role=role)
 client.AddAclEntry(new_doc, acl_entry, send_notification=True)

(2) Load your base file, and this time append the following code and save it as 'RemovePermits.py':
 feeduri='/feeds/default/private/full?title='+title_doc+'&title-exact=true&max-results=5'
 feed2 = client.GetResources(uri=feeduri)
 if not feed2.entry:
  print 'No document of that title.\n'
 doc_id=feed2.entry[0]
 acl_feed = client.GetAcl(doc_id)
 # REMOVE FIRST GRANTED PERMISSIONS FROM THE DOCUMENTS
 acl_entry = acl_feed.entry[1]
 client.DeleteAclEntry(acl_entry)

(3) Load your base file, and this time append the following code and save it as 'ReturnDocs.py':
 feeduri='/feeds/default/private/full?title='+title_doc+'&title-exact=true&max-results=5'
 feed2 = client.GetResources(uri=feeduri)
 if not feed2.entry:
  print 'No document of that title.\n'
 doc_id=feed2.entry[0]
 acl_feed = client.GetAcl(doc_id)
 scope = gdata.acl.data.AclScope(value=email, type='user')
 role = gdata.acl.data.AclRole(value='reader')
 acl_entry = gdata.docs.data.AclEntry(scope=scope, role=role)
 new_acl = client.AddAclEntry(doc_id, acl_entry)
Feel free to play around with the code, and let me know if you come up with any improvements!

N.B. Code updated as of 2013, to work with the latest gdata-python-client library (version 2.0.17).

0 comments:

Post a Comment

Visitors: check my comments policy first.
Non-Blogger users: If the comment form isn't working for you, email me your comment and I can post it on your behalf. (If your comment is too long, first try breaking it into two parts.)