Monday, December 8, 2014

Google Classroom: Top 5 Fixes I'd like to see before I sign up for a second semester

This semester, I took on a new role as director of instructional technology just as google released a new tool: google classroom. Given how thoroughly google-dependent our school already is, I thought it was a natural fit and signed myself and my students up for a semester of piloting google classroom. Here's what I've found.

Google Classroom is close to being great. When I compare the user interface of google classroom to the user interface of our school's online gradebook, it's an object lesson in UI design. Where our gradebook is byzantine, slow, and opaque, Google classroom is simple, smooth, and clear.

That said, google classroom gets details wrong, and it gets them wrong in ways that matter. More importantly, for reasons I can't quite comprehend, google seemed to stop short of making classroom a full-blown powertool on the level of its other google apps.

As it is, I'm unlikely to use classroom for another semester; I do better with my own cobbled together system using a range of scripts + docs +mail + calendar to get things done.

Here's the top 5 fixes I'd like to see before I commit to classroom for another semester:

#5. Allow collaboration everywhere. The killer feature of google docs is collaboration. Remember the days of editing a shared word doc on a server and being locked out while a colleague edits? Yeah, that was awful; google's real-time collaboration + revision history is awesome. How ironic then that google classroom makes no convenient way to assign small group projects or work with coteachers.

#4. Allow multi-step assignments. Good teachers use google classroom to help students draft, revise, collaborate, re-draft, copy-edit and, finally, publish work. When I was in grad school, I couldn't have dreamed of tools as good as google docs for teaching the writing process.

Alas, google classroom makes it easy to create an assignment with a single due date, but leaves me hanging if I want to have a multi-step assignment (brainstorm due to share with a team on one day, a draft due for feedback a week later, and a final draft due a week after that). Classroom only allows associating a single due date with an assignment and has no way to carry docs from one assignment automatically over into a new one.

It is possible to have an assignment go back and forth between teacher and student multiple times using "resubmit" and "hand back," which is what I've done, but my students complain that it's hard to keep track of due dates for revision this way, and they're right.

#3 Fix the assignment view in google drive for teachers: When you're grading a virtual stack of papers, little details matter, and google gets most of them wrong.

First, there's no easy way to sort documents by student first or last name, which is usually how they're listed in a gradebook. Second, if students attach files of their own, they don't get renamed with their name in the title. Third, if students hand in multiple files, they don't get grouped together in any way.

In my pre-google-classroom life, I had a folder for each class and each student had a single folder named last-name, first-name. This made it trivial to go down a class list and check work. Google classroom gives me nothing as easy.

If you have multi-document projects, like the one I'm grading right now, it can be a nightmare simply sorting out which document came from who (this is made worse by the ownership problem -- I can't sort the files by owner because the owner is me in all cases once the student's have handed the work in).
  
#2. Fix the broken ownership model. Google classroom has students own documents until they get turned in; then teachers own them until they hand them back. And so on. Teachers should be co-owners of the documents from the beginning.

Lacking ownership means no ability to access revision history until an assignment is turned in, which robs me of one of google docs greatest powers in the classroom.  (Think of this simple scenario: I'm about to conference with a student who has been "writing" in my class for the last 40 minutes. Before I move over to their desk to talk, I'd like to take a look at what they've produced -- under a traditional doctopus or shared-folder model with shared ownership, I can peek at revision history and see exactly what they've done with their 40 minutes of classroom; with the google classroom ownership model, I've got nothing).  

#1. Create a scripting API: the beauty of classroom is its simplicity, so I don't really expect google to get everything right. There's no way they can handle all the different ways teachers assess (rubrics, standards-based, point-based, etc. etc.), or the different features we might want to implement (group work, multi-stage work, models, and on). It's easy to see that if they do implement all these features, they'll quickly lose the elegance that is one of the main features of classroom to begin with.

The solution? Add-ons. Writing custom scripts for docs and sheets is a breeze and a boon for schools. Releasing classroom without an API for scripting stands in the way of educators who want to really get a hold of it and make it work for real schools. Google can't hope to get this right for all of us, but they can give us the power to do it ourselves. Once they do so, an ecosystem of hacker educators will spring up to make creative uses of classroom google never imagined. Until they do so, we're all left sitting on our hands, meekly hoping that google will deign to implement our next feature request.

Monday, October 6, 2014

Four Freedoms

Updating our wordpress based newspaper site, I saw that wordpress prominently includes the 4 key freedoms in their "about" page. They are:
  1. You have the freedom to run the program, for any purpose.
  2. You have access to the source code, the freedom to study how the program works, and the freedom to change it to make it do what you wish.
  3. You have the freedom to redistribute copies of the original program so you can help your neighbor.
  4. You have the freedom to distribute copies of your modified versions to others. By doing this you can give the whole community a chance to benefit from your changes.
This strikes me as a reasonable starting point for any public institution, including ours, which seeks to inculcate students with feelings of curiosity, ownership, and empowerment around computers.

It's a useful reminder that free as in freedom is different and more important than free as in free of cost. How ironic that the principle reason we don't use free software at our school is that google has made is so extraordinarily convenient to use their own free-as-in-beer closed-source solutions in schools. While key tools like chrome are open source and while Google does make API's available for many aspects of their apps suite that make tinkering with docs/sites/etc. not just do-able but often fun, I find the exercise of just re-reading the basic software freedoms to be a sobering one.

I wonder, in the face of good free-as-in-beer enterprise software from google, are there any schools out there working to give students their software freedoms? Making the switch from a Microsoft infrastructure to a Google one seems like a no-brainer (lower cost, greater functionality), but it strikes me that the ease of that switch may have made a switch to a truly open infrastructure a non-starter.

Creating digital portfolios with google sites

Our school has used google sites for some time for students to use portfolios. We have a template portfolio that we use, and then we have traditionally had students create their own portfolios based on that template. The problem is that the "creating the site" step can take a while, and, in spite of clear directions, students get mixed up and don't follow our naming system, and then for years down the road whenever a teacher wants to see a portfolio, it's hard to find.

So, this year, I automated the process of creating student portfolios. This blog post serves as notes for me and anyone else who wants to automate the process of:

(A) Creating new google sites based on a template
(B) Re-assigning ownership to someone else
(C) Setting the permissions so everyone in the domain can view the site.

Though google documents this stuff, it's not as simple or transparent as it should be, and it's a bit error prone. There's also precious little help to be googled -- hopefully this post will help improve that situation.

Google, if you're listening, I would love to see an updated google sites API soon. Among other things, you really need to start supporting your page templates better through your API -- right now there's no working way to programmatically touch content on pages that use page templates (which, in the case of our school, is *all* pages)

Let me walk you through the different scripting steps...
  1. Log into google...

    import atom.data
    import gdata.sites.client
    import gdata.sites.data
    import urlparse
    
    client = gdata.sites.client.SitesClient(source='yourCo-yourAppName-v1', domain='domain.org')
    pw = 'SECRET'
    master_user = 'USERNAME@DOMAIN.org'
    client.ClientLogin(master_user,pw,client.source)

    This gets you the client object, which is what you'll use to do nearly everything.
  2. Create a new site based on a template.

    client.CreateSite is the command to create a site. The tricky part here is finding a template to use. Google's tutorial tells you to use the parameter source_site, but only template source_sites are usable. To find the right value, it's simplest to (1) create a site using the template you want (2) find the site object for that site (3) grab the source_site attribute off of that one. In my case, the basic code to create a site looks like this:
    entry = client.CreateSite(name+' '+str(yog), source_site='https://sites.google.com/feeds/site/innovationcharter.org/sampleportfolio')
  3. Fix permissions: My last step is changing the permissions so the site is owned by the student and not me. This code has to run after the site has successfully been created; when running code creating hundreds of sites, there can be an issue with this not working properly. I ended up writing the code so that first I created all the sites, then I fixed all the permissions, which worked more reliably than creating a site then fixing permissions. I believe there may be a switch I could hand to CreateSite to fix the issue I was having with editing permissions immediately after creating the site, but I've lost track of that documentation at this point.

    At any rate, here's the way to fix the permissions up.
    First, add the user to the entry:

    def add_user_to_entry (user, entry):
       print 'Adding user to entry...',user,entry
       role = gdata.acl.data.AclRole(value='owner')
       scope = gdata.acl.data.AclScope(value=user,type='user')
       user_acl = gdata.sites.data.AclEntry(scope=scope,role=role)
       client.Post(user_acl,entry.FindAclLink())


    Second, delete ourselves from the entry (this assumes our master user is stored in the variable master_user, as above)

    def delete_master_entry (entry):
        acl = client.GetAclFeed(entry.FindAclLink())
        for e in acl.entry:
            if master_user in e.to_string():
                client.Delete(e)

    Finally, in our school, we have portfolios readable by anyone within our domain. To enforce this, we run this code:

    def make_domain_readable (entry):
        print 'Make domain-readable'
        role = gdata.acl.data.AclRole(value='reader')
        scope = gdata.acl.data.AclScope(value='innovationcharter.org',type='domain')
        domain_acl = gdata.sites.data.AclEntry(scope=scope,role=role)
        client.Post(domain_acl,entry.FindAclLink())
    
To put it all together, here is the complete script I used to read in a list of usernames from a file and create digital portfolios accordingly. The file format is simply a CSV file with Email addresses on it. I inferred the first/last name based on the address -- it would be trivial to alter the code to put whatever data you wanted in a spreadsheet in.


import re
import csv
import atom.data
import gdata.sites.client
import gdata.sites.data
import urlparse
import traceback
import csv
import time

client = gdata.sites.client.SitesClient(source='yourCo-yourAppName-v1', domain='mydomain.org')
pw = 'topsecretpassword'
master_user = 'admin@mydomain.org'
client.ClientLogin(master_user,pw,client.source)
# Convenience Functions

def delete_master_entry (entry):
    '''Remove master user from site - get rid of our ownership'''
    acl = client.GetAclFeed(entry.FindAclLink())
    for e in acl.entry:
        if master_user in e.to_string():
            client.Delete(e)
    
def add_user_to_entry (user, entry):
    '''Add user as owner of site entry'''
    role = gdata.acl.data.AclRole(value='owner')
    scope = gdata.acl.data.AclScope(value=user,type='user')
    user_acl = gdata.sites.data.AclEntry(scope=scope,role=role)
    client.Post(user_acl,entry.FindAclLink())

def make_domain_readable (entry):
    '''Make site readable by everyone in domain'''
    role = gdata.acl.data.AclRole(value='reader')
    scope = gdata.acl.data.AclScope(value='mydomain.org',type='domain')
    domain_acl = gdata.sites.data.AclEntry(scope=scope,role=role)
    client.Post(domain_acl,entry.FindAclLink())

def create_dp (email, name, yog):
    '''Create a digital portfolio based with a title based on name
     and year of graduation. 
    '''
    print 'Create DP',email,name,yog
    try:
        entry = client.CreateSite(name+' '+str(yog),
            source_site='https://sites.google.com/feeds/site/mydomain.org/sampleportfolio')
    except:
        # Print exceptions but don't raise them - this allows us to run
        # the script through even if there's a few bad data entries that
        # need correcting later.
        traceback.print_exc()
        entry = None
    if entry:
        # We postpone fixing permissions since there seems to be a bug in the API
        # that means updating permissions right away will often silently fail.
        def pull_trigger_later ():
            print 'Fixing user info for ',email,name,yog
            add_user_to_entry(email,entry)
            delete_master_entry(entry)        
            make_domain_readable(entry)
    else:
        def pull_trigger_later():
            print 'No entry was created for ',email,name,yog,'so no further action'
    # We return a function to fix permissions and the entry -- that way whoever
    # calls us can decide when to fix the permissions.
    return entry,pull_trigger_later

def print_permissions (entry):
    afeed = client.GetAclFeed(entry.FindAclLink())
    for e in afeed.entry:
        print e.to_string()
# LOOP TO READ IN DATA FROM CSV FILE, CREATE SITES, AND THEN, LATER, 
# FIX PERMISSIONS.

entries = [] # To store sites we've created
triggers = [] # Functions to fix permissions (we'll do this later)

reader = csv.reader(file('digital_portfolio_data.csv','r'))
reader.next() # Remove first line - header
for line in reader:
    # You'd update this for whatever data source you can conveniently create
    # for your own domain. This is based on our own usernames which are
    # first.last@innovationcharter.org, and it's based on the fact that I'm
    # creating a group of sites for the class of 2018 only (if I weren't, I'd
    # have added a field to the csv with the YOG).
    email = line[0]
    first = email.split('.')[0].capitalize()
    last = email.split('.')[1].split('@')[0].capitalize()
    name = ' '.join([first,last])
    e,f = create_dp(email,name,'2018')
    entries.append(e)
    triggers.append(f) # Save the permissions-fixing for later

# We're done creating sites, now let's fix permissions...
print len(triggers),'triggers to run.'
print 'Sleeping while google finishes its business' 
time.sleep(30) # Ugly ugly ugly
print "I'm alive again!"
for n,t in enumerate(triggers):
    try:
        print 'Running trigger #',n
        t()
    except:
        # Again, report errors but don't stop loop for them.
        traceback.print_exc()

Saturday, March 22, 2014

Two views of addition, two weeks apart

Now that I'm watching my kids learn skills like addition and reading which go above and beyond the obvious natural language acquisition, it's still amazing to watch the process of acquisition, and to see how suddenly things click so that what seemed impossible becomes totally natural in a matter of just weeks.

Since a colleague of mine who used to be an elementary school teacher mentioned to me that "counting on" was something that took kids a while to get, I've been paying attention. Sure enough, though Grace can happily count numbers and add them, she's spent quite a long time doing addition by putting two groups together and then counting all the items, rather than simply starting counting from one set and then up to the other. It's kind of fascinating how stubborn she is in this, and how much math she can seem to know when she still can't get the basic idea that if you have five things and you add four you only have to count up four from five.

Anyway, here's two facebook posts (the new journal? ack?) of mine documenting the shift -- because they have dates, I can actually see how little time it took to make the transition from absolutely not counting on to doing it like it's nothing.

From March 7th:

Watched Grace add 8 and 5 today to get 13. I noticed she did it all on the fingers of one hand and seemed to be counting up to thirteen. I asked her how she did it, and she explained she did 4 + 4 +5, because she somehow already knew 8 was 4 + 4.

Doesn’t it seem kind of weird that she can break that problem down that way in her head all to avoid using two hands to count (I think maybe she was holding something with the other hand?), but still can’t simply count on (i.e. start at 8 and then count 5 more up to 13)?

From March 22nd:

Grace magically started counting on today!

(in the context of a card game where this matters...) Me: Grace, what's 4 + 7
Grace: Do you mean 7 + 4?
Me: Sure -- that's the same thing.
Grace: Okay... [counting to four on one hand], 8,9,10,11... 11!
Me: Grace, who taught you to do that?
Grace: No one, I just do it.
Me: Huh.


So I can tell you with reasonable certainty that 2 weeks ago Grace couldn't count on when I tried to coach her to do it and looked at me in total confusion when I suggested it as an approach to handling numbers that didn't fit on her fingers. Now she's doing it like it's the most obvious thing in the world, with no awareness of having learned it.

The question, as ever: did she "learn" this from the inadvertent teaching I've been doing as I've been trying to understand how she does math in her head, or did something in her development just click today so she was ready to do it now?

I realize the answer is almost certainly a little of both, but I certainly lean toward the development side of this question. It amazes me how many skills seem absolutely unlearnable until some magical threshold is passed and they're learned seemingly effortlessly...