Badgr - Badging Support

Note: This is my first Open edX implementation.

I’m looking to implement badging to my platform, and from what I can find on the OpenedX documentation, Badgr was / is baked in. There are variables that can be set in the yml files such as “ENABLE_OPENBADGES”.

When going through the documentation about configuring Badgr and setting up a server, all of the links appear to be broken. As I look into things, I see that they have changed into Canvas Credentials, and there is no information readily available there either.

My question then is, where should I be looking to get things going? I have access to the Badgr Backpack, but I see no clear direction on where to go now that Badgr is Canvas Credentials.

Has anyone successfully used Badgr with OpenedX recently? Or is there another OpenBadges implementation that I should be looking into?

Thanks!

Hi @MrJohnson-SixNinesIT and welcome :wave:

I don’t know anything about Badgr and I won’t be able to help with follow-up questions. However I remember seeing a post about Badgr in June: Backporting Badgr integration updates to Lilac

It looks like it was merged to Maple and working then. Are you using Nutmeg and finding it broken? Or are you using a release earlier than Maple?

Hi @MrJohnson-SixNinesIT

We’ve been using badgr in Lilac and Maple, haven’t tested with Nutmeg yet but as far as I can tell the API endpoints (at least the ones that edx-platform is using) haven’t been changed.

When going through the documentation about configuring Badgr and setting up a server, all of the links appear to be broken.

You are right, seems that the Badgr docs have moved, that is news to me as well.

My question then is, where should I be looking to get things going? I have access to the Badgr Backpack, but I see no clear direction on where to go now that Badgr is Canvas Credentials.

I have no experience with Badgr Backpack but I think it’s for displaying earned badges, it could be useful for testing your integration.

For your Open edX platform to be able to award badges, you’ll need to create an “Issuer App” in Badgr. I believe the relevant docs are now here:

From that Issuer you should be able to retrieve the following settings for your Open edX platform:

BADGR_USERNAME: ""
BADGR_PASSWORD: ""
BADGR_BASE_URL: ""
BADGR_ISSUER_SLUG: ""

And as our docs suggest, for badges to work properly on your platform you’ll need to have certificates enabled and you’ll need to run migrations after enabling badges.

Linking this post for reference here as well since it contains an example tutor plugin.

I hope this helps :slight_smile:

2 Likes

Hello Sarina,

Thanks for the response. We are using Nutmeg. My questions were more around that we did not see a clear path on the typical way of moving forward, given the links were broken and that Badgr now seems to be a paid feature.

Hello @mrtmm, thank you for the reply.I will look into setting things up as an issuer and giving it a go.

As an aside, is Badgr the only Badging system supported by OpenedX? I see that after a certain number of users (500) Badr requires an enterprise account. There are any free alternatives that can be integrated in a similar fashion to Badgr?

@mrtmm I created an Issuer on the badgr.com. I also created a badge and a group. The disconnect I am having is, where do I get the badgr base URL and slug from? Talking about from within the badgr platform.

As I navigate badgr.com while logged in, I am in the issuer section, is the baseURL talking about Badgr.com? For example, I have this URL for navigating to my badges: Digital Credential Network Powered by Badgr Pro

Would it look like this?:
BADGR_BASE_URL = “https://badgr.com
BADGR_ISSUER_SLUG=“6334700065ef7361326061d6”

And that username and password be the username and password in which I used to sign up / login?

Edit: had a copy + paste error in slug.

Hi @MrJohnson-SixNinesIT

Badgr is currently integrated and supported but you can integrate some other systems as well, see the docs. I guess you’ll need to run a patch on your edx-platform for that.

I believe there is (or at least used to be) an option to host your own Badgr server. But similarly to the docs, seems like things have changed. There used to be a repo at https://github.com/concentricsky/badgr-server but it’s gone now. I do see a fork of it at GitHub - ffedoroff/badgr-server: Open Badge issuing and management with Django but I really don’t know much more about it. Once upon a time I spun it up locally for brief testing following the README but that’s it.

I believe the base url should point to an API, so it should something like https://api.test.badgr.com or https://api.badgr.io or https://api.eu.badgr.io but I am not sure where this is configured exactly and which is the correct one for you.

That seems about right

Yes, I believe so.

You should be able to confirm the values by testing the token creation.

curl -X POST '{BADGR_BASE_URL}/o/token' -d "username={BADGR_USERNAME}&password={BADGR_PASSWORD}"

If you can create a token with these values, so can your LMS.

@mrtmm Thanks for the awesome and quick replies.

We were able to successfully access the user, and from the standpoint of the admin page (mydeployment.com/admin), we were able to create a badge class that was able to successfully call the Badgr api.

We also made a Course Event Badge Configuration and applied the badge class to it (in the admin page). How are badges managed inside of the non-admin portal? Are we supposed to see configuration options for them when creating a course?

I’m not having any luck on finding someone going through and setting up badges or anything on like YouTube either. It is difficult to tell if the problem is on our end. Are there more things we need to do in the admin page? Is there some Advanced Module were are supposed to add for the course under the course content?

Any ideas would be much appreciated.

Just seeing if anyone has any experience of successfully implementing and using badges.Still having difficulty finding any documentation on the complete process.

Hi @MrJohnson-SixNinesIT,

I am not exactly clear where you got stuck (looks like you’re on the right track); let me try to walk through the steps of awarding a course completion badge to a learner in your course, assuming that all the above mentioned settings are in place. Hopefully that will help clear things a bit more.

  1. Make sure your course has some graded content that can provide a passing grade.

  2. Make sure your course has an activated certificate configuration (In Studio; Settings → Certificates)

  3. Issuing course completion badges should be enabled for all courses by default but might want to double check just in case.

  4. Act as a learner (with the enrollment mode that has the activated certificate) and get a passing grade in your course

  5. Go to your progress page and hit the “Request Certificate” button (unless you have enabled certificate autogeneration)

  6. If all works as it should, all the following steps should’ve happened in the background (you can tail logs to check):

    • a certificate object is generated for your user, see in /admin/certificates/generatedcertificate/

    • if your user is the first one to get a passing grade and earn a badge in the course, a badge class representive object (for the LMS) is generated for the course, see in /admin/badges/badgeclass/

    • the LMS has initiated a badge creation in Badgr and the identifier (“EntityId”) from it is saved to your badge class in the LMS as the “Badgr server slug”

    • your user will be awarded the badge, see in /admin/badges/badgeassertion/

    • your user should see their badge in the profile page

Awarding a course event badge works in a similar way, more details here, you just create the representative badge class object for the LMS yourself (leaving the “Badgr server slug” value empty) and have to trigger the event for awarding the badge (that should then initiate the creation of the badge on the Badgr side). Or, if you manually created the badge on Badgr already, you can add the “EntityId” from it to the LMS as “Badgr server slug” to connect the two.

Be aware of testing this flow with your staff account, specially re-testing, you might need to manually delete the generatedcertificate object the badgeassertion, also reset learner state, unenroll and enroll again etc.

I hope this helps!

@mrtmm Okay, I think this is getting us really close. When following the steps, every time we have a user enroll in a course (or we switch to viewing it as a learner), we seem to be in the “audit track”, which doesn’t have certificates awards. We even have tried switching to “Learner in Verified” (Verified is the mode we set the certificate to), but when looking at the Progress, we are always told we are on the audit track.

How do we actually get users to be on one of the modes (in our case, verified) that allows for awarding certificates?

Thanks again for helping us with this.

@MrJohnson-SixNinesIT that’s right, when the user is enrolled in the audit track, even if you act as Verified learner, it won’t work.

You can try unenrolling and enrolling again for example.

Or, see if there is way to ‘upgrade’ your learner to Verified? Perhaps in the dashboard, selecting the :gear: icon. (I haven’t used verified so I don’t know exactly)

Or, perhaps you need to add Verified as a default enrollment mode to the configuration, a yaml tutor plugin example:

name: verified
version: 1.0.0
patches:
  openedx-common-settings: |
    COURSE_MODE_DEFAULTS = {
        "bulk_sku": None,
        "currency": "usd",
        "description": "Verified Certificate",
        "expiration_datetime": None,
        "min_price": 0,
        "name": "Verified",
        "sku": None,
        "slug": "verified",
        "suggested_prices": ""
}

Once the above is deployed, you’ll also need to unenroll/enroll.

Again, I haven’t used Verified so I don’t know exactly and I am not sure if any of these will do the trick, but I hope something does!

You should be able to check in the instructor tab how many users are enrolled in the Verified track, to see if any of these work.

@mrtmm Thank you, I will give these a go. You said you do not use the Verified track, is there a preferred or different common track that is used for certificates?

@mrtmm Actually, I went the route of using the “Honor” mode since that seems to be what everyone else is using. Your advice to set the courses to use a default is working. I created a unit in a subjection that has a test that is graded as a final exam, and has a certificate, but when I go to view the course Progress, I do not see a certificate there. Even though I no longer see the message about “this mode does not have certificates”.

So the certificate is activated:

I’m logged in as a learner in Honor:

I don’t see a certificate here (no longer get the audit not being a mode for certificates though):

Certificate generation is enabled in the portal:

Not sure what else I am missing. I even enrolled in the course as a separate user in Honor mode, and still do not see certificates. Is there more I have to do to actually “complete” the graded work?

Not sure about what is preferred overall, it depends on the use cases, we’ve used Honor.

This sounds very familiar, I’ve probably had that issue also.

I assume your course is self-paced and you want learners to get certificates as soon as they get the passing grade, right? Have you changed the Certificates Display Behavior to "early_no_info"? (Related docs)

@mrtmm Okay, so a combination of what you have above, plus what is detailed here: Certificates and Maple - #4 by muneera_salah, has allowed us to successfully generate certificates!

So to link all of this together with Badging, do we have to create a Badge Class for that course ahead of time for a badge to be issued at the same time as a certificate? Like creating one of these:

If so, after generating the certificate, where would we look to confirm that we indeed have had a badge generated with our registered (in our case, Badgr) server?

Hi, I have created a post about badging with Badgr Badging with Badgr. The LMS will create the BadgeClass for Course Completion in both LMS and Badgr Server.

1 Like

For course completion badges, no, the LMS should initiate the badge creation and then award it to the learner. If that doesn’t happen I suggest checking the logs for some errors.

I would check in admin for a badgeclass with the matching course-id and for a badgeassertion for the learner.
And of course in the Profile page of the learner :slight_smile:

++

@mrtmm we also went back and followed the steps from Jacatove’s post, and we ultimately end up with the follow logs. It exceptions out with a CertificateDoesNotExist exception, and then throws another exception while handing that, involving the absolute paths:

2022-11-09 19:07:56,115 INFO 11 [celery.app.trace] [user None] [ip None] trace.py:131 - Task lms.djangoapps.grades.tasks.recalculate_subsection_grade_v3[eea15c22-bb6b-4af7-b6c0-fb8f72da44dc] succeeded in 0.26011689299775753s: None
                                               
2022-11-09 19:07:58,211 INFO 11 [openedx_events.tooling] [user None] [ip None] tooling.py:160 - Responses of the Open edX Event <org.openedx.learning.certificate.changed.v1>:                                                                                                       
 []  
                                                                                                                                                                                                                                                                                 
2022-11-09 19:07:58,969 INFO 11 [botocore.vendored.requests.packages.urllib3.connectionpool] [user None] [ip None] connectionpool.py:734 - Starting new HTTPS connection (1): s3.amazonaws.com
                                                                                       
2022-11-09 19:07:59,662 ERROR 11 [django.dispatch] [user None] [ip None] dispatcher.py:214 - Error calling create_course_badge in Signal.send_robust() (This backend doesn't support absolute paths.)                                                                                
   Traceback (most recent call last):                                                                                                                                                                                                                                                   
     File "/openedx/venv/lib/python3.8/site-packages/django/db/models/query.py", line 581, in get_or_create                                                                                                                                                                             
       return self.get(**kwargs), False                                                                                                                                                                                                                                                 
     File "/openedx/venv/lib/python3.8/site-packages/django/db/models/query.py", line 435, in get                                                                                                                                                                                       
       raise self.model.DoesNotExist(                                                                                                                                                                                                                                                   
   lms.djangoapps.certificates.models.GeneratedCertificate.DoesNotExist: GeneratedCertificate matching query does not exist.                                                                                                                                                            
                                                                                                                                                                                                                                                                                      
 During handling of the above exception, another exception occurred:                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                                      
 Traceback (most recent call last):                                                                                                                                                                                                                                                   
   File "/openedx/venv/lib/python3.8/site-packages/django/dispatch/dispatcher.py", line 212, in send_robust                                                                                                                                                                           
     response = receiver(signal=self, sender=sender, **named)                                                                                                                                                                                                                         
   File "/openedx/edx-platform/lms/djangoapps/certificates/models.py", line 1241, in create_course_badge                                                                                                                                                                              
     course_badge_check(user, course_key)                                                                                                                                                                                                                                             
   File "/openedx/edx-platform/lms/djangoapps/badges/utils.py", line 27, in wrapped                                                                                                                                                                                                   
     return function(*args, **kwargs)                                                                                                                                                                                                                                                 
   File "/openedx/edx-platform/lms/djangoapps/badges/events/course_complete.py", line 129, in course_badge_check                                                                                                                                                                      
     badge_class.award(user, evidence_url=evidence)                                                                                                                                                                                                                                   
   File "/openedx/edx-platform/lms/djangoapps/badges/models.py", line 131, in award                                                                                                                                                                                                   
     return self.backend.award(self, user, evidence_url=evidence_url)  # lint-amnesty, pylint: disable=no-member                                                                                                                                                                      
   File "/openedx/edx-platform/lms/djangoapps/badges/backends/badgr.py", line 323, in award                                                                                                                                                                                           
     self._ensure_badge_created(badge_class)                                                                                                                                                                                                                                          
   File "/openedx/edx-platform/lms/djangoapps/badges/backends/badgr.py", line 316, in _ensure_badge_created                                                                                                                                                                           
     self._create_badge(badge_class)                                                                                                                                                                                                                                                  
   File "/openedx/edx-platform/lms/djangoapps/badges/backends/badgr.py", line 120, in _create_badge                                                                                                                                                                                   
     with open(image.path, 'rb') as image_file:                                                                                                                                                                                                                                       
   File "/openedx/venv/lib/python3.8/site-packages/django/db/models/fields/files.py", line 59, in path                                                                                                                                                                                
     return self.storage.path(self.name)                                                                                                                                                                                                                                              
   File "/openedx/venv/lib/python3.8/site-packages/django/core/files/storage.py", line 128, in path                                                                                                                                                                                   
     raise NotImplementedError("This backend doesn't support absolute paths.")                                                                                                                                                                                                        
 NotImplementedError: This backend doesn't support absolute paths.                                                                                                                                                                                                                    
 2022-11-09 19:08:00,075 ERROR 11 [django.dispatch] [user None] [ip None] dispatcher.py:214 - Error calling create_completion_badge in Signal.send_robust() (This backend doesn't support absolute paths.)                                                                            
 Traceback (most recent call last):                                                                                                                                                                                                                                                   
   File "/openedx/venv/lib/python3.8/site-packages/django/db/models/query.py", line 581, in get_or_create                                                                                                                                                                             
     return self.get(**kwargs), False                                                                                                                                                                                                                                                 
   File "/openedx/venv/lib/python3.8/site-packages/django/db/models/query.py", line 435, in get                                                                                                                                                                                       
     raise self.model.DoesNotExist(                                                                                                                                                                                                                                                   
 lms.djangoapps.certificates.models.GeneratedCertificate.DoesNotExist: GeneratedCertificate matching query does not exist.                                                                                                                                                            
                                                                                                                                                                                                                                                                                      
 During handling of the above exception, another exception occurred:

Traceback (most recent call last):                                                                                                                                                                                                                                                   
   File "/openedx/venv/lib/python3.8/site-packages/django/dispatch/dispatcher.py", line 212, in send_robust                                                                                                                                                                           
     response = receiver(signal=self, sender=sender, **named)                                                                                                                                                                                                                         
   File "/openedx/edx-platform/lms/djangoapps/certificates/models.py", line 1249, in create_completion_badge                                                                                                                                                                          
     completion_check(user)                                                                                                                                                                                                                                                           
   File "/openedx/edx-platform/lms/djangoapps/badges/utils.py", line 27, in wrapped                                                                                                                                                                                                   
     return function(*args, **kwargs)                                                                                                                                                                                                                                                 
   File "/openedx/edx-platform/lms/djangoapps/badges/events/course_meta.py", line 57, in completion_check                                                                                                                                                                             
     award_badge(config, certificates, user)                                                                                                                                                                                                                                          
   File "/openedx/edx-platform/lms/djangoapps/badges/events/course_meta.py", line 32, in award_badge                                                                                                                                                                                  
     badge_class.award(user)                                                                                                                                                                                                                                                          
   File "/openedx/edx-platform/lms/djangoapps/badges/models.py", line 131, in award                                                                                                                                                                                                   
     return self.backend.award(self, user, evidence_url=evidence_url)  # lint-amnesty, pylint: disable=no-member                                                                                                                                                                      
   File "/openedx/edx-platform/lms/djangoapps/badges/backends/badgr.py", line 323, in award                                                                                                                                                                                           
     self._ensure_badge_created(badge_class)                                                                                                                                                                                                                                          
   File "/openedx/edx-platform/lms/djangoapps/badges/backends/badgr.py", line 316, in _ensure_badge_created                                                                                                                                                                           
     self._create_badge(badge_class)                                                                                                                                                                                                                                                  
   File "/openedx/edx-platform/lms/djangoapps/badges/backends/badgr.py", line 120, in _create_badge                                                                                                                                                                                   
     with open(image.path, 'rb') as image_file:                                                                                                                                                                                                                                       
   File "/openedx/venv/lib/python3.8/site-packages/django/db/models/fields/files.py", line 59, in path                                                                                                                                                                                
     return self.storage.path(self.name)                                                                                                                                                                                                                                              
   File "/openedx/venv/lib/python3.8/site-packages/django/core/files/storage.py", line 128, in path                                                                                                                                                                                   
     raise NotImplementedError("This backend doesn't support absolute paths.")                                                                                                                                                                                                        
 NotImplementedError: This backend doesn't support absolute paths.                                                                                                                                                                                                                    
 2022-11-09 19:08:00,416 INFO 11 [openedx_events.tooling] [user None] [ip None] tooling.py:160 - Responses of the Open edX Event <org.openedx.learning.certificate.created.v1>:                                                                                                       
 []                                                                                                                                                                                                                                                                                   
 2022-11-09 19:08:00,425 INFO 11 [lms.djangoapps.certificates.generation] [user None] [ip None] generation.py:98 - Generated certificate with status downloadable, mode honor and grade 1.0 for 7 : course-v1:FakeOrg+BDG109+2022_T1. Certificate was created.                        
 2022-11-09 19:08:00,433 INFO 11 [tracking] [user None] [ip None] logger.py:41 - {"name": "edx.certificate.created", "context": {"org_id": "FakeOrg", "course_id": "course-v1:FakeOrg+BDG109+2022_T1"}, "username": "", "session": "", "ip": "", "agent": "", "host": "", "referer":  
 2022-11-09 19:08:00,436 INFO 11 [celery.app.trace] [user None] [ip None] trace.py:131 - Task lms.djangoapps.certificates.tasks.generate_certificate[55b6da8f-cd77-46d1-8a24-d13a70fc024a] succeeded in 2.262843096999859s: None                                                      
 2022-11-09 19:08:25,616 INFO 1 [celery.worker.control] [user None] [ip None] control.py:310 - sync with celery@edx.lms.core.default.%lms-worker-75f89cbb6d-svkbq                                                                                                                     
 2022-11-09 19:09:13,068 INFO 1 [celery.worker.strategy] [user None] [ip None] strategy.py:161 - Task lms.djangoapps.instructor_task.tasks.generate_certificates[0dd500d6-59fc-485b-a73d-98239fd2e84d] received                                                                       
 2022-11-09 19:09:13,072 INFO 11 [edx.celery.task] [user None] [ip None] tasks.py:278 - Task: 0dd500d6-59fc-485b-a73d-98239fd2e84d, InstructorTask ID: 2, Task type: certificates generated, Preparing for task execution                                                             
 2022-11-09 19:09:13,106 INFO 11 [edx.celery.task] [user None] [ip None] runner.py:108 - Task: 0dd500d6-59fc-485b-a73d-98239fd2e84d, InstructorTask ID: 2, Course: course-v1:FakeOrg+BDG109+2022_T1, Input: {'statuses_to_regenerate': ['downloadable']}, Starting update (nothing ce 
 2022-11-09 19:09:13,116 INFO 11 [lms.djangoapps.instructor_task.tasks_helper.certs] [user None] [ip None] certs.py:64 - About to attempt certificate generation for 1 users in course course-v1:FakeOrg+BDG109+2022_T1. The student_set is None and statuses_to_regenerate is ['down 
 2022-11-09 19:09:13,119 INFO 11 [lms.djangoapps.instructor_task.tasks_helper.certs] [user None] [ip None] certs.py:75 - Attempt will be made to generate a course certificate for 7 : course-v1:FakeOrg+BDG109+2022_T1.                                                              
 2022-11-09 19:09:13,124 INFO 11 [lms.djangoapps.certificates.generation_handler] [user None] [ip None] generation_handler.py:54 - Attempt will be made to generate course certificate for user 7 : course-v1:FakeOrg+BDG109+2022_T1                                                  
 2022-11-09 19:09:13,156 INFO 11 [lms.djangoapps.certificates.generation_handler] [user None] [ip None] generation_handler.py:220 - 7 : course-v1:FakeOrg+BDG109+2022_T1 is eligible for a certificate without requiring a verified ID. Skipping results of the ID verification check 
 2022-11-09 19:09:13,161 INFO 11 [lms.djangoapps.certificates.generation_handler] [user None] [ip None] generation_handler.py:367 - Certificate with status downloadable already exists for 7 : course-v1:FakeOrg+BDG109+2022_T1, and is not eligible for generation. Certificate can 
 2022-11-09 19:09:13,161 INFO 11 [lms.djangoapps.certificates.generation_handler] [user None] [ip None] generation_handler.py:184 - One of the common checks failed. Certificate cannot be generated for 7 : course-v1:FakeOrg+BDG109+2022_T1.                                        
 2022-11-09 19:09:13,177 INFO 11 [edx.celery.task] [user None] [ip None] runner.py:126 - Task: 0dd500d6-59fc-485b-a73d-98239fd2e84d, InstructorTask ID: 2, Course: course-v1:FakeOrg+BDG109+2022_T1, Input: {'statuses_to_regenerate': ['downloadable']}, Task type: certificates gen  2022-11-09 19:09:13,195 INFO 11 [celery.app.trace] [user None] [ip None] trace.py:131 - Task lms.djangoapps.instructor_task.tasks.generate_certificates[0dd500d6-59fc-485b-a73d-98239fd2e84d] succeeded in 0.10948053199899732s: {'action_name': 'certificates generated', 'attempte  2022-11-09 19:34:15,565 INFO 1 [celery.worker.strategy] [user None] [ip None] strategy.py:161 - Task openedx.core.djangoapps.content.block_structure.tasks.update_course_in_cache_v2[978f5250-908b-472c-8a23-c850a7157463] received                                                   2022-11-09 19:34:45,721 INFO 11 [openedx.core.djangoapps.content.block_structure.store] [user None] [ip None] store.py:165 - BlockStructure: Added to cache; block-v1:FakeOrg+BDG109+2022_T1+type@course+block@course, size: 1895                                                     2022-11-09 19:34:45,723 INFO 11 [celery.app.trace] [user None] [ip None] trace.py:131 - Task openedx.core.djangoapps.content.block_structure.tasks.update_course_in_cache_v2[978f5250-908b-472c-8a23-c850a7157463] succeeded in 0.05410869000115781s: None  

@mrtmm Looks like it throws the file path error here, in badgr.py: