Skip to main content

Using Google Sites For Student Work ( Philosophy )

The Idea

Prof +Tom Stoneham and Nick Jones had the idea of using Google Sites as an alternative to textual documents for student work, in this case, a dissertation about a certain philosopher.

Google Sites give the opportunity for the creation of a network of information rather than a narrative document. A site can hold videos, audio and refer to other online resources with links.

The idea was that there would be a simple template site (see above), with boiler plate text and guidance about copyright issues etc. and the student could then start editing existing pages and creating new ones.

Administration

From an administrational perspective, the Google sites would need to be closed to student when the deadline was met. Ideally, it would good if the student could have a copy of their Google Site - both to continue working on it and to use in their portfolio of work.

Whilst Tom didn't need the student's identity to be anonymized, but we used a unique reference number for the name of the site anyway.

Technical

From a technical perspective, creating a 80 Google Sites would seem to be a simple task. It would be, were it not for the fact that often, when creating resources like Google Sites or Calendars or Groups there can be a variable lag from asking Google to create the resource and it being available for further use. And whilst there is a lag, the Apps Script code you write to create a Google site is asynchronous, which effectively means you do the coding equivalent of ...

var basket = new Basket()
basket.addEggs( 12 ) // At this point there probably is NO BASKET! Error!

The "solution" I hit upon was using a once-a-minute Trigger to pick off the tasks I wanted to run one at time, meaning that after each one Google's system have more than enough time to "catch up". The tasks being...

  • create a Google Site from a template
  • add the Student as Editor
  • When the deadline is reached, add the examiner to the site as Viewer and remove the student as Editor
  • Email the examiner that they have a site to mark
  • create a copy of the student's Google Site and make them Owner for their portfolio.
I created a spreadsheet with three sheets ( to the wind ). The first, Administration is where you set up the name of your project, which Google Site to use as your template and when the deadline is. The second, Students is a list of student emails, unique references and examiner emails. And the last is a utility sheet that gets a list of your Google Sites ( for interface niceness ).

I then created a huge function to gently create the Sites and add the right permissions to the right people at the right time. It looks like this.





function triggered_site_maker( ){
 
  var ss = SpreadsheetApp.getActiveSpreadsheet( );
  var sheet = ss.getSheetByName( "Students" );
 
  // Get default values from the Administration sheet
  var admin_sheet =  ss.getSheetByName("Administration");
  var template_site_name =  admin_sheet.getRange("B1").getValues()
  var domain =  admin_sheet.getRange("B4").getValue( )
  var homework_id =  admin_sheet.getRange("B5").getValues( )
  var homework_title =  admin_sheet.getRange("B6").getValues( )
 
  // get all the data in the Students sheet
  var range = sheet.getDataRange().getValues();
  var students = rangeToObjects(range);
 
  for(var i = 0; i < students.length; i++){  
    var student = students[i];
   
    var student_unique_ref = student.uniquereference.toString().toLowerCase()
    var new_site_name = homework_id + "-" + student_unique_ref  
   
    var student_email = student.email
    var examiner = student.examiner
    var siteurl = student.siteurl
    var status = student.status
   
    var statuscellname = "E" + (i +2 )
    var timestampcellname = "F" + ( i +2 )
    var timestamp =new Date()
    var cellname = "D" + ( i +2 ) //
   
    if ( student_unique_ref != ''){
     
      switch (status){
        case '': // We're at the beginning, make a site for the student.
         
          try{
            // Note: sites_url is a NEW sites url not an already made one.
            var sites_url = create_a_site(template_site_name, new_site_name )
           
            sheet.getRange(cellname).setValue(sites_url)
            sheet.getRange(statuscellname).setValue('site made')
            sheet.getRange( timestampcellname ).setValue( timestamp )
            break
           
          }catch(e){
            ErrorMail(e)
          }
         
        case 'site made': // The site has been made, now add the student.
         
          try{
            var name = SiteUrlToName(siteurl) // workout site's name from URL. Had problems with getSiteByUrl()
           
            if (domain == ''){
              var site = SitesApp.getSite( name)
              }else{
                var site = SitesApp.getSite(domain, name)
             }
            site.addEditor(student_email);
            sheet.getRange(statuscellname).setValue('site made and student added')
         
            site.setTitle( homework_title ) // Handy for searching later I guess. Gulp.
            sheet.getRange( timestampcellname ).setValue( timestamp )
           
          }catch (e) {
            ErrorMail(e)
          }
          break
         
          // NOTIFY EXAMINER/REMOVE STUDENT AND COPY SITE  
          case  'site made and student added':
         
          var deadline =  admin_sheet.getRange("B2").getValue( )
          var now = new Date()
          if (now > deadline){
            try{
              var name = SiteUrlToName(siteurl) // workout site's name from URL
              if (domain == ''){
                var site = SitesApp.getSite( name)
                }else{
                  var site = SitesApp.getSite(domain, name)
                  }
              site.removeEditor( student_email )
              sheet.getRange(statuscellname).setValue('student removed')
              sheet.getRange( timestampcellname ).setValue( timestamp )
             
            }catch(e){
              ErrorMail(e)
            }
          }
          break
         
          case  'student removed': //Now, add the examiner
         
          try{
            var name = SiteUrlToName(siteurl) // workout site's name from URL
            if (domain == ''){
              var site = SitesApp.getSite( name)
              }else{
                var site = SitesApp.getSite(domain, name)
                }
            var site_name = site.getName()
            Logger.log( site_name)
            var viewers = site.getViewers( )
            if ( examiner != ''){
             
              site.addViewer( examiner )
              sheet.getRange(statuscellname).setValue('examiner added')
              sheet.getRange( timestampcellname ).setValue( timestamp )
            }
            break
           
          }catch(e){
            ErrorMail(e)
          }
         
        case  'examiner added': //Email them with a link to the site
          try{
            MailApp.sendEmail(examiner,
                              "A Google Site To Mark",
                              "As an examiner, a Google Site called '" + homework_title +"' has been created for you to mark. \n\n " +
                              "Unique student reference: " + student_unique_ref + "\n\n " +
                              "url: " + siteurl + "\n\n",                  
                              {name:"Google Site Marking", noReply:true});
            sheet.getRange(statuscellname).setValue('examiner emailed')
            sheet.getRange( timestampcellname ).setValue( timestamp )
            break
          }catch(e){
            ErrorMail(e)
          }
         
        case  'examiner emailed': //Make the student a copy for their portfolio.
          try{
           
            var name = SiteUrlToName(siteurl) // workout site'ss name from URL
            if (domain == ''){
              var site = SitesApp.getSite( name)
              }else{
                var site = SitesApp.getSite(domain, name)
                }
            var new_name = "" + name + "-copy"
            var title = site.getTitle()
            var summary = site.getSummary()
            var site_name = site.getName( ) // use this site as a template
           
            if (domain == ''){
              var copied_site = SitesApp.copySite(  new_name , title, summary, name)
              }else{
                var copied_site = SitesApp.copySite( domain, new_name , title, summary, name)
                }
           
            Utilities.sleep(4000)
            var copied_site_url = copied_site.getUrl()
            sheet.getRange( "I" + (i + 2) ).setValue(  )
            sheet.getRange(statuscellname).setValue('student copy created')
            sheet.getRange( timestampcellname ).setValue( timestamp )
            break
           
          }catch(e){
            ErrorMail(e)
          }
         
         
        case  'student copy created': // Add them as owners to these copies.
          try{
            var copied_url = sheet.getRange( "I" + (i + 2) ).getValue()
           
            var name = SiteUrlToName(siteurl)
            if (domain == ''){
              var site = SitesApp.getSite( name)
              }else{
                var site = SitesApp.getSite(domain, name)
                }
            site.addOwner(student_email)
            sheet.getRange(statuscellname).setValue('student added to copy')
            sheet.getRange( timestampcellname ).setValue( timestamp )
            break
           
          }catch(e){
            ErrorMail(e)
          }
        case  'student added to copy':
         
          sheet.getRange(statuscellname).setValue('Completed')
          sheet.getRange( timestampcellname ).setValue( timestamp )
          break
      }//end case
     
     
    }//end if empty student_unique_ref -- 
  }//end forloop
 
  // It would be good at this point if we could then de-activate the trigger....
 
}

function test_trigger(){
  triggered_site_maker()
}





function create_a_site( template_site_name, new_site_name ){

  var ss = SpreadsheetApp.getActiveSpreadsheet( );
  var admin_sheet =  ss.getSheetByName("Administration");
  var domain =  admin_sheet.getRange("B4").getValues( )
 
  if ( domain == ''){
    var template_site = SitesApp.getSite( template_site_name )
    }else{
      var template_site = SitesApp.getSite(domain, template_site_name );
    }
 
  var title = template_site.getTitle()
  var summary = template_site.getSummary()
 
  // See the warning in https://developers.google.com/apps-script/class_sitesapp
  if ( domain == ''){
    var site = SitesApp.copySite(  new_site_name, title, summary, template_site);
  }else{
    var site = SitesApp.copySite( domain, new_site_name, title, summary, template_site);
  }
  Utilities.sleep( 3000 ) // Yawn! 
  var sites_url = site.getUrl( );
  return sites_url ;
}





function my_sites_names(){   /////  GET A LIST OF SITES YOU'VE MADE 
  // This is used in the interface as a data validation thing.
  var ss = SpreadsheetApp.getActiveSpreadsheet( );
  var admin_sheet =  ss.getSheetByName("Administration");
  var my_sites_sheet =  ss.getSheetByName("My Sites");
 
  // clear the ole data...
  var last_row = my_sites_sheet.getDataRange().getLastRow()
  var range = my_sites_sheet.getRange(2, 2, last_row)
  range.clear()
 
  var domain =  admin_sheet.getRange("B4").getValue( )
 
  if (domain == ''){
    var sites = SitesApp.getSites()
    }else{
      var sites = SitesApp.getSites(domain)
      }
 
  var site_names = new Array();
  for(var i = 0; i < sites.length; i++){
    var site = sites[i]
    var site_name = site.getName()
    //set name
    var range = my_sites_sheet.getRange(i+2, 1)
    range.setValue( site_name )
    Logger.log( site_name )
   
    //set URL
    var range = my_sites_sheet.getRange(i+2, 3)
    range.setValue( site.getUrl() )
   
    site_names.push(  site.getName() )
  }
  return site_names
}



Take a Copy And Try It Yourself

Note: This will only work with Apps for Education/Business accounts ( and won't work for consumer accounts because you can't create a Google Site for random people willy nilly ).


1. File > Make a copy of the spreadsheet here.

2. Go to the Script Editor and Run Event Handling > onOpen() . This authenticates the Script to ask which are your Google Sites. This is just for interface niceness.

3. Fill in the Administration sheet. This needs a domain and a deadline. There's also a "project ID" which can be anything, but is used to differentiate different "assignments".

4. Add students, unique references and examiners to the Students sheet ( there is some Utility code to help generate them if you don't have unique IDs ).

5. You might want to add your email address to the Utility > ErrorEmail function. When a Script is running from a Trigger I don't think you get access to any Logger.log() messages, so this is a handy way of debugging the app.

6. You are now ready to run the triggered_site_maker() function. You can run it straight from the ScriptEditor, but you will need to add it as a Trigger for it to do all it needs to do. Like this...




Once running, you should see various values being populated as your Google Sites get created and permissions added. When the deadline is met, the students are removed as Editors and given a copy of their site. Once this is finished you can delete the Trigger.









Comments

Popular posts from this blog

Inserting A Google Doc link into a Google Spreadsheet

This article looks at using Apps Script to add new features to a Google Spreadsheet.

At the University of York, various people have been using Google spreadsheets to collect together various project related information. We've found that when collecting lots of different collaborative information from lots of different people that a spreadsheet can work much better than a regular Google Form.

Spreadsheets can be better than Forms for data collection because:

The spreadsheet data saves as you are editing.If you want to fill in half the data and come back later, your data will still be there.The data in a spreadsheet is versioned, so you can see who added what and when and undo it if necessaryThe commenting features are brilliant - especially the "Resolve" button in comments.
One feature we needed was to be able to "attach" Google Docs to certain cells in a spreadsheet. It's easy to just paste in a URL into a spreadsheet cell, but they can often all look too si…

Writing a Simple QR Code Stock Control Spreadsheet

At Theatre, Film & TV they have lots of equipment they loan to students, cameras, microphone, tripod etc. Keeping track of what goes out and what comes back is a difficult job. I have seen a few other departments struggling with the similar "equipment inventory" problems.

A solution I have prototyped uses QR codes, a Google Spreadsheet and a small web application written in Apps Script. The idea is, that each piece of equipment ( or maybe collection of items ) has a QR code on it. Using a standard and free smartphone application to read QR codes, the technician swipes the item and is shown a screen that lets them either check the item out or return it.

The QR app looks like this.



The spreadsheet contains a list of cameras. It has links to images and uses Google Visualisation tools to generate its QR codes. The spreadsheet looks like this.


The Web Application The web application, which only checks items in or out and should be used on a phone in conjunction with a QR cod…

One-To-Many Relationship in a Google Spreadsheet

It's often the case that you want and need to be creating a database to store your data, but Google Spreadsheets are just so handy aren't they? But Google Spreadsheets are very good at relational data.

Here's an example where, you want to have one column for the name of your recipe and another for the ingredients ( comma separated ).

How you use this script is you click on the cell you want to be relational and choose the Admin > Show Relationship Editor. This opens up a dialog window showing you all the options included so far. You then alter the ingredients and it saves a comma separated list into the spreadsheet.







Here's the spreadsheet. Use File > Make a copy to see it work and rummage around in the code.

If anyone can help make the UI prettier I'd be grateful, thanks.