IN DEVELOPMENT, PROPOSAL READY FOR REVIEW
YES
The main impetus for this is to fix the broken integration with Badgr.io backends.
The integration with Badgr on Badgr.io is now broken. Badge awarding won't work for most use cases. It should still work with the open source version of badgr-server but Concentric Sky tells me that a new release of this is forthcoming in December 2017 and will bring the badgr-server API in line with the Badgr.io site. (Badgr.io seems to have implemented some kind of "forwarding" from existing Badge Classes with old style slugs to the new slug. A new API call to create a Badge Class for slug 'openedx__coursefooslug' will not create a Badge Class addressable by that slug, but older Badge Classes will still be found by a GET to an old-style slug).
goals:
I've started with implementation of this plan, and will post PRs when I am farther along, but I am looking for architectural input now.
List related PRs:
| branch | PR |
| ----------- | ---- |
| repo/branch | link |
| repo/branch | link |
Do you think edX will need to perform the following?
* [ X] Product manager review
* [ ] UX/UI review
* [ ] Accessibility review
* [ X] Technical design review
* [ X] Documentation review - documentation of changes to Badging will be outside of the PR
The following steps will demonstrate the current problem with generating and awarding badges via Badgr.io. Some of the expected results may not have been original intentions of the badges app creator, but are likely expectations of users.
INITIAL SETUP
GET /v1/user/auth-token, passing Badgr.io username and password as Basic Auth credentials
AUTOMATIC COURSE COMPLETE BADGE CLASS CREATION
Expected:
A Badge Class exists called 'My Course'. It has the default 'honor' course mode Image. It has 1 recipient, which is your user's email address. The recipient has the evidence URL matching the webview to the certificate you earned.
Actual:
A new Badge Class with the default course mode badge image, with the name of your course. It has not been awarded - "0 recipients".
MANUAL BADGE CLASS CREATION STARTING FROM DJANGO ADMIN
Expected:
A Badge Class exists called 'My Course', with the same PNG graphic as the Image you chose in Django admin, the Description 'foo', and the Criteria 'http://mylms.com/courses/course-v1:edx+MyCourse+2017/about'. It has 1 recipient, which is your LMS user's email address. The recipient award has the evidence URL matching the webview to the certificate you earned.
Actual:
You will find a new Badge Class has been created in Badgr, with the name of your course, the criteria 'http://mylms.com/courses/course-v1:edx+MyCourse+2017/about', the Description 'Completed the course "My Course" (honor) '. It has no recipients and usees the default course mode badge image
MANUAL BADGE CLASS CREATION VIA BADGR.IO INTERFACE, MATCHING IN OPEN EDX
Expected:
Your Badge Class has 1 recipient, which is your user's email address. The recipient award has the evidence URL matching the webview to the certificate you earned.
Actual:
Your Badge Class has 0 recipients.
Badges app, student Accomplishments, course module field default change for Issue Open Badges field.
In our experience (at Appsembler), end users do not wish to award badges for completion of all courses. Other changes proposed here would make BadgeClass creation and badge issuance automatic for all courses with "Issue Open Badges" set to True. This should be changed to default to False to support the more likely common use case. Site-wide feature flag ENABLE_OPENBADGES should continue to default to False.
Currently, BadgeClass slugs are not unique. Uniqueness of BadgeClasses is determined by a combination of slug, course id, and issuing component. I propose making slug unique, and using AutoSlugField to generate a sensible value from the combination of course id, mode, and issuing component if used (This would involve adding AutoSlugField as a new requirement). BadgeClasses would be unique by slug, as well as by course id and mode together.
The main issue is that Badgr.io will no longer accept a custom slug when creating a Badge Class through the API. It now populates the 'slug' AutoSlugField and related Id field with a UUID-based identifier. The LMS badges app currently "slugifies" the BadgeClass Name and Issuing Component values and passes that to the Badgr API as the requested backend Badge Class slug, expecting that this will be a way to look up the slug in the backend. This no longer works. The API request, will, however, return the assigned Badgr slug and id (IRI) in the Response so we can store this value on the BadgeClass.
I'm proposing to decouple Open edX BadgeClass slugs and backend Badge Class ids. In the case of Badgr, the backend identifier is a Django slug storing a UUID, but to support other backends, we should just call this an 'id'. ('Slug' is not a part of the Open Badges spec—it only specifies that Badge Classes have an IRI id.) It does still make sense to maintain a "human-friendly" Django slug for the BadgeClass object, to make it easier to reference using the Django admin, especially for creating course group event configurations.
The backend implementation (Badgr only for now) should expect an API call for creation of a backend Badge Class to return the Id. We can store this backend Id in a new field on Badge Class. For the Badgr backend we will store the value of Badgr slug.
(I think it should be possible to specify separate backends/issuers per BadgeClass, but this could be done in a future phase. You could argue that the BadgeClass object should be completely independent of backend, but I'm not sure the complexity of storing a lookup from BadgeClass to backend in another persistent object is worthwhile overhead. ).
We will need to implement a data migration for existing BadgeClass objects to populate the new backend id field since the slug field is no longer a reliable future-proof identifier. The migration will involve a call to the backend API.
Currently, when a user is granted a certificate, a course complete badge is looked up by querying by a slug that must be in a very specific format derived from a hash of the course key and mode. If a user tries to set up a BadgeClass manually via the Django Admin and picks a common sense slug, course completion badge awarding will fail. The only way a Badge Class would be created with a slug in that format currently needed is if it is automatically created after a certificate event, in which case the Badge image used will be a generic image for an enrollment mode. Most users outside of edX will wish to manually create their Badge Classes and use their own badge image related to a course or educational goal. Many will wish to first create a Badge Class in the backend, then copy over the details to the LMS Django admin.
I propose several things to address these use cases and the current problems.
Currently, lookups to the course group, courses completed and enrollment configuration objects are filtered on Issuing Component = 'openedx__course'. I'm not sure that's necessary. With my proposed changes, a Badge Class slug will be unique and can be used as the key for which slug to award. Requiring a specific Issuing Component value would be redundant in terms of ensuring uniqueness.
The Open edX badges app has the concept of Issuing Component. This will no longer be part of a backend id, but is a potentially useful concept for distinguishing between course-scoped (Issuing Component="openedx__course") BadgeClasses and possible other scopes (e.g., Issuing Component="openedx__xseries"). A potential future Studio UI for Badge Class creation might support the automatic assignment of appropriate Issuing Component values.
To encourage the use of Issuing Component as a useful cross-Issuer BadgeClass identifier, I propose to append its value to the 'tags' field value in the backend. The Open Badges spec v 2.0 supports a tags field which is an array of text values.
Has edX proposed a broader list of possible Issuing Component values? If so, what are the possible values?
Deployment will involve a mandatory data migration. App requirements will have to be updated to use the AutoSlugField package.