Pass user photo to Brightspace

James.Mueller9674
James.Mueller9674 Posts: 8 🌱
edited November 2022 in Development

We would like to set the Brightspace profile photos for our users using our photos from their ID cards. Is there a way using file transfer or the API to pass the photos? Has someone else done this before? We'd like to then prevent them from changing it (we have an approval process in place for the photos already in our SIS).

Tagged:

Answers

  • David Wag207
    David Wag207 Posts: 17
    edited November 2022

    Hey James,

     

    Using the Profile Image API you can update the profile image for a specified user. The API uses our simple file upload process to upload the image. Here is a link to the details for the API:

     

    http://docs.valence.desire2learn.com/res/user.html#post--d2l-api-lp-(version)-profile-user-(userId)-image

     

    Here is a link describing the simple file upload process:

    http://docs.valence.desire2learn.com/basic/fileupload.html#simple-uploads

     

    Hope that helps,

    Dave

     

  • David Wag207
    David Wag207 Posts: 17
    edited November 2022

    No problem, feel free to ask questions as you start working your way through getting an API call up and running. A great starting point for your use case might be the following article:

     

    https://community.brightspace.com/s/article/ka1610000000pcYAAQ/API-Cookbook-Headless-Non-Interactive-Web-Service-Workflow

     

    It does a walk through creating a back-end service to make API calls using the id-key authentication system (http://docs.valence.desire2learn.com/basic/auth.html). In addition to that an example was created for retrieving the Brightspace Data Sets using a background service with OAuth 2.0(http://docs.valence.desire2learn.com/basic/oauth2.html) authentication model:

     

    https://community.brightspace.com/s/article/ka1610000000poFAAQ/Brightspace-Data-Sets-Headless-Non-Interactive-Client-Example

     

    Hope that helps,

    Dave

  • James.Mueller9674
    James.Mueller9674 Posts: 8 🌱
    edited November 2022

    Thanks David. Looks like I'll have to learn the API. We are using IPSIS as the only communication with Brightspace at the moment.

  • James.Mueller9674
    James.Mueller9674 Posts: 8 🌱
    edited November 2022

    I admit I've been mostly liker a fish out of water on this. The help stuff expects a level of knowledge I don't have. I have fumbled my way further, but I'm unable to get a user other than the API user.

     

    What I don't understand (and couldn't find in the documentation) was how to pass parameters to the commands

     

    For example, I want to get the UserID for a user based off of their OrgDefinedID.

     

    I see that's a parameter for the get:

    GET /d2l/api/lp/(version)/users/

     

    I'm using the API Test tool as a bases and can't get this to work (because I'm missing something)

     

    app_creds = { 'app_id': my_app_id, 'app_key': my_app_key }

    ac = d2lauth.fashion_app_context(app_id=app_creds['app_id'], app_key=app_creds['app_key'])

     

    auth_url = ac.create_url_for_authentication(my_lms, 'http://localhost:8080')

    redirect_url = 'http://localhost:8080' + my_returned_url

     

    uc = ac.create_user_context(result_uri=redirect_url, host=my_lms, encrypt_requests=True)

     

    api_command = '/d2l/api/lp/1.0/users/?orgDefinedId=1'

    url = uc.create_authenticated_url(api_command)

    I get

     File "<stdin>", line 1, in <module>

     File "C:\Program Files\Python36\lib\site-packages\d2lvalence\auth.py", line 486, in create_authenticated_url

      method=method),

     File "C:\Program Files\Python36\lib\site-packages\d2lvalence\auth.py", line 414, in _build_tokens_for_path

      raise ValueError("path contains invalid characters for URL path")

    ValueError: path contains invalid characters for URL path

    I've tried ampersands and questions. The help document isn't specifying how to

  • David Wag207
    David Wag207 Posts: 17
    edited November 2022

    Hey @James Mueller​,

     

    I'm not an expert in python, but I wonder if the query parameter should be added after you've created the authenticated URL instead of before. Here is some example python calling the same route, that adds the query parameter after it's authenticated the URL.

     

    http://docs.valence.desire2learn.com/samples/permissionCheck.html#id3

     

    Hope that helps,

    Dave

     

  • James.Mueller9674
    James.Mueller9674 Posts: 8 🌱
    edited November 2022

    David, that's exactly what I needed!

  • James.Mueller9674
    James.Mueller9674 Posts: 8 🌱
    edited November 2022

    Here's out python script (with our info stripped out someone would need to plug in their options. This is designed to be called one by one as new photos come in.

     

    import sys

    import requests

    import d2lvalence.auth as d2lauth

    import json

     

    def main():

     if len(sys.argv) - 1 == 0:

       sys.exit('You must provide a Student ID')

     else:

       # Set the ID into a variable

       sis_id = sys.argv[1]

     

       # Create the link to the image file

       stu_img= '//server/directory/' + sis_id + '.jpg'

     

       # Set the basic variables that we need

       # The LMS url address (minues the access method)

       my_lms = 'yourschool.brightspace.com'

       my_return_server = 'http:///localhost:8080'

     

       # These come from using the interactive API Test Tool https://apitesttool.desire2learnvalence.com/

       # need app_id, app_key

       my_app_id = ''

       my_app_key = ''

     

       # After authenticating using the Application user you'll get the resulting URL tail which is our user verification data

       my_returned_url = ''

     

       # Create the Credentials we need to connect and the Application's context we are authorizing with

       my_app_creds = { 'app_id': my_app_id, 'app_key': my_app_key }

       my_app_context = d2lauth.fashion_app_context(app_id=my_app_creds['app_id'], app_key=my_app_creds['app_key'])

     

       # Get the URLS we need for the commands

       my_auth_url = my_app_context.create_url_for_authentication( my_lms, my_return_server )

       my_redirect_url = my_return_server + my_returned_url

     

       # Create our User Context to handle the requests

       my_user_context = my_app_context.create_user_context(result_uri=my_redirect_url, host=my_lms, encrypt_requests=True)

     

     

       # Run our commands

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

     

       # We need to get the D2L User ID (UserId) from their SIS id number

       api_command = '/d2l/api/lp/1.9/users/'

       query_params = {'orgDefinedID':sis_id}

       api_url = my_user_context.create_authenticated_url(api_command)

     

       # Request the data

       query_result = requests.get(api_url, params=query_params)

     

       if query_result.status_code != 200:

         sys.exit( 'Info:   ' + sis_id + ' does not exist in Brightspace' )

     

       # We get back a JSON object, get the UserId from it and convert it to a string datatype

       d2l_user_id = str(query_result.json()[0]['UserId'])

     

       # Set the command to pass the photo

       upload_command = '/d2l/api/lp/1.9/profile/user/' + d2l_user_id + '/image'

     

       # Create the payload for our image to upload

       upload_content = { 'profileImage': ('profileImage.jpg', open( stu_img, 'rb' ) ) }

     

       # Get it properly configured to be a post to Brightspace

       upload_url = my_user_context.create_authenticated_url( upload_command , "POST" )

     

       # Post it

       upload_response = requests.post( upload_url, files=upload_content )

     

       if upload_response.status_code != 200:

         sys.exit( 'Error:  ' + sis_id + ' ' + upload_response.text )

     

       sys.exit ( '        ' + sis_id + ' uploaded' )

     

    if __name__ == "__main__":

     main()

  • James.Mueller9674
    James.Mueller9674 Posts: 8 🌱
    edited November 2022

    I thought I'd also share a version that's self contained that searches a certain folder for files changed in the last few days and passes them up. Works faster for small directories or cases where multiple images are passed at once.

     

    import os

    import os.path

    import datetime

    import re

    import requests

    import d2lvalence.auth as d2lauth

    import json

     

    def main():

     # Get list of IDs

     print('Getting Recent Files')

     my_path = '///server//folder'

     filter_date = datetime.datetime.now() - datetime.timedelta(days=2) #2 days ago

     pattern = re.compile("^i\d{5,6}.jpg") #starts with i, has 5-6 digits, ends with .jpg change as needed

     

     # Create our empty list

     id_list = []

     

     # Search for those recent files

     for root, dirs, files in os.walk(my_path):

       for file_name in files:

         if pattern.match(file_name):

           # The file matched our regex, now find out if it was a recent file

           path = os.path.join(root, file_name)

           st = os.stat(path)

           mtime = datetime.datetime.fromtimestamp(st.st_mtime)

           if mtime > filter_date:

             # Recent file, put it in our array, stripping the 'i' and '.jpg' off (leaving just the id)

             id_list.append(file_name[1:-4])

     

     if len(id_list) > 0:

       # We had at least one id in our array

       print('Passing images for')

       print(id_list)

       pass_images( id_list )

     else:

       print('No files to pass')

     

     

    def pass_images( in_list ):

     # Set the basic variables that we need

     # The LMS url address (minues the access method)

     my_lms = 'yourschool.brightspace.com'

     my_return_server = 'http:///localhost:8080'

     

     # These come from using the interactive API Test Tool https://apitesttool.desire2learnvalence.com/

     # need app_id, app_key

     my_app_id = ''

     my_app_key = ''

     

     # After authenticating using the InfoCentral user you'll get the resulting URL tail which is our user verification data

     my_returned_url = ''

     

     # Create the Credentials we need to connect and the Application's context we are authorizing with

     my_app_creds = { 'app_id': my_app_id, 'app_key': my_app_key }

     my_app_context = d2lauth.fashion_app_context(app_id=my_app_creds['app_id'], app_key=my_app_creds['app_key'])

     

     # Get the URLS we need for the commands

     my_auth_url = my_app_context.create_url_for_authentication( my_lms, my_return_server )

     my_redirect_url = my_return_server + my_returned_url

     

     # Create our User Context to handle the requests

     my_user_context = my_app_context.create_user_context(result_uri=my_redirect_url, host=my_lms, encrypt_requests=True)

     

     # Pass the photos

     for sis_id in in_list:

       # We need to get the D2L User ID (UserId) from their CX id number

       api_command = '/d2l/api/lp/1.9/users/'

       query_params = {'orgDefinedID':sis_id}

       api_url = my_user_context.create_authenticated_url(api_command)

     

       # Request the data

       query_result = requests.get(api_url, params=query_params)

     

       if query_result.status_code != 200:

         print( 'Info:   ' + sis_id + ' does not exist in Brightspace' )

         continue

     

       # We get back a JSON object, get the UserId from it and convert it to a string datatype

       d2l_user_id = str(query_result.json()[0]['UserId'])

     

       # Set the command to pass the photo

       upload_command = '/d2l/api/lp/1.9/profile/user/' + d2l_user_id + '/image'

     

       # Create the payload with our image to upload

       gu_img = '//server/folder/i' + sis_id + '.jpg'

       upload_content = { 'profileImage': ('profileImage.jpg', open( gu_img , 'rb' ) ) }

     

       # Get it properly configured to be a post to Brightspace

       upload_url = my_user_context.create_authenticated_url( upload_command , "POST" )

     

       # Post it

       upload_response = requests.post( upload_url, files=upload_content )

     

       if upload_response.status_code != 200:

         print( 'Error:  ' + sis_id + ' ' + upload_response.text )

         continue

     

       print( '        ' + sis_id + ' uploaded' )

     print( 'Done uploading' )

     

    if __name__ == "__main__":

     main()

  • David Wag207
    David Wag207 Posts: 17
    edited November 2022

    Hey @James Mueller​,

     

    Thanks you for sharing your solution. I'm sure it will be helpful for people attempting to do the same thing in the future!

     

    Appreciated,

    Dave

  • Renee.J.194
    Renee.J.194 Posts: 119
    edited November 2022

    @_David _Wagler​ and @James Mueller​ , we are trying to set up a profile pic upload as well. We do not want to replace any existing profile the user may have uploaded, we just want to add an image if there isn't one.

    All I can see to do would be to get the profile image and use the status code to know which students need to receive a profile image because they don't have one.

    Do you have a better way to handle the already-exists-don't-replace situation?

  • James.Mueller9674
    James.Mueller9674 Posts: 8 🌱
    edited November 2022

    I can't think of a better way to do it.

  • Rich.McCourt3946
    Rich.McCourt3946 Posts: 2 🌱
    edited November 2022

    Ok, some help please. I'm not close to being a developer, more of just a hack.

     

    I'm trying to create a script to upload student photos to our brightspace tenant. I have a folder with photos of each student named (#######.jpg) where the ####### is the OrgDefinedID.

     

    I am trying to get the script above that searches a folder and uploads the pics functioning, but I can't get past authentication (surprise).

     

    What I've done:

    1. Created an 'administrative' service account in my tenant.
    2. Created an appId/Key using manage Extensibility in my tenant
    3. Used the https://apitesttool.desire2learnvalence.com to log in using the above two items. I am able to perform the /d2l/api/lp/1.0/users/whoami successfully and it returns the administrative service account.
    4. I then adjusted the appId/Key page to trust https://localhost:8080 (added the s to http in the script as well)
    5. I've pulled down the script to my utility server and pip installed the modules needed.
      1. I've customized the script (slightly) to point at the correct folder for photos (currently contains one image for testing) and adjusted the regex.
      2. I've added my_app_id and my_app_key
    6. I don't know what to put in the script for my_returned_url.
      1. Tried blank - fail
      2. Tried '/' - fail
      3. Tried /?x_aXXXX&x_bXXXX&x_cXXXX - fail (but got further)
      4. Tried /?x_aXXXX&x_bXXXX - fail
      5. Tried /index.php (Hail Mary) - fail

     

    Thoughts? Next tries?

     

    Thanks in advance!

  • James.Mueller9674
    James.Mueller9674 Posts: 8 🌱
    edited November 2022

    Rich,

     

    You get that return string from the API Test tool. Make sure that the API Test Tool address below is the trusted URL in your apps properties in Brightspace.

     

    Go to https://apitesttool.desire2learnvalence.com

    put in your Brighstpace instance

    put in your app ID and app key

     

    Then click "Authenticate" and login with the user who is going to be doing the work on the Brightspace side (I created a new user in Brightspace just to do ours)

     

    After you log in successfully you should be asked to grant it permission, test to make sure you can do a WhoAmI

     

    Now for the thing you are needing the "my_return_url".

     

    Look up at the URL in the web browser. You'll see https://apitesttool.desire2learnvalence.com/ and then the "?" followed by a really long code. That is your returned URL. You'll need the whole thing starting with the "?"

     

     

  • Rich.McCourt3946
    Rich.McCourt3946 Posts: 2 🌱
    edited November 2022

    James - Thank you. I had an extra / in the URL that was creating all sorts of havoc. You're note to start with the '?' pointed me in the right direction.

  • Maurice.B.220
    Maurice.B.220 Posts: 1
    edited November 2022

    @Rich McCourt​  Hi Rich, im looking at doing the exact same, your steps above are really useful . Would you be free for a quick chat by any chance? I'd like some advice with the setup.