cancel
Showing results for 
Search instead for 
Did you mean: 

How to Set Up Google Analytics for Canvas

jperkins
Instructure
Instructure
26 113 30K

Using Google Analytics means that your institution is subject to Google's terms of service. Please verify with your institution's legal team before installing Google Analytics in your Canvas instance to ensure that you are in compliance with relevant laws and regulations.

This outlines the use of Google Analytics without Google Tag Manager. This is an old implementation. You may want to look into some of these other community resources if you are configuring Google Analytics for the first time. It does require a little bit more upfront time to get configured, but is considered the new way of doing Google Analytics.

New Custom Dimensions & Updated Script - May 31, 2019

A core code change to Canvas on Aug 28, 2019 may have broken your implementation of Google Analytics. Please install the updated code to restore functionality.

The steps below will help you get set up to use Google Analytics for Canvas. If you'd like to know more about why you might want to do this, please watch this Canvas Live session.

Create a Google Analytics Account

  1. Sign into Google
  2. Navigate to https://analytics.google.com  and click Sign Up
  3. Fill out the Form as desired, using your Canvas url in the website url field (eg: https://example.instructure.com) (or vanity url if you have one)
  4. Click "Get Tracking ID" button
  5. Accept Terms of Service
  6. Copy down the "Tracking ID" (eg: UA-12345678-1)

Set Up Tracking By User-ID New

Allows for more accurate session unification based on the user's Canvas ID, granting better user overtime metrics

  1. Navigate to the Admin Portal (If you just created your account you are already there)

  2. Click on "Tracking Info" to expand the menu item

  3. Click on "User-ID" option

  4. Read the agreements and follow the prompts

Add Custom Dimensions to Google Analytics Updated

This will let you pass custom variables from Canvas to Google Analytics such as User ID's, User Names, Course ID's and more.

  1. Navigate to the Admin Portal (If you just created your account you are already there)
  2. Click on "Custom Definitions" menu item
  3. Click on "Custom Dimensions menu item
  4. Click "+ NEW CUSTOM DIMENSION" button
  5. Add the following dimensions in order (If your index number is incorrect shared dashboards may not work)314355_pastedImage_7.png

Add Custom Javascript to Your Institution's Theme Editor

** Custom Javascript has been known to break things in Canvas. As of writing this post, May 31, 2019, it is working, but be aware that could change in the future.

  1. Download the attached Javascript File (If you cannot download for browser security reasons, you may copy and paste the code into a .js file from github. Minified Script. Non-minified Script.)
  2. Edit the File in a text editor
    • You'll need to customize line 3 (if using the minified version), or line 168 (non-minimized) with your tracking id number. Updated
    • //Working as of Aug 28, 2019
      function removeStorage(e){try{localStorage.removeItem(e),localStorage.removeItem(e+"_expiresIn")}catch(t){return console.log("removeStorage: Error removing key ["+e+"] from localStorage: "+JSON.stringify(t)),!1}return!0}function getStorage(e){var t=Date.now(),o=localStorage.getItem(e+"_expiresIn");if(null==o&&(o=0),o<t)return removeStorage(e),null;try{return localStorage.getItem(e)}catch(t){return console.log("getStorage: Error reading key ["+e+"] from localStorage: "+JSON.stringify(t)),null}}function setStorage(e,t,o){o=null==o?86400:Math.abs(o);var s=Date.now()+1e3*o;try{localStorage.setItem(e,t),localStorage.setItem(e+"_expiresIn",s)}catch(t){return console.log("setStorage: Error setting key ["+e+"] in localStorage: "+JSON.stringify(t)),!1}return!0}async function coursesRequest(e){let t=await fetch("/api/v1/users/self/courses?per_page=100"),o=await t.text();o=o.substr(9),o=JSON.parse(o);var s=JSON.stringify(o);return setStorage("ga_enrollments",s,null),parseCourses(e,s)}function parseCourses(e,t){if(null!=t){let s=JSON.parse(t);for(var o=0;o<s.length;o++)if(s[o].id==e)return s[o]}return null}function gaCourseDimensions(e){custom_ga("set","dimension4",e.id),custom_ga("set","dimension5",e.name),custom_ga("set","dimension6",e.account_id),custom_ga("set","dimension7",e.enrollment_term_id),custom_ga("set","dimension8",e.enrollments[0].type),custom_ga("send","pageview")}function googleAnalyticsCode(e){var t,o,s,n;if(custom_ga("create",e,"auto"),t=ENV.current_user_id,o=ENV.current_user_roles,custom_ga("set","userId",t),custom_ga("set","dimension1",t),custom_ga("set","dimension3",o),n=window.location.pathname.match(/\/courses\/(\d+)/)){n=n[1],s=0;try{let e=getStorage("ga_enrollments");if(null!=e){var r=parseCourses(n,e);null===r?coursesRequest(n).then(e=>{null===e?(custom_ga("set","dimension4",n),custom_ga("send","pageview")):gaCourseDimensions(e)}):gaCourseDimensions(r)}else coursesRequest(n).then(e=>{null===e?(custom_ga("set","dimension4",n),custom_ga("send","pageview")):gaCourseDimensions(e)})}catch(e){if((s+=1)>5)return custom_ga("set","dimension4",n),void custom_ga("send","pageview")}}else custom_ga("send","pageview")}!function(e,t,o,s,n,r,a){e.GoogleAnalyticsObject=n,e[n]=e[n]||function(){(e[n].q=e[n].q||[]).push(arguments)},e[n].l=1*new Date,r=t.createElement(o),a=t.getElementsByTagName(o)[0],r.async=1,r.src="https://www.google-analytics.com/analytics.js",a.parentNode.insertBefore(r,a)}(window,document,"script",0,"custom_ga");
      googleAnalyticsCode("UA-12345678-1"); // customize google analytics tracking number here‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
  3. Save the javascript file after making changes
  4. Upload the javascript file into your institution's Theme Editor
  5. Navigate back to Google Analytics and view real-time data to see if it is tracking real-time users on your account. Know that you may have to wait for 15 minutes or so to be able to see custom dimension data.

Build Dashboards & Reports in Google Analytics

  1. You may wish to get started by downloading the following dashboards:
  2. Build your own and once you get it the way you want, share the template URL in the comments on this document!

Embedded Custom Javascript File

There were issues with people copying the embedded Javascript. Please view the scripts on Github.

Changes Made (May 31, 2019) New

The updated script has undergone significant change. Below are some highlights of what the updated script does. The updated script is mostly backwards compatible with the previous version.


General Changes

  • Updated to remove any jQuery dependencies. Modern javascript is being used (may not work on Internet explorer). This means that API calls are now asynchronous and non-blocking. Improves performance.
  • Fixed a bug that meant some pageviews were never sent.
  • Added a timeout if API requests fail to avoid server spamming
  • Added support for Google Analytics User-ID tracking
  • Altered API Endpoint & Data Caching
    • Instead of pulling the course information (/api/v1/course/:course_id), the script pulls a user's courses ("/api/v1/users/self/courses?per_page=100") which includes an enrollment object and caches the returned value into local storage. The associated enrollment object allows us to set the "Canvas Course Role" dimension. Caching the data should also result in significantly better performance due to reduced API calls. If a course does not appear in the user's course list just the course_id and user dimensions will be sent (should only be for Admins and public courses).  Also it will only pull the first 100 enrollments for a user (let me know if you need support for pagination with the revised endpoint).

Dimension Changes

  • Canvas User RoleUpdated
    • Instead of filtering for a best match of a user's roles and only returning 1 value this is now a comma separated array of all of a user's roles. When filtering on this value, look use something like "contains" & "student" to get any users with a role of student. The data is still pulled from environmental variables. You should be able to build better Audience Segments from this data. (ie: Students should only have user, student, and maybe ta roles).
  • Canvas Term ID New
    • This can be a valuable filtering tool as much of Canvas built in reports filter on terms
  • Canvas Course Role New
    • By altering the way the script queries the course data, we are able to now capture the exact role the user has in a course. If a user has multiple user roles in a course, the value assigned to the pageview will always be the first role in the array. This should be a very useful dimension for building reports and analyzing course usage. If you think capturing all user roles will be helpful, let me know in the comments.
113 Comments
simsrg
Community Participant

Yep just append it to your JS file.  It may be picky about being at the beginning or end of the file. The GA code is at the end of our custom JS file.  

simsrg
Community Participant

Sorry to hear that.  I know we are interested in knowing the traffic data for the Canvas mobile app to try and get a complete picture of Canvas usage on our campus.

rgibson1
Community Champion

We have a question: We have Google Analytics connected to our test server (for testing), but we're not getting any data. Nothing. The dashboard is empty. I assumed the test server would provide at least something, but I don't see anything at all. Would that be normal?

michaelg
Community Member

I have tried countless times to get Analytics connected - had several people look at the code and have found nothing wrong with it. My thread is above. 

I've also reached out to Instructure/Canvas support and the most they can do is refer me to this page. We have been using Canvas since the fall and still have 0 analytics and absolutely no one from Canvas willing to support us on this endeavor. I look forward to learning how or what steps you guys do to get it fixed on your end. Sorry I was of no help in this reply. 

Boekenoogen
Community Contributor

Are there any tricks we should know about before we begin? My team will start this process over the summer.

jperkins
Instructure
Instructure

rgibson1‌ This has been my experience, test and beta do not send data. I believe it has to do with the initial set up. I haven't tried this, but if you create a new account from the Admin console and plugin the test environment URL, I think you might get test data.

jperkins
Instructure
Instructure

 @tom_lamy ‌ Did you ever get a response on this? The vanity URL is what I would use to configure it with.

jperkins
Instructure
Instructure

 @kburns6 , If you run a provisioning report of users, it will include the Canvas database ID that is being sent to Google analytics. That way you can look people up without having to navigate to their user in Canvas.

jperkins
Instructure
Instructure

I'd recommend installing this Google Analytics Debugger - Chrome Web Store. You can toggle the plugin on then see the data that is being sent to Google Analytics.  You can see it by opening up the browser console (right click and inspect). You should see two the data being sent and whether the custom dimensions were set. This is helpful when troubleshooting as it can take up to 24 hours for some of the custom data to populate in the dashboards.

272564_pastedImage_2.png

Boekenoogen
Community Contributor

Thanks for the information! 

simsrg
Community Participant

Anyone else get an email from Google about the new European data protection law and how it affects data in Google Analytics?  I received it about a week ago and just taking a look at it.  Has anyone else taken a look at this issue?

ptart
Community Participant

I think every Google Analytics user got that. From what I can tell, it helps clarify how long data is stored on Analytics' servers. There have been several companies updating data issues after Facebook got the limelight.

Data retention - Analytics Help 

Go to .js Tracking Info > Data Retention.

Google Analytics

Update how long you want your data to be retained.

Google Analytics Data Retention Options

All finished.

simsrg
Community Participant

That data retention did seem to be the big take away from the email.  The privacy laws going into effect from the EEA are pretty far reaching and it will be interesting to see if this data retention setting is all that will need to be changed or modified.  There was a mention about deleting user data for users from the EEA which could be quite a task with the volume of data collected.  

bridgetduran
Community Member

I'm reaching out fr a little assistance.  Just set this up for our district, per instructions provided and we are receiving no data.  Set it up twice, but still "nada".  Are there any other network setting that may blocking the data from getting out...possibly web content filter or firewall?  Grasping for straws here, but our Instructional Tech and District Admin need some real clarification on usage.  Any help or suggestions would be awesome.

jperkins
Instructure
Instructure

Bridget, have you already tried using the Google Analytics Debugger - Chrome Web Store  that I mentioned in another comment? That might help you troubleshoot if whether the information is being sent from Canvas, and maybe being stopped by filter or firewall before getting to Google Analytics. Let us know if you are still having issues configuring.

ericmueller
Community Participant

Hi Jeremy,

I’m working on the Google Analytics project with Bridget and using your advice I installed and ran the Analytics Debugger that you recommended.  Have you heard of getting an error called “Uncaught SyntaxError: Invalid or unexpected token” on the first line of the console view?

bneporadny
Community Champion

Hi Eric,

An uncaught syntax error normally means there is an out of place comma, quote, semi colon etc. Below is the code We are using for Google Analytics. You should be able to copy and paste it into your custom javascript‌ file and then just add your key to it. Hope this helps.

////////////////// GOOGLE START /////////////////////
ga('create', 'Your Google UA KEY HERE', 'auto');

//Get User Information
sUserId = ENV["current_user_id"]
ga('set', 'dimension1', sUserId);


//Get User Role
if ($.inArray('admin', ENV['current_user_roles']) == -1 && $.inArray('teacher', ENV['current_user_roles']) == -1 && $.inArray('student', ENV['current_user_roles']) > -1) {
sUserRole = "student"
} else if ($.inArray('admin', ENV['current_user_roles']) == -1 && $.inArray('teacher', ENV['current_user_roles']) > -1) {
sUserRole = "teacher"
} else if ($.inArray('admin', ENV['current_user_roles']) > -1) {
sUserRole = "admin"
} else {
sUserRole = "other"
}

ga('set', 'dimension3', sUserRole);

//If the user is in a course
try {
sTemp = window.location.pathname.match(/\/courses\/(\d+)/);
if (sTemp[1]) {

//Get Course information - Course Name and parent sub-account id
var d1 = $.get('/api/v1/courses/' + sTemp[1], function (_course) {
parent_account = _course.account_id
parent_account = parent_account.toString();
sCourseName = _course.name
});


$.when(d1).done(function (_account) {
// ...do stuff...
ga('set', 'dimension4', sTemp[1]);
ga('set', 'dimension5', sCourseName);
ga('set', 'dimension6', parent_account);
ga('send', 'pageview');
});
} else {
ga('send', 'pageview');
}
} catch (err) {}

// END - Google Analytics Tracking Code

ericmueller
Community Participant

Thanks Brian that solved the syntax error that we were getting.  However, now I got an error on line 2 of the code you sent which defines the tracking ID number. "Uncaught ReferenceError: ga is not defined at google_analytics_new.js:2"

Question: Should I be including the global Site Tag that is generated in Analytics before the code that you supplied?  Or does the code that you supplied take the place of the gtag.js?

bneporadny
Community Champion

Hey Eric,

Just realized it didn't copy everything it was supposed to. Try below and see if it doesn't fix your error.

//In Google Analytics you'll need to set up custom dimensions as follows
// Custom Dimension 1 = Canvas User ID --- Scope = User
// Custom Dimension 2 = Archived --- Scope = User
// Custom Dimension 3 = Canvas User Role --- Scope = User
// Custom Dimension 4 = Canvas Course ID --- Scope = Hit
// Custom Dimension 5 = Canvas Course Name --- Scope = Hit
// Custom Dimension 6 = Canvas Sub-Account ID --- Scope = Hit

(function (i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;
i[r] = i[r] || function () {
(i[r].q = i[r].q || []).push(arguments)
}, i[r].l = 1 * new Date();
a = s.createElement(o),
m = s.getElementsByTagName(o)[0];
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m)
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');

// START - More Google Analytics Tracking Code
var sUserId
var sUserRole
var sTemp // Course ID from URL
var _course
var sCourseName = null
var parent_account //Give you the subaccount_id that the course is in

////////////////// GOOGLE START /////////////////////
ga('create', 'Your Google UA KEY HERE', 'auto');

//Get User Information
sUserId = ENV["current_user_id"]
ga('set', 'dimension1', sUserId);


//Get User Role
if ($.inArray('admin', ENV['current_user_roles']) == -1 && $.inArray('teacher', ENV['current_user_roles']) == -1 && $.inArray('student', ENV['current_user_roles']) > -1) {
sUserRole = "student"
} else if ($.inArray('admin', ENV['current_user_roles']) == -1 && $.inArray('teacher', ENV['current_user_roles']) > -1) {
sUserRole = "teacher"
} else if ($.inArray('admin', ENV['current_user_roles']) > -1) {
sUserRole = "admin"
} else {
sUserRole = "other"
}

ga('set', 'dimension3', sUserRole);

//If the user is in a course
try {
sTemp = window.location.pathname.match(/\/courses\/(\d+)/);
if (sTemp[1]) {

//Get Course information - Course Name and parent sub-account id
var d1 = $.get('/api/v1/courses/' + sTemp[1], function (_course) {
parent_account = _course.account_id
parent_account = parent_account.toString();
sCourseName = _course.name
});


$.when(d1).done(function (_account) {
// ...do stuff...
ga('set', 'dimension4', sTemp[1]);
ga('set', 'dimension5', sCourseName);
ga('set', 'dimension6', parent_account);
ga('send', 'pageview');
});
} else {
ga('send', 'pageview');
}
} catch (err) {}

// END - Google Analytics Tracking Code

ericmueller
Community Participant

Awesomesauce!!!

That's it.  I'm seeing life in the raw data active users view i created in Analytics.

Thank you Brian!!

Boekenoogen
Community Contributor

Every time I am trying to look at the custom Dashboards that I have connected to my GA, I get this error.

275247_pastedImage_1.png

Boekenoogen
Community Contributor

I am using the exact code from the reply above. I only added our GA number.

Boekenoogen
Community Contributor

I missed a step in the process and it looks like it might be working.

Make sure to create the Dimension's first.

ericmueller
Community Participant

I don't know if your a newbie to Google Analytics like I am, but I thought I would share this.  I've been going through this Google Analytics for Beginners and its given me a couple of good ideas.  Google Analytics Academy 

Boekenoogen
Community Contributor

Thanks for the info... I will take a look at the link. 

Boekenoogen
Community Contributor

I can not express enough how excited I am seeing the data come in from Google Analytics. We are seeing information that we should have been tracking for a while, but never had the time to set it up. I will now play with more settings to see how much more data I can draw out.

arjen_visser
Community Member

Hi,

I have a problem I think, because I don't get any data in google analytics. I changed the GA code to mine and than uploaded into canvas. I tried a few times and waited for a few hours, but nothing came up. I am using an bitnami canvas lms hosted in the azure enviroment. Has anyone an idea what is going wrong?

dtod
Community Contributor

Has anyone updated this for gtag.js?

kunal_ashar
Community Participant

Thank you for the integration with GA. How/where do I view the analytics of the users' interaction with the "conversations" route? All I see in my GA dashboard are "courses" routes.
Edit: The script above has a "bug" in that anything that isn't a "course" doesn't fall through into the else clause, and instead gets caught as an exception. A better way would be as follows:

...

sMatches = window.location.pathname.match(/\/courses\/(\d+)/);
    sCourseId = sMatches && sMatches[1];
    if (sCourseId) {
        // Get Course information - Course Name and parent sub-account id

...

s_lynagh
Community Member

Just a quick comment to say that in my case the get call was not working as intended, and needed the 'json' datatype specified as parameter before pageviews were being logged successfully.

We are loading alternate version of jQuery (3.2.1) which may be the reason this failed.

My code from line 58, note the 'json' line at the end: 

var d1 = $.get('/api/v1/courses/' + sTemp[1], function (_course) {
                    parent_account = _course.account_id;
                    parent_account = parent_account.toString();
                    sCourseName = _course.name;
                }, 'json');
jperkins
Instructure
Instructure

I have a fairly big update to this document that will be coming in the near future. I will now probably strip out jquery thanks to your comment. Smiley Happy 

david_summervi1
Community Participant

Hmm, getting no data whatsoever - have followed directions to the letter

jperkins
Instructure
Instructure

 @david_summervi1 ‌, you can download this Google Extension for Chrome to see what events are being sent to Google Analytics. You should see two Google Analytic events fired (a general instructure one, and your new custom one).  If you aren't seeing any data being sent with that tool, try opening up your developer console to see if you are seeing any errors coming through. Otherwise, pm me and I can try and take a look.

jperkins
Instructure
Instructure

A core code change to how Canvas uses Google Analytics was pushed out today. It is possible that it has broken your Google Analytics setup. I've updated the code on this post to get around the issue. Updated Script  

kenneth_rogers
Community Champion

 @jperkins ‌ Thanks for this. My analytics actually stopped the 27th at around noon CDT. What exactly did Canvas change? And why the start of semester for so many people? This is crucial data I'm missing.

Also, now the new code is breaking my other javascript. I'm not a javascript person by any means, so do you have any suggestions? I am using it to put in custom menu items to the global nav and then our student survey tool uses it for a pop-up message (which is obviously very important to a lot of people that it works!).

Thanks again for the note!

dtod
Community Contributor

I think there's a missing semi-colon here:

googleAnalyticsCode("UA-12345678-1") // replace google analytics tracking id here

should be

googleAnalyticsCode("UA-12345678-1"); // replace google analytics tracking id here
kenneth_rogers
Community Champion

Thanks,  @dtod ‌. I had high hopes for this fix! Unfortunately I'm still having issues with the rest of my script.

dtod
Community Contributor

I don't know what your level of knowledge is, but you may be able to find the specific problem in the Developer Console (see images below) by clicking the reference on the right of the screen.

Access Developer Tools

Zero in on errors

jperkins
Instructure
Instructure

 @kenneth_rogers ,

I'm not sure about your other custom Javascript, but the change that Canvas made that broke google analytics was updating Canvas's implementation of it. Both the previous version of this script and the new Canvas one were using the same global google analytics variable for the window. So the only real change in my above script is changing the global analytics variable to be 'custom_ga' instead of just 'ga'.

kenneth_rogers
Community Champion

Thank you, sir!

kenneth_rogers
Community Champion

Thanks for the extra info,  @jperkins ‌. I think at my root of things I was using way old code. I might go back to my code that was working and see about changing the variable you mentioned to see if that works. If not, back to the Community to help write my script! Smiley Wink

bfelix
Community Participant

Our code looked something like this:

(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-12345678-1', 'auto');
ga('send', 'pageview');

We found that adding this line knocked out Canvas' new tracking and therefore was able to send properly to our tracking. Doubtful this is best practice but might tide us over til we set up newer GA approach:

(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('remove');
ga('create', 'UA-66888157-1', 'auto');
ga('send', 'pageview');

Also assume Canvas gets their tracking send successfully first (as the custom JS load is last), so hopefully everyone is happy...

chofer
Community Coach
Community Coach

Hi  @simsrg ...

We just got Google Analytics set up at our Technical College.  Do you know if the Student and Instructor Data Dashboard links you've provided above still work?  I tried adding them just like I did for  @jperkins ' and awilliams' custom Dashboard views, and all I see are flat lines and no data in either the Student or Instructor Data Dashboards.  Might I be missing something?

chofer
Community Coach
Community Coach

We recently got Google Analytics set up at our Technical College.  So cool to see this kind of data for our own Canvas environment!  I found some custom dashboard templates that I added to my Dashboards screen.  You might want to try these out for yourself: 7 Google Analytics Custom Dashboard Examples (To Segment Traffic).

afromero2
Community Participant

Brad, we had the same problem and we lost the tracking from Canvas to Analytics. Thanks for four post. ¡It works for us! .

Regards. 

blantons1
Community Member

Newbie question here - how do I set up Sub-Account ID, Term ID, and Course Role in Tag Manager? What type of variable are these, etc?

cifuentesm2
Community Member

Hey  @jperkins ‌ , our institution just started gathering analytical data into our process to help make data-driven decisions. Is it possible to make a ninth dimension to pull from course_format. I was able to make the javascript to include course_format, but not quite sure how to properly filter out that data on analytics side. Any recommendations? 

dbrace
Community Contributor

Is this document considered to be an official Canvas document?  Does Canvas officially support it and help with any questions or problems?

michaelg
Community Member

Canvas does not support Google Analytics in any way. You are on your own

and you have to rely on other community members' workarounds, etc.

I've had very little success in getting Analytics to work and your CSM

cannot offer any help or support.

Good Luck!

On Tue, May 19, 2020 at 11:12 AM dbrace@lccc.edu <instructure@jiveon.com>

dbrace
Community Contributor

Thank you,  @michaelg .