[User Settings] Printable Roster with Photo Size Selectable

The printable roster is nice but the photos are much too small to be useful. The size of the photos, when available, should be selectable by instructors (e.g., small, medium, large). It would be nice if the rosters with photos could be sorted by groups too (see previous idea: Printable course roster ).

 

I can open the individual photos and they are much larger than they appear in the roster. For example, attached is the Canvas photo from the roster and attached is the Angel photo from the roster (we used to use Angel for classes). You can see the difference in usability.

 

203820_Example Canvas Photo.jpgExample Canvas Photo.jpg203821_Example Angel Photo.jpgExample Angel Photo.jpg

37 Comments
rake_9
Community Champion

Feeling some FOMO, but my instance of Canvas doesn't have the Print Roster button or the dropdown to control how many entries show per page.  Are those Admin settings we missed? 

We also don't have the checkboxes for which picture to show, but I'm betting that is a customization.

smg36
Community Novice
Author

Hi Valerie,

I know very little about the customization process so someone else will have to speak to your question. However, I do know that the availability of photos at all, avatars, and certain aspects of the roster are things that were set by people who administer and support Canvas. I know this because I inquired about the photo size option within our university first.

maguire
Community Champion

Inspired by the Jive posting, I wrote a little bit of python code to get the information about the users in a course and output this as a CSV file. Then I use a little bit of VBA in Excel to import the user's images and size them. The images are placed so that you can sort the users and the images sort with the users as expected. Perhaps it is not as nice as a button, but might suit some of the functions that people want.

VBA code (inspired by the response by "teylyn" Apr 18 '13 at 21:11 on the page http://superuser.com/questions/584650/how-do-i-load-external-images-into-excel and enhanced with help from http://stackoverflow.com/questions/12936646/how-to-insert-a-picture-into-excel-at-a-specified-cell-p... )http://stackoverflow.com/questions/12936646/how-to-insert-a-picture-into-excel-at-a-specified-cell-p...:

Sub InsertPicturesViaURL()

  For Each cel In Selection

  cel.Offset(0, 1).Select

  ActiveSheet.Pictures.Insert(cel.Value).Select

  Selection.Placement = 2 'xlMove

  Selection.ShapeRange.LockAspectRatio = msoTrue

  Selection.ShapeRange.Height = 100

  Selection.PrintObject = True

  Next cel

End Sub

python code:

#!/usr/bin/python3.4

# -*- coding: utf-8 -*-

#

# ./users-in-course.py course_id

#

# outputs a CSV file with column headings = ['user_id', 'role', 'course section', 'sort last_name', 'sort first_name', 'e-mail', 'html_url', 'picture']

#

# Import (using the Text IMport Wizard)  into a blank Excel spreadsheet using the Data->Get External Data->From Text.

#  Make sure to specify that the import and specify that the File Origin is "UTF-8".

#

# If you want to sort by section number or some other fields, do so now.

#

# In Excel one can selected the column of picture (actually the URL to the user's icon/picture, then you can select this column and they use the following Vistual Basic

# subroutine:

#  Sub InsertPicturesViaURL()

#    For Each cel In Selection

#        cel.Offset(0, 1).Select

#        ActiveSheet.Pictures.Insert(cel.Value).Select

#    Next cel

#  End Sub

#

# The above will insert the pictures. Now you can use the Excel menu "Find & Select" -> "Go To Special" choose "Object". This selects all of the picture objects,

# now you can use the "Picture Tools" menu to set the size of the images (for example to 1 cm). Now use the Cells Format menu to set the row height to 75.

# You now have small versions of the picture in spreadsheet.

#

# Note that since the actual graphical images are in an overlay, they will _not_ sort with the entries in the spreadsheet.

#

# G. Q. Maguire Jr.

#

# 2016.11.26

#

import csv, requests, time

from pprint import pprint

import optparse

import sys

from io import StringIO, BytesIO

import json

#############################

###### EDIT THIS STUFF ######

#############################

# styled based upon https://martin-thoma.com/configuration-files-in-python/

with open('config.json') as json_data_file:

       configuration = json.load(json_data_file)

       access_token=configuration["canvas"]["access_token"]

       baseUrl="https://"+configuration["canvas"]["host"]+"/api/v1/courses/"

       baseUrluser="https://"+configuration["canvas"]["host"]+"/api/v1/users/"

modules_csv = 'modules.csv' # name of file storing module names

log_file = 'log.txt' # a log file. it will log things

header = {'Authorization' : 'Bearer ' + access_token}

payload = {}

##############################################################################

## ONLY update the code below if you are experimenting with other API calls ##

##############################################################################

def write_to_log(message):

       with open(log_file, 'a') as log:

              log.write(message + "\n")

              pprint(message)

def sections_in_course(course_id):

       sections_found_thus_far=[]

       # Use the Canvas API to get the list of section for this course

       #GET /api/v1/courses/:course_id/sections

       url = baseUrl + '%s/sections' % (course_id)

       if Verbose_Flag:

              print("url: " + url)

       r = requests.get(url, headers = header)

       if Verbose_Flag:

              write_to_log("result of getting sections: " + r.text)

       if r.status_code == requests.codes.ok:

              page_response=r.json()

       for p_response in page_response: 

              sections_found_thus_far.append(p_response)

       # the following is needed when the reponse has been paginated

       # i.e., when the response is split into pieces - each returning only some of the list of modules

       # see "Handling Pagination" - Discussion created by tyler.clair@usu.edu on Apr 27, 2015, https://community.canvaslms.com/thread/1500

       while r.links['current']['url'] != r.links['last']['url']: 

              r = requests.get(r.links['next']['url'], headers=header) 

              page_response = r.json() 

              for p_response in page_response: 

                     sections_found_thus_far.append(p_response)

       return sections_found_thus_far

# create the following dict to use as an associate directory about users

selected_user_data={}

user_found_thus_far=[]

def users_in_course(course_id):

       global user_found_thus_far

       # Use the Canvas API to get the list of users enrolled in this course

       #GET /api/v1/courses/:course_id/enrollments

       url = baseUrl + '%s/enrollments' % (course_id)

       if Verbose_Flag:

              print("url: " + url)

       r = requests.get(url, headers = header)

       if Verbose_Flag:

              write_to_log("result of getting enrollments: " + r.text)

       if r.status_code == requests.codes.ok:

              page_response=r.json()

       for p_response in page_response: 

              user_found_thus_far.append(p_response)

       # the following is needed when the reponse has been paginated

       # i.e., when the response is split into pieces - each returning only some of the list of modules

       # see "Handling Pagination" - Discussion created by tyler.clair@usu.edu on Apr 27, 2015, https://community.canvaslms.com/thread/1500

       while r.links['current']['url'] != r.links['last']['url']: 

              r = requests.get(r.links['next']['url'], headers=header) 

              page_response = r.json() 

              for p_response in page_response: 

                     user_found_thus_far.append(p_response)

def user_profile_url(user_id):

       # Use the Canvas API to get the profile of a user

       #GET /api/v1/users/:user_id/profile

       url = baseUrluser + '%s/profile' % (user_id)

       if Verbose_Flag:

              print("user url: " + url)

       r = requests.get(url, headers = header)

       if Verbose_Flag:

              write_to_log("result of getting enrollments: " + r.text)

       if r.status_code == requests.codes.ok:

              page_response=r.json()

       return page_response

sections_info=[]

def section_name_from_section_id(section_id):

       global sections_info

       for i in sections_info:

            if i['id'] == section_id:

                   return i['name']

def output_spreadsheet_of_users_in_a_course(course_id):

       global sections_info

       sections_info=sections_in_course(course_id)

       if Verbose_Flag:

              print("sections_info: {}".format(sections_info))

       users_in_course(course_id)

       output_file  = open('enrollment-in-'+course_id+'.csv', "wb")

       print ("Name of the file: ", output_file.name)

       #output column headings

       headings = ['user_id', 'role', 'course section', 'sort last_name', 'sort first_name', 'e-mail', 'html_url', 'picture']

       for h in headings:

              output_file.write(h.encode())

              output_file.write(",".encode())

       output_file.write("\n".encode())

       if Verbose_Flag:

              print("user_found_thus_far: {}".format(user_found_thus_far))

       for user in user_found_thus_far:

              if Verbose_Flag:

                     print("user: {}".format(user))

                     print("user: {}".format(user['user_id']))

              user_id=user['user_id']

              if Verbose_Flag:

                     print("user_id: {}".format(user_id))

              output_file.write(str(user['user_id']).encode())

              output_file.write(",".encode())

              output_file.write(user['role'].encode())

              output_file.write(",".encode())

              output_file.write(section_name_from_section_id(user['course_section_id']).encode())

              output_file.write(",".encode())

              output_file.write(user['user']['sortable_name'].encode())

              output_file.write(",".encode())

              output_file.write(user['user']['login_id'].encode())

              output_file.write(",".encode())

              output_file.write(user['html_url'].encode())

              output_file.write(",".encode())

              profiles=user_profile_url(user_id)

              if Verbose_Flag:

                     print("profiles: {}".format(profiles['avatar_url']))

              output_file.write(profiles['avatar_url'].encode())

              output_file.write("\n".encode())

                    

       output_file.close()

def main():

       global Verbose_Flag

       parser = optparse.OptionParser()

       parser.add_option('-v', '--verbose',

                         dest="verbose",

                         default=False,

                         action="store_true",

                         help="Print lots of output to stdout"

       )

       options, remainder = parser.parse_args()

       Verbose_Flag=options.verbose

       if Verbose_Flag:

              print('ARGV      :', sys.argv[1:])

              print('VERBOSE   :', options.verbose)

              print('REMAINING :', remainder)

       # add time stamp to log file

       log_time = str(time.asctime(time.localtime(time.time())))

       write_to_log(log_time)  

       if (len(remainder) < 1):

              print("Inusffient arguments\n must provide course_id course_code module filename.html\n")

       else:

              output=output_spreadsheet_of_users_in_a_course(remainder[0])

              if (output):

                     print(output)

       # add time stamp to log file

       log_time = str(time.asctime(time.localtime(time.time())))

       write_to_log(log_time)  

       write_to_log("\n--DONE--\n\n")

if __name__ == "__main__": main()

smg36
Community Novice
Author

Wow, this is wonderful. Thank you for taking the time to create and share this. I clearly need to develop clue in VBA and Python so I can take advantage of this.

curtain
Community Participant

At Kansas State we have also developed our own Photo Roster LTI tool to include in courses that allows for printable rosters with customization options such as what info to include and larger photos.  It also includes information about the students that is not available in Canvas such as their major/degree program, their year in school, etc.

We would love to be able to retire this for a more 'complete' Roster tool in Canvas

scottdennis
Instructure
Instructure

Anyone interested in this topic should check out: https://community.canvaslms.com/events/1908

Smiley Happy

maguire
Community Champion

I have made an enhanced python program that not only directly produces the spreadsheet (eliminating the need to use VBA), but also allows you to choose whether or not you want pictures (from the user's avatar) of each user (if you do include pictures you can set their size in pixels).)

This version also includes section information, so that you can easily sort and produce per section lists.

For further details and code see Enhanced list of users in a course: Chip sandbox

mmccain
Community Novice

A photo roster with usable sized pictures would help everyone.  I think the point of the pictures is so that we can recognize our students.  The current size of the pictures does nothing for anyone.

maguire
Community Champion

The code that I put at Enhanced list of users in a course: Chip sandbox  allows you to set the size of the pictures in pixels. Is this not sufficient?

mmccain
Community Novice


Thanks Gerald.  Unfortunately, I know nothing about Python or Sandboxing.   Also, I'm on a Mac.  Not sure if that makes a difference.  I was hoping we would have something built into Canvas soon.  But I do appreciate you taking the time to inform me.