Skip navigation
All Places > Canvas Developers > Blog > 2016 > October
2016
Tyler Clair

Sections to Groups Script

Posted by Tyler Clair Oct 26, 2016

From a related post I was tagged in, the question was asked how to automatically create groups based off of sections in a course, Automatic Group creation for students in same section. I took some time and wrote up a script in Python 3 that based off of a course ID and group category name it will do the following:

 

  1. Create the Group category with the name you specified
  2. Get a list of sections in a course along with the list of students in each section
  3. Create groups based off of those sections
  4. Create the membership for each student in the group

 

The one thing that this doesn't do is maintain the groups if a student enrolls in a course or if a student is moved to a different section. Those tasks will have to be done manually but when initially ran it will be fairly easy to make those small changes from within Canvas.

 

I have posted the code on my github page and it can be access here: https://github.com/tylerclair/canvas_admin_scripts/blob/master/sections_to_groups.py

@Kelley L. Meeusen thought the functionality below could be of interest to Canvas developers. View the original thread here: https://community.canvaslms.com/ideas/4541?commentID=66234&et=notification.mention#comment-66234

 

Our instructors at the University of Agder in Norway were used to telling their students to put a bin outside the students group room if they needed help from the instructor. They wanted a similar behavior in Canvas. One could argue that this is a better solution than having the instructor subscribe to all the group discussions, especially since an instructor might have many groups in large classes. But being able to do both things in native Canvas would of course be best.

 

We solved the “bin” feature using custom js/css in Canvas. It is a small piece of a larger open source custom Canvas design which is desribed here: http://matematikk-mooc.github.io/

 

The main javascript code for the “bin” solution can be found in this file:

 

https://github.com/matematikk-mooc/frontend/blob/master/src/js/modules/menu.js

 

Just search for showDiscussionGroupMenu in that file. The code is depending on other files in https://github.com/matematikk-mooc/frontend, but a javascript programmer should be able to extract the functionality in a smaller set of custom css/js files.

The images below is taken from a students group discussion view. When the student group needs help, one of the student presses the "Tilkall veileder" button (notify instructor). This in turn leads to the steps shown below.

binbehaviour.png

A best practice you should adopt is storing sensitive information used in scripts in config files rather than directly in the scripts. This helps ensure a few important things:

  1. It is easier for you to make changes when you only have to change one file rather than multiple files.
  2. You can reference different configuration items like the production, beta, and test URLs of your Canvas instance. In my configuration file I have tokens for different roles and permissions as well as the different instance URLs.
  3. You are less likely to accidentally leak sensitive information. This is very important when you use a version control system, such as GitHub to share or archive your scripts. Unless you pay for a Github subscription to have private repositories, your code is publicly visible. There are people and groups constantly scanning Github and other publicly available version control systems for API tokens, keys, secrets, usernames, and passwords for exploitation. Luckily most version control systems include a way to ignore or prevent certain files from being uploaded to a repository. So you will want to add any config files containing sensitive information to that ignore list.

 

I use Python 3 as my go to scripting environment and a few years ago I adopted the use of configuration files using the configparser module https://docs.python.org/3/library/configparser.html. One of the greatest strengths that configparser has is the ability to separate key value pairs in sections. In my config file I have an instance and an auth section. The instance section contains the different Canvas instance URLs appropriately named prod, beta, and test.

 

[instance]
prod = https://abc.instructure.com
beta = https://abc.beta.instructure.com
test = https://abc.test.instructure.com
catalog = https://catalog.abc.edu
[auth]
token = 1234567890……
teacher_token = 0987654321…..
student_token = 9045387230…..

 

You can name this anything you want, have any extension you want, and place it anywhere as well, e.g: config.ini, config/secrets.cfg. If you manage multiple instances you could create separate config files for each instance containing the different URLs and any tokens you need access to. In the event that a config file may be leaked or exposed, it will be isolated to that one file.

 

The code below is how I reference the config file, it makes it very convenient to drop this block of code into a new python file and I am ready to make some API calls.

 

from configparser import ConfigParser
config = ConfigParser()
config.read('config/config.ini')
token = config.get('auth', 'token')
domain = config.get('instance', 'prod')
headers = {'Authorization': 'Bearer {}'.format(token)}

In .NET LTI Project - Part 1 - Connect to Canvas we got an app wired into Canvas, but it really doesn't do anything yet.  It was a simple exercise in how to configure Canvas to recognize your application.

 

The next step is to understand the launch request.  When a user clicks on the link in Canvas to access your application, Canvas is going to send a request to your web server with a bunch of variables.  Let's take a look at those variables and what they mean.

 

You might want to take tag this document from IMS Global as a point of reference:

IMS Global Learning Tools Interoperability™ Basic LTIv1 Implementation Guide

 

Inspect Your Launch Request

If you're using the tutorial from Part 1, go to you HelloWorld controller and place a break point on the return view(); statement.  Attach to the w3wp.exe process to start debugging and click the ltiDemo link from the admin navigation panel.

 

Once you have caught the break point, add the following watch variable for Request.Form.AllKeys

form-keys.png

Place break point on the return view(); statement in the Index() method, and compare the list of variables you receive there.  Basically you are comparing the difference between the data you receive when you launch from the admin navigation panel vs. the course navigation panel.

 

You should see a difference in the variables associated with the course launch request vs. the admin launch request.  Many of the variables are the same, but there are differences.

The standard LTI variables are defined in the IMS Global documentation here:

IMS Global Learning Tools Interoperability™ Basic LTIv1 Implementation Guide

Of great importance are the oauth_* variables, which are critical for authentication.  We'll get into these when we tackle the oauth piece of the puzzle.

 

There are also "custom" Canvas variables, which you can research here:

LTI Variable Substitutions - Canvas LMS REST API Documentation

The Canvas API document uses "dot" notation instead of underscores.  For example, the Request.Form variable is "custom_canvas_api_domain", and in the API document it is "Canvas.api.domain".

 

Take a look at both the IMS Global document and the Canvas API document and think about how each of these variables could be of use to you as you identify users, their roles, and the courses the parts of Canvas they are requesting from.

  • How can you use these variables to enforce different levels of user access in your app?
  • Which variables would you use to expose a set of features in your app to a student vs. an instructor?
  • How would you make sure you don't push an update to production from a launch request received from beta, or test?  Or the other way around?

 

Conclusion

Take a few minutes to launch your app from different locations in the Canvas UI, and note the differences in the variables that you receive.  Also take a look at the values.  This is also a good opportunity to think about how you want to parse these values out of the Request object for lookup in other parts of  your application.  A dictionary could be a useful way to allow for easy lookup of these variables later.  Or maybe you prefer a dynamic object for easy serialization to/from json.  How you store these variables is important as you start to think about how you are going to manage your session state.

 

Food for thought:

If a user has multiple tabs open and launches multiple instances of your LTI app, how are you going to keep track of which course they are launching from and make sure you don't mix course information?  Definitely something to start thinking about.

 

Something like MemCached might be useful.

If you do not use cloud services, I have used this product on my local machine in the past: NoSQL Database | Couchbase

 

Ok, so we're getting to the point where we'll need to attack oauth.

I'll try to work on a walk through that makes sense.

 

Part 3 is published here:  .NET LTI Project - Part 3 - OAuth

A basic Visual Studio project is published here:  LTI Demo Project