Monthly Archive for March, 2010

What I’ve starred this week: March 30, 2010

Here's some posts which have caught my attention this week:

Automatically generated from my Google Reader Shared Items.

Using Google Apps Script for a event booking system (Spreadsheet to Calendar & Site | Form to Spreadsheet, Email and possible Contacts)

Update: New version is posted here The best Google Spreadsheet Event Manager (w/h Sites, Contact, Calendar integration) ever, ever, ever

A while ago I was looking at Updating a Google Calendar and Google Site from a Google Spreadsheet (the beginnings of an event booking system). This idea was inspired by Tony Hirst’s work on updating a google calendar from a spreadsheet the endpoint being a way to manage a simple event booking system using Google Apps. This all started to unravel as I couldn’t find a way to create a custom booking form for each event. Tony suggested that I should look at just using a generic form which was manually updated with new events, the system handling the rest of the booking process. So with that little hurdle out of the way I revisted my script and as well as rewritting most of it I took the concecpt a little further.

So what do we have now? The video below walks through the workflow:

[You might want to enable full screen view to see what is happening]

The core code is at the end of this post and you can access the full spreadsheet and script here (if you want to copy this spreadsheet to your Google Apps domain account you’ll need to edit the link to <- thanks @Eion). Once you open it click File –> Make a copy to allow you to edit. The bulk of this code is actually a reworking of some existing Google Apps Script tutorials:

To use this code yourself some variables need defining (I could have just called these from a sheet but ran out of time). To do this click Tools –>  Scripts –> Script editor… and you’ll see the variables at the top. The first time you run the script a security dialog will popup. You will need to ‘grant access’ for it to work.


Once you do this here are some instructions for use (in general yellow fields are for user input):

  1. In the ‘Events’ sheet enter title, description, dates etc. You can enter as many events as you like.
  2. To make an event public enter the text ‘Add’ in the action column, then click Booking System –> Process events. This will push it to calendar and site and create a unique sheet for the event.
  3. To allow bookings click on Form –> Edit form and add the event to the drop down using the format ‘ID#{theEventNumberUsedOnTheSheet} - {theNameOfYourEvent}’ – !this format including whitespaces is really important
  4. A limitation of the Google Apps Script is it doesn’t yet handle onFormSubmit actions (Google are looking to add this), so for now to process bookings you need to click Booking System –> Process Bookings. This sends an email to your admin to notify them that there is a booking
  5. To approve a booking enter ‘Y’ in the Action column and again click Booking System –> Process Bookings. (You can approve as many booking as you like in one go). The script will then send a confirmation to the delegate and copy their details to the appropriate event sheet.
  6. When you are ready to send joining instructions you can edit the message in the EmailTemplates sheet (you can also edit the format of the other emails used). When you are ready to send go to the correct Event sheet then click Booking System –> Email joining instructions

Limitations/waiting for Google to fix

Deleting events from calendar and sites is still a manual process (I don’t recall anywhere in the API which allows you to do this). You will also see in the code I’ve commented out a section which would add all delegates to Google Contacts.

The code

// AppEventManger Script
// by mhawksey at
// User defined variables
var BOOKING_FORM_URL = ""; //your booking form url (I'd mine to shorten
var SITE_DOMAIN = "Your domain"; // your apps domain name
var SITE_NAME = "Name of your site"; // your apps site name
var CALENDAR_EVENTS = "Name of your calendar"; // the name of the calendar to update
var STATE_MANAGER_EMAIL = "[email protected]"; //email address for booking notifications
// some additional variables to change at your peril
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [ {name: "Process Events", functionName: "processEvents"}, {name: "Process Bookings", functionName: "onFormSubmit"}, {name: "Email joining instructions", functionName: "sendEmails"} ];
ss.addMenu("Booking System", menuEntries);
function processEvents() {
//declare vars
var cal = CalendarApp.openByName(CALENDAR_EVENTS);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheet = ss.getSheetByName("Events"); // ref sheet name (thought I might have ended up with multiple sheets)
var data = getRowsData(dataSheet);
var cal = CalendarApp.getDefaultCalendar();
var site = SitesApp.getSite(SITE_DOMAIN, SITE_NAME); // .getSite(domain, sitename)
var annPage = site.getAnnouncementsPages();
// pull data
for (var i = 0; i < data.length; ++i) {
var row = data[i];
row.rowNumber = i + 2;
if (row.action =="Add"){
var descText = row.description + "More info: " + BOOKING_FORM_URL;
cal.createEvent(row.title, row.start, row.stop, {location:row.location, description:descText}); // create calendar event
var message = "<strong>Start:</strong> " + Utilities.formatDate(row.start, "GMT", "dd/MM/yy HH:mm")
+ "<br/><strong>Finish:</strong> " + Utilities.formatDate(row.stop, "GMT", "dd/MM/yy HH:mm")
+ "<br/><strong>Location:</strong> " + row.location
+ "<br/><strong>Description:</strong> " + row.description
+ "<br/><a href='" + BOOKING_FORM_URL + "'>Click here to book a place</a>"; // prepare message
site.createAnnouncement(row.title, message, annPage[0]); // add announcement to site
var annList = site.getAnnouncements();
var eventID = annList.length; // get announcement ID
var eventSheetName = "Event#" + eventID;
ContactsApp.createContactGroup(eventSheetName); //create a contact group for the event
dataSheet.getRange(row.rowNumber, 2, 1, 1).setValue(new Date()); // add today's date/time to 'Added Date' column
// create a new event booking sheet from template
var nuSheet = ss.duplicateActiveSheet();
ss.renameActiveSheet(eventSheetName); // rename using event Id
// insert data into sheet
var eventSheet = ss.getSheetByName(eventSheetName);
eventSheet.getRange(1, 2, 1, 1).setValue(row.numberOfPlaces);
eventSheet.getRange(1, 3, 1, 1).setValue(row.title);
eventSheet.getRange(2, 3, 1, 1).setValue(row.location);
eventSheet.getRange(3, 6, 1, 1).setValue(row.start);
eventSheet.getRange(3, 8, 1, 1).setValue(row.stop);
ss.setActiveSheet(ss.getSheetByName("Events")); // switch back to events sheet
dataSheet.getRange(row.rowNumber, 3, 1, 1).setValue(eventSheetName);
dataSheet.getRange(row.rowNumber, 1, 1, 1).setValue("Added by "+Session.getUser().getUserLoginId()); //set the fact that we have updated the calendars for this event
Browser.msgBox(row.title + " has been published to the calendar and website. IMPORTANT: Add this event to the Form to allow delegates to book in.");
function onFormSubmit() {
// This function has been designed for when App Scripts automatically runs when a Form is submitted.
// For now it has to be manually started. See
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheet = ss.getSheetByName("Bookings");
var templateSheet = ss.getSheetByName("EmailTemplates");
var emailTemplate = templateSheet.getRange("A7").getValue();
// Create one JavaScript object per row of data.
data = getRowsData(dataSheet);
for (var i = 0; i < data.length; ++i) {
// Get a row object
var row = data[i];
row.rowNumber = i + 2;
if (!row.state) { // if no state notify admin of booking
var emailTemplate = templateSheet.getRange("A7").getValue();
var emailText = fillInTemplateFromObject(emailTemplate, row);
var emailSubject = "Booking Approval Request ID: "+ row.rowNumber;
MailApp.sendEmail(STATE_MANAGER_EMAIL, emailSubject, emailText);
dataSheet.getRange(row.rowNumber, COLUMN_STATE).setValue("TBC");
} else if (row.action == "Y") { // if admin have approved send confirmation
var approvedOrRejected = (row.action == "Y") ? "confirmed" : "rejected";
// capture the sheet to copy booking to
var eventID = row.event;
eventID = eventID.substring(eventID.indexOf("#")+1,eventID.indexOf(" -"));
var eventSheet = ss.getSheetByName("Event#" + eventID);
// create a booking ID
var bookingID = "ID#"+eventID+"B"+row.rowNumber;
dataSheet.getRange(row.rowNumber, COLUMN_BOOKING_ID).setValue(bookingID);
// copy booking details to event sheet
var rowNum = eventSheet.getLastRow()+1;
eventSheet.getRange(rowNum, 3, 1, 1).setValue(bookingID);
eventSheet.getRange(rowNum, 4, 1, 1).setValue(row.timestamp);
eventSheet.getRange(rowNum, 5, 1, 1).setValue(row.firstName);
eventSheet.getRange(rowNum, 6, 1, 1).setValue(row.surname);
eventSheet.getRange(rowNum, 7, 1, 1).setValue(;
eventSheet.getRange(rowNum, 8, 1, 1).setValue(row.organisation);
eventSheet.getRange(rowNum, 9, 1, 1).setValue(row.otherInfo);
eventSheet.getRange(rowNum, 10, 1, 1).setValue(row.comments);
//Add/edit details of new/existing delegate to Google Contacts
// This code generates a 'Service error: Contacts : POST method does not support
// concurrency' looks like an API bug.
var curDate = Utilities.formatDate(new Date(), "GMT", "dd/MM/yy HH:mm");
var c = ContactsApp.findByEmailAddress(;
if (!c){
var c = ContactsApp.createContact(row.firstName, row.surname,;
var prop = {};
prop.Organisation = row.organisation;
prop.Added = curDate;
var group = ContactsApp.findContactGroup(row.organisation);
if (!group){
var group = ContactsApp.createContactGroup(row.organisation);
} else {
c.setUserDefinedField("Last activity", curDate);
//var group = ContactsApp.findContactGroup("EventID#"+eventID);
//c.addToGroup(group); // add contact to event group
//prepare email
var emailTemplate = templateSheet.getRange("A4").getValue();
var emailText = fillInTemplateFromObject(emailTemplate, row);
var emailSubject = "Booking Approved (Booking ID: "+ bookingID + ")";
// fill in the template using stored variables
emailText = emailText.replace("STATE_MANAGER_EMAIL", STATE_MANAGER_EMAIL || "");
emailText = emailText.replace("APPROVED_OR_REJECTED", approvedOrRejected || "");
emailText = emailText.replace("BOOKING_ID", bookingID || "");
MailApp.sendEmail(, emailSubject, emailText);
// Update the state of bookings
dataSheet.getRange(row.rowNumber, COLUMN_STATE).setValue(STATE_APPROVED+" by "+Session.getUser().getUserLoginId());
dataSheet.getRange(row.rowNumber, COLUMN_ACTION).setValue("");
// Code to send joining instructions - based on simple mail merge code from
// Tutorial: Simple Mail Merge
// Hugo Fierro, Google Spreadsheet Scripts Team
// March 2009
function sendEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheet = ss.getActiveSheet();
var eventName = ss.getRange("C1").getValue();// pull event name from sheet
var dataRange = dataSheet.getRange(5, 3, dataSheet.getLastRow() - 3, 8);
var templateSheet = ss.getSheetByName("EmailTemplates");
var emailTemplate = templateSheet.getRange("A10").getValue();
// Create one JavaScript object per row of data.
objects = getRowsData(dataSheet,dataRange,4);
// For every row object, create a personalized email from a template and send
// it to the appropriate person.
for (var i = 0; i < objects.length; ++i) {
// Get a row object
var rowData = objects[i];
rowData.rowNumber = i + 5;
// Generate a personalized email.
// Given a template string, replace markers (for instance ${"First Name"}) with
// the corresponding value in a row object (for instance rowData.firstName).
if (!rowData.emailed) {
var emailText = fillInTemplateFromObject(emailTemplate, rowData);
emailText = emailText.replace("EVENT_NAME", eventName);
var emailSubject = "Joining Instrucations for " + eventName;
MailApp.sendEmail(rowData.emailAddress, emailSubject, emailText);
dataSheet.getRange(rowData.rowNumber, 2).setValue(Utilities.formatDate(new Date(), "GMT", "dd/MM/yy HH:mm"));
// Replaces markers in a template string with values define in a JavaScript data object.
// Arguments:
//   - template: string containing markers, for instance ${"Column name"}
//   - data: JavaScript object with values to that will replace markers. For instance
//           data.columnName will replace marker ${"Column name"}
// Returns a string without markers. If no data is found to replace a marker, it is
// simply removed.
function fillInTemplateFromObject(template, data) {
var email = template;
// Search for all the variables to be replaced, for instance ${"Column name"}
var templateVars = template.match(/\$\{\"[^\"]+\"\}/g);
// Replace variables from the template with the actual values from the data object.
// If no value is available, replace with the empty string.
for (var i = 0; i < templateVars.length; ++i) {
// normalizeHeader ignores ${"} so we can call it directly here.
var variableData = data[normalizeHeader(templateVars[i])];
email = email.replace(templateVars[i], variableData || "");
return email;
// there are also some Google example functions used getRowsData, getObjects, normalizeHeaders, normalizeHeader, isCellEmpty, isAlnum and isDigit

Gordon Brown’s Building Britain’s Digital Future announcement with twitter subtitles

I’ve been quietly snaffling twitter timelines using the subtitle generator I created. The latest one was prompted by Brian Kelly’s post on the The “Building Britain’s Digital Future” Announcement. In this post he mentioned that twitter was a buzz with the #bbdf hashtag.

Clicking on the image below will take you to a page I created from the subtitle generator. Because Number 10 use a commercial company to host some of their content, using a bespoke flash player, I create a page and embed the video and used the Javascript SMIL Browser.

Twitter subtitles for the Digital Future Speech

Update: As well as being ‘Fun, intriguing..‘ Brian Kelly has a nice post on the ‘Issues In Crowd-sourced Twitter Captioning of Videos‘ (which despite the title I read as a positive use of tweets as they are contextualised with an event). Tony (who inspired me to look at this area) and I are both keen to take some of the twitter captioning ideas forward so if any developers or funders want to get involved we’d like to hear from you.

Update: If you want to see how I combined Gordon Brown’s speech and tweets here is the ‘making of’ video

Update: Number 10 have put Gordon Brown’s speech on YouTube which makes embeding subtitles a lot easier. Click here to see the same example but using the YouTube clip

What I’ve starred this week: March 23, 2010

Here's some posts which have caught my attention this week:

Automatically generated from my Google Reader Shared Items.

Google Wave Update: Latest Developments and New Applications

The post below originally appeared in RSC NewsFeed. I’m reposting to add to my Google Wave collection.

It has been noticeable that mentions of Google Wave have dropped off as the year has progressed. Whilst Google’s Buzz has been catching the headlines, the Wave Team are still quietly working away tweaking both the core code and functionality of this application. Two of the biggest changes have been the inclusion of  ‘read-only and restore’ and ‘email notifications’ features. [Editor: Another new feature not mentioned in this post is the new Extensions Link and Gallery]

Prior to this new feature all participants on a wave had read/write access. This meant that anyone viewing a wave could also edit it. Users had developed a workaround by adding an automated participant (a robot) to the wave which would freeze the wave from further editing but it was clear that this solution was a compromise.

This allows the user to restore the wave to a previous version via playback.

Email notification
Shortly after the official first outing of Google Wave there were a number of headlines describing it as an ‘email killer’. Email is so deeply embedded into our communication strategies it is very unlikely that Wave will replace email, instead it is more likely to be a symbiotic relationship. This is apparent in Wave’s latest feature, email notifications. This allows users to receive an email notification about new and updated waves. Some may see this as a retreat by Google away from their original intension to ‘reinvent email’ but I see it as a realistic response to the wider environment.

New application – ConceptDraw MindWave

Google Wave continues to be developed by third parties, a sign perhaps that users can see beyond the hyperbole. One of the most recent extensions is a gadget called ConceptDraw MindWave. This application allows users to collaboratively build mind maps within waves  which can then be downloaded to the desktop ConceptDraw MINDMAP software. View this link for a demonstration video and more information.

What I’ve starred this week: March 16, 2010

Here's some posts which have caught my attention this week:

Automatically generated from my Google Reader Shared Items.

Updating a Google Calendar and Google Site from a Google Spreadsheet (the beginnings of an event booking system)

Update: New version is posted here The best Google Spreadsheet Event Manager (w/h Sites, Contact, Calendar integration) ever, ever, ever

I’m going to start this most with a small homage to Tony Hirst. Tony has the honour of posting the first ever comment on this blog and ever since I’ve been an avid follower of his work on When I saw Tony had started playing around experimenting with using Google Apps Scripts to post data from Spreadsheet to Calendar and vice versa it looked like he was having way to much fun, so like with so many of Tony’s other ideas I thought I would have a go too.

The challenge I set myself was to take Tony’s work and see if I could use it as the basis on an online events booking system. The idea was to enter event details into a spreadsheet which would then be used to automatically populate a site and spreadsheet, delegates being able to book in via a form. Here’s how I got on …

#Issue 1 – This solution requires Google App Scripts which aren’t available to in the basic version of Docs/Site. To get access you need at least a Google Apps Standard, which is free but you need to register with a domain url.

Update: I like to think it was because of my post that Google now makes Apps Script available to everyone using Spreadsheet ;-)

Having signed up to Google Apps I then started following Tony’s posts on Updating Google Calendars from a Google Spreadsheet and Maintaining a Google Calendar from a Google Spreadsheet, Reprise. My spreadsheet layout is almost identical except I added 2 more columns, an ‘Added Date’ and ‘ID’.image

Tony, I was able to set the locale for the date in the spreadsheet via ‘File –> Spreadsheet Settings’ and added some conditional formatting to highlight items which hadn’t been added yet.

Below is the code I dropped into the spreadsheets script editor.

function processEvents() {
 //declare vars
 var cal = CalendarApp.getDefaultCalendar();
 var ss = SpreadsheetApp.getActiveSpreadsheet();
 var dataSheet = ss.getSheetByName("Events"); // ref sheet name (thought I might have ended up with multiple sheets)
 var maxcols = dataSheet.getMaxColumns();
 var dataRange = dataSheet.getRange(2, 1, dataSheet.getMaxRows() - 1, maxcols);
 var data = dataRange.getValues();
 var cal = CalendarApp.getDefaultCalendar();
 var site = SitesApp.getSite("", "mashe-events-from-calendar"); // .getSite(domain, sitename)
 var annPage = site.getAnnouncementsPages();
 var col_added, col_title, col_desc, col_tstart, col_tstop, col_loc, col_num, col_ID;
 // pull column header
 for (var j=1;j<=maxcols;j++){
   var header= dataSheet.getRange(1, j, 1, 1).getValue();
     case "Status/Action":col_added=j-1;
     case "Title":col_title=j-1; break;
     case "Description":col_desc=j-1; break;
     case "Start": col_tstart=j-1; break;
     case "Stop": col_tstop=j-1; break;
     case "Location": col_loc=j-1; break;
     case "Number of places": col_num=j-1; break;
     case "ID": col_ID=j-1; break;
 // pull data
 for (i in data) {
   var row = data[i];
   var added = row[col_added];  //Check to see if details for this event have been added to the calendar(s)
   if (added == "Add"){
     // collect event details
     var title = row[col_title];
     var desc = row[col_desc];
     var loc = row[col_loc];
     var tstart = row[col_tstart];
     var tstop = row[col_tstop];
     cal.createEvent(title, tstart, tstop, {location:loc, description:desc}); // create calendar event
     var message = "Start:" + Utilities.formatDate(tstart, "GMT", "dd/MM/yy HH:mm") +"<br/>Finish: "+Utilities.formatDate(tstop, "GMT", "dd/MM/yy HH:mm")+"<br/>"+desc; // prepare message
     site.createAnnouncement(title, message, annPage[0]); // add announcement to site
     var annList = site.getAnnouncements();
     var eventID = annList.length; // get announcement ID
     // set value in spreadsheet
     var v = parseInt(i)+2; // +2 is an offset to do with the numbering of rows and the "blank" header row 0;
     dataSheet.getRange(v, 3, 1, 1).setValue(eventID); // add the event ID
     dataSheet.getRange(v, 2, 1, 1).setValue(new Date()); // add today's date/time to 'Added Date' column
     dataSheet.getRange(v, 1, 1, 1).setValue("Added"); //set the fact that we have updated the calendars for this event

My main tweaks were:

  • dynamically capture the data range using getMaxRows() and getMaxColumns();
  • define a Google Site to push data to (as well as the Calendar);
  • adding the event to a Google Site using createAnnouncement(); and
  • adding the ‘added date’ and ‘Event ID’

Here is the public calendar and Google site populated with data from the spreadsheet (web page view) - I’m currently playing around with the script a bit more so these links are turbulent.

Having gotten this far my next plan was to use the Expense Report Approval Tutorial as a basis for a booking form which would allow a submit/approve workflow. To do this my plan was to have a template spreadsheet for each event which I duplicate using the event ID as an identifier.

# Issue 2 – Google Apps Script doesn’t have a method for duplicating spreadsheets

I think I might have got around this by creating a function which would cycle through an existing spreadsheet, storing the values and then creating a new sheet reversing the process to populate it with data. I don’t think it would however have duplicated form functionality. The concept was beginning to unravel however because …

# Issue 3 – You can only have one form per spreadsheet.

Oh dear!

It was all getting a bit too messy which is a shame because it would have been fun to use Contact Services API to add delegates to Google Contacts either using the setNotes() or setUserDefinedField() to record events the delegate had signed up for, dietary requirements etc. and lots more interesting stuff.

One solution to the who registration issue  might have been to Leveraging Google App Engine services from scripts, but that is going into a whole realm of coding I would prefer to avoid.

So SharePoint anyone? ;-)

What I’ve starred this week: March 9, 2010

Here's some posts which have caught my attention this week:

Automatically generated from my Google Reader Shared Items.

NSSE Survey: Enhancement of student engagement and high impact activities (aka the big beasty approach)

Me doing my bit for 'The Services' The 2nd and 3rd of March saw the 7th annual Enhancement Themes conference. As with the past two year I was a delegate at this event, but this time in the slightly different role of one of the exhibitors. In between showcasing some of the JISC wares I managed to slip into some of the keynote sessions. Each year the Enhancement themes do a great job is finding presenters who are incredibly influential and thought provoking. This year my favourite was Professor George Kuh, Chancellor’s Professor of Higher Education at Indiana University Bloomington, and founding director of widely used National Survey of Student Engagement (NSSE*).

Unlike the NSS which is looking at general student satisfaction, NSSE is a survey which aims to “assess the extent to which students engage in educational practices associated with high levels of learning and development”. It assesses this by asking how students spend their time and what they gain from attending their institution. My understanding is that NSSE is

using engagement as an indicator of “grades, persistence, student satisfaction and gains across a range of desired outcomes”

The survey is broken into four main areas: student behaviours; institutional actions and requirements; reactions to the institution; and student background information. For examples of type of questions asked you can view past and present NSSE surveys.

The NSSE has been widely adopted in North America (US: 3million responses from 1,400 institutions; Canada: 64 HEIs), South Africa and Australia and been used since 2000 resulting in a rice dataset.

Student engagement varies more within than between institutions

The NSSE has found that there is more local than inter institutional variation in student engagement. i.e. there is little difference in student engagement between the best and the worse institutions, but the differences between institutional departments is major.

‘How can you get students more engaged?’ I hear you ask, why with ‘high-impact’ learning activities. As it happens George has written book on the topic, High-Impact Educational Practices: What They Are, Who Has Access to Them, and Why They Matter. Although it wasn’t explicitly mentioned I image George has used the NSSE survey to identify ‘high-impact’ activities. His top 10 were:

  • First-Year Seminars and Experiences
  • Common Intellectual Experiences
  • Learning Communities
  • Writing-Intensive Courses
  • Collaborative Assignments and Projects
  • “Science as Science Is Done”; Undergraduate Research
  • Diversity/Global Learning
  • Community-Based Learning
  • Internships
  • Capstone Courses and Projects

There are some Americanisms in this list but hopefully you get the picture. I would say all of these activities are reflected in the existing academic research on best teaching/learning practices.

Engaging in these activities George suggested that students are more likely to:

  • Invest time and effort
  • Interact with faculty and peers about substantive matters
  • Experience diversity
  • Get more frequent feedback
  • Reflect & integrate learning
  • Discover relevance of learning through real-world applications

The Enhancement Themes have already generated a number of resources covering these areas including research teaching linkages, integrative assessment and assessment, the first year and responding to student needs (as well as flexible delivery). The conference also saw the publication of 5 new briefing papers under the current ‘Graduates for the 21st Century strand:

In George’s presentation he highlighted three action steps:

  1. Use engaging pedagogies campus wide
  2. Put money where it will make a difference to student success
  3. Ensure programs are high quality

I’m sure you would agree that all of these are what we are striving for anyway and not particularly earth shattering. If you adopted the NSSE locally there is an opportunity to use the data diagnostically to identify the your ‘local variations’, identifying courses that are not engaging students. I’m sure many of you are already doing this through end of module surveys, but I would recommend looking at the NSSE questions to see if these need to be re-examined.

Slides for all the conference keynote sessions including George Kuh’s are available on the Enhancement Themes plenary presentations page (videos of the sessions will be available shortly). Update: Videos now available here

*pronounced Nessie, hence the title of this post

What I’ve starred this week: March 2, 2010

Here's some posts which have caught my attention this week:

Automatically generated from my Google Reader Shared Items.


This blog is authored by Martin Hawksey e-Learning Advisor (Higher Education) at the JISC RSC Scotland N&E.

JISC RSC Scotland North & East logo

If you would like to subscribe to my monthly digest please enter your email address in the box below (other ways are available to subscribe from the button below):

Subscribe to MASHe to monthly email updates


The MASHezine (tabloid)

It's back! A tabloid edition of the latest posts in PDF format (complete with QR Codes). Click here to view the MASHezine

Preview powered by: Webthumb

The MASHezine (eBook)

MASHe is also available in ebook and can be downloaded in the following formats:



Opinions expressed in this blog are not necessarily those of the JISC RSC Scotland North & East.

JISC Advance Logo

JISC Advance is a new organisation that brings together the collective expertise of established JISC services:

For further information visit

Creative Commons Licence
Unless otherwise stated this work is licensed under a Creative Commons Attribution-ShareAlike 2.5 UK: Scotland License