One of the problems of measuring script or library usage is the identification of returning users. As you know email addresses or some other identification is not easily accessible (rightly so), and neither should it be stored in Analytics data. In Universal analytics measurement protocol for your GAS libraries I showed how Google Analytics could be used for library measurement, but the problem still persisted of counting the absolute number of different users in a kind of anonymous way.

Since the properties service has a script and a user level store, and although it’s obvious, it occurred to me that this could be used to know whether someone had used a library before. If they had, there would be a user level store – if not – they must be new.

So I put together this library that you can use for tracking usage (it can of course also track its own usage). Here’s the key, or you can copy the code at the end of this post.

MwbZ76EQqpFNfqh-XUl5Jxqi_d-phDA33

How to use

In the script that needs tracking, then do this. It returns the usage stats if you want to look at them

// update usage of this script
  Logger.log (GoingGasLib.PropUtils.updateUsage(
    PropertiesService.getScriptProperties(),
    PropertiesService.getUserProperties()
	));

If you need to get overall usage stats, then you can also get them with this

Logger.log(GoingGasLib.PropUtils.getUsage(
    PropertiesService.getScriptProperties()
	));

If you want to track a library, I would recommend that you also allow opting-in rather than just tracking without asking. If you are using my version of this library and want to opt in to my tracking, then put this in your script too. The default is that no tracking is done.

GoingGasLib.PropUtils.initializeUsage();

What data is stored

Here’s what will be in your script store

{
 "visits": 5,
 "averageTimeBetweenVisits": 283606.2,
 "uniqueVisitors": 2,
 "dateOfLastVisit": 1453893328519
 }

And here’s what’s in each users store

{
 "visits": 3,
 "id": "4386370c-f222-4c57-9442-557d4c79c0d7",
 "averageTimeBetweenVisits": 45053.333333333336,
 "dateOfLastVisit": 1453892045537
 }

The Id

So now every unique user will have an id you can use – for example – now I can improve Universal analytics measurement protocol for your GAS libraries by using this id to submit data against. It has the benefit of not being traceable to a specific person since no identifiable information is stored, but yet is unique and reusable, so will be useful in its own right – whether or not you use this for tracking usage.

The code

Here’s the function code.

/**
* libary for use with Going Gas Videos
* PropUtils contains useful functions for handling Properties
* @namespace
*/

var PropUtils = (function(ns) {
  
  
  /**
  * get information on this overall script usage
  * @param {PropertiesStore} store the one to use
  * @return {object} usage
  */
  ns.getUsage = function  (store) {
    var result = store.getProperty ('usage');
    return result ? JSON.parse(result) : null;
  };
  
  /**
  * set information on this overall script usage
  * @param {PropertiesStore} store the one to use
  * @param {object} value the data to set
  * @return {object} the data
  */
  ns.setUsage = function  (store,data) {
    store.setProperty ('usage', JSON.stringify(data));
    return data;
  };
  /**
  * update library usage
  * @param {PropertiesStore} scriptStore the script store
  * @param {PropertiesStore} userStore the user store
  * return {object} the two updated usages {script:{},user:{}}
  */
  ns.updateUsage = function (scriptStore, userStore) {
    
    var now = new Date().getTime();
    // get  this users usage, create empty if new
    var userUsage = ns.getUsage(userStore) || { 
      visits:0,
      id:Utilities.getUuid(),
      averageTimeBetweenVisits:0,
      dateOfLastVisit:0
    };
    
    //  get  this users usage, create empty if new
    var scriptUsage = ns.getUsage(scriptStore) || {
      visits:0,
      averageTimeBetweenVisits:0,
      uniqueVisitors:0,
      dateOfLastVisit:0
    };

    
    // now update
    if (scriptUsage.visits) {
      scriptUsage.averageTimeBetweenVisits = 
        (scriptUsage.averageTimeBetweenVisits * scriptUsage.visits + 
         now - scriptUsage.dateOfLastVisit)/ (scriptUsage.visits+1);
    }
    
    scriptUsage.visits++;
    scriptUsage.dateOfLastVisit = now;
    
    // if its a new user..
    if (!userUsage.visits) {
      scriptUsage.uniqueVisitors ++;
    }
    
    // and the user stuff
    if (userUsage.visits) {
      userUsage.averageTimeBetweenVisits = 
        (userUsage.averageTimeBetweenVisits * userUsage.visits + 
         now - userUsage.dateOfLastVisit)/ (userUsage.visits+1);
    }
    userUsage.visits++;
    userUsage.dateOfLastVisit = now;
    
    // set the the updated numbers
    return {
      script:ns.setUsage(scriptStore,scriptUsage),
      user:ns.setUsage(userStore, userUsage)
    };
    
  }
  
  // initialize library usage
  ns.initializeUsage = function () {
    ns.updateUsage(
      PropertiesService.getScriptProperties(), PropertiesService.getUserProperties()
    );
  };
  
  return ns;
  }) ( PropUtils || {});

Opting in for libraries

As I mentioned earlier, I think you that users should have to opt in to your tracking tracking of the usage of your library. But you can make the tracking mandatory, simply by adding this just before the end of your copy of the namespace, which will initiate track every time a script is run that includes your library.

ns.initializeUsage();
For more like this see Google Apps Scripts Snippets
Why not join our forum, follow the blog or follow me on Twitter to ensure you get updates when they are available.