Pages

Wednesday, 24 April 2013

On That Note...Building a Notification System

As a part time teacher and Director of Technology for the PCCE (Prairie Centre for Christian Education) I have constantly been on the lookout for practical teaching tools for both my classroom and my fellow colleagues. Google Apps for Education initially caught my attention a few year ago and since then, has continued to amazed me with the amount of care and development that has gone into this outstanding suite of tools for educators.

One of my initiatives with the PCCE was to build a curriculum development environment (www.tftshare.ca) for our teachers that utilized the highly collaborative Google Apps for Education platform. It is a place where teachers can create units, lesson plans and resources using the Google Apps and Google Docs suite of tools and share them with one another. When the unit is finished, the user can post the unit for others in the domain to view. Naturally, the fantastic flexibility of Google Apps script was used as the glue that holds it all together. We now are extremely pleased to have over 2000 educational units currently under development by our instructors! Even with this wonderful success story, we saw a few issues and some definite areas for improvement.

Ultimately, we wanted to increase collaboration and facilitate the sharing of information between staff. However, many people did not check the email address associated with their tftshare.ca account; and who could blame them? Most of us have a plethora of accounts that seems to be growing from year to year. Having one more account to check just seems like one more thing. What to do? Well, I decided that the environment needed an email notification system that would inform people when important emails were sent out to them. Emails that would:
  • notify the user when a request was made to share a unit
  • let the user know when one of their units was posted
  • inform the user when they received a Google + notification from a colleague
  • share important information from TfT Share administrators (updates, new features, etc.)
Building the email notification system was surprisingly easy with the Gmail Service provided through Google Apps Script. For example, when users share a unit or a TfT Coordinator posts a unit, the user will be informed via email using a script sent via an app on the tftshare.ca website.


First, the Gmail account is scanned to see if a [TfT Share] label has been created. This is the label that will be used to archive the email notification when the link is clicked to view its contents or the user clicks the close button:
// Check the user’s labels to see if a “[TfT Share]” label exists. If not, create one
var labels = GmailApp.getUserLabels();
var x = labels.length;
var found = false;
 while (x--) {
   var label = labels[x].getName();
   if (label == "[TfT Share]") {
    found = true;
    }
 }
 if (found == false) {
  GmailApp.createLabel('[TfT Share]');
 }

The user's messages are then scanned via the GmailApp.getInboxThreads() function:
// scan the first 50 threads of a user’s inbox and store the results in an array
// next, scan the messages first subject text of a thread via the .getFistMessageSubject() function
var threads = GmailApp.getInboxThreads(0, 50);
var x = threads.length;
var message = threads[x].getFirstMessageSubject();

Once I have the subject text of the first message in a thread, I scan the subject text for key words and perform functions depending on specific keyword matches:
// find a match in the subject text and create a notification box based on the text - this particular notification is for a unit shared with editing privileges
if (message.search(/\[TfT Share\]/) !== -1 && message.search('EDITING Permission') !== -1) {
 // found a match so do create the notification
 var threadId = threads[x].getId();
 var date = threads[x].getLastMessageDate();     
 // image for the notification icon
 var nImg = app.createImage('https://sites.google.com/a/tftshare.ca/g-g/3-0/n-edit.png');
 // URL to identify the email if clicked
 var mailUrl = "https://mail.google.com/mail/u/0/#all/" + threadId;
 // set the id of this element via .setId(‘panel’+threadId) to retrieve when element is is clicked
 var msgPanel = app.createVerticalPanel().setId('panel'+threadId)
     .setStyleAttribute('borderRadius','10px')
     .setStyleAttribute('margin','7px')
     .setStyleAttribute('background','rgb(220,235,190)')
     .setStyleAttribute('padding','10px');
 // top message for the notification      
 var topMsg = app.createLabel('A TfT Unit Was Shared With You With EDITING Permission:').setStyleAttribute('minWidth','375px')
     .setStyleAttribute('fontSize','14px')
     .setStyleAttribute('fontWeight','bold')
     .setStyleAttribute('color','rgb(0,57,101)');
 // Link to open the notification email
 // set the id of this element via .setId(threadId) to retrieve when element is is clicked
 var botLink = app.createAnchor(message, mailUrl).setId(threadId).setStyleAttribute('minWidth','435px')
     .setStyleAttribute('fontWeight','bold')
     .setStyleAttribute('fontFamily','arial')
     .setStyleAttribute('color','rgb(37,132,201)')
     .setStyleAttribute('textDecoration','none');
 // add the date to the notification
 var dateMsg = app.createLabel(date)
     .setStyleAttribute('fontSize','10px')
     .setStyleAttribute('fontStyle','italic')
     .setStyleAttribute('color','green');
 // click handler archives the email to [TfT Share] label - created automatically by script on open    
 var linkCkH = app.createServerClickHandler('archive');
 botLink.addClickHandler(linkCkH).addClickHandler(ClH);
 // close button - archives the link and removes it from notification system if it isn’t read
 // set the id of this element via .setId(‘close’+threadId) to retrieve when element is is clicked
 var close = app.createImage('https://sites.google.com/a/tftshare.ca/g-g/3-0/button-close-n.png').setId('close'+threadId);
 var closeCkH = app.createServerClickHandler('archiveClose');
 close.addClickHandler(closeCkH).addClickHandler(ClH);  
 var msgGrid = app.createFlexTable();
 msgGrid.setWidget(0,0,nImg).setWidget(0,1,topMsg).setWidget(0,2,close);
 msgPanel.add(msgGrid).add(botLink).add(dateMsg);
 vPanel.add(msgPanel);
 // add one more to the total number of email notifications for the red notification indicator total
 total++;
}

The archive functions archive the selected email notification under the [TfT Share] label that is created at the beginning of the script:
// EMAIL URL ANCHOR LINK CLICKED: this function captures the threadId via “e.parameter.source” and .getId() function to archive the email and close the panel. The function also decreases the total counter and sets the text to show the change in the total
function archive(e) {
 var app = UiApp.getActiveApplication();
 // read the threadID set up via .setId() from the source of the clicked element
 var threadId = e.parameter.source;
 // close the panel containing the clicked element
 app.getElementById('panel'+threadId).setVisible(false);
 // archive the email that has the threadId
 var thread = GmailApp.getThreadById(threadId);
 var label = GmailApp.getUserLabelByName('[TfT Share]');
 thread.addLabel(label).moveToArchive();
 // decrease the total count in the visible counter
 var tempTotal = parseInt(UserProperties.getProperty('nTotal'));
 var nTotal = tempTotal -1;
 UserProperties.setProperty('nTotal',nTotal);
 app.getElementById('nTotal').setText(nTotal);
 app.getElementById('screenerPanel').setVisible(false);
 if (nTotal == 0) app.getElementById('noNotesPanel').setVisible(true);
 return app;  
}

// CLOSE BUTTON CLICKED: this function captures the threadId via “e.parameter.source” and .getId() function to archive the email and close the panel. The function also decreases the total counter and sets the text to show the change in the total
function archiveClose(e) {
 var app = UiApp.getActiveApplication();
 // read the threadID set up via .setId() from the source of the clicked element
 var tempId = e.parameter.source;
 // remove the word ‘close’ from the Id to extract the threadId variable
 var threadId = tempId.substr(5);
 // close the panel containing the clicked element
 app.getElementById('panel'+threadId).setVisible(false);
 // archive the email that has the threadId
 var thread = GmailApp.getThreadById(threadId);
 var label = GmailApp.getUserLabelByName('[TfT Share]');
 thread.addLabel(label).moveToArchive();
 // decrease the total count in the visible counter
 var tempTotal = parseInt(UserProperties.getProperty('nTotal'));
 var nTotal = tempTotal -1;
 UserProperties.setProperty('nTotal',nTotal);
 app.getElementById('nTotal').setText(nTotal);
 app.getElementById('screenerPanel').setVisible(false);
 if (nTotal == 0) app.getElementById('noNotesPanel').setVisible(true);
 return app;  
}

And there you have it! Essentially, the notification system has three major functions:
  1. SCAN user emails for keywords to trigger notifications
  2. CREATE notifications and add them to the total
  3. ADD clickHandlers to handle close events and anchor click events to close the panel, archive the email and decrease the total count

Although designed for an educational setting, this notification system design could be adapted to any number of use cases, from a classroom student notification system to a organizational notification system for businesses and non-profits. Quick and easy to modify, the notifications can also be customized by colour and icon to suit any need.

Sample Notification from the System
Sample Notification from the System
Email Notification

Notification System with the Curriculum Creation/sharing Environment
[written entirely in Google Apps Script]