undid my auto formatters reordering of the includes, because I'm
assuming that's what the lint-amnesty directives are there for. Best
practice would have a human-written comment explaining why they need to
stay there, but I'm just going have to assume it's correct, Because no
such human-written comment exists.
FIXES: APER-2851
adds a management command to modify certificate templates, with a dry
run option. Inherently unsafe; just as a search and replace on the first
string match.
TODO:
* unit tests
* make sure the multi-line string replacements work via django admin
FIXES: APER-2851
This PR adds the ability for the LMS to publish `CERTIFICATE_REVOKED` events to the Event Bus. There is also work in progress for Credentials to consume these events.
[APER-2344]
We would like to start consuming Certificate related events in Credentials from the event bus. This PR starts the process by publishing CERTIFICATE_CREATED events to the event bus. It also introduces a new feature flag (`SEND_CERTIFICATE_CREATED_SIGNAL`) to gate the functionality.
The VERIFIED_NAME_FLAG, the VerifiedNameEnabledView, and the verified_name_enabled key removed from responses for both VerifiedNameView view and VerifiedNameHistoryView
were removed as part https://github.com/edx/edx-name-affirmation/pull/12. This was released in version 2.0.0 of the edx-name-affirmation PyPI package. Please see below for additional context for the removal, copied from the name-affirmation commit message.
The VERIFIED_NAME_FLAG was added as part https://github.com/edx/edx-name-affirmation/pull/12, [MST-801](https://openedx.atlassian.net/browse/MST-801) in order to control the release of the Verified Name project. It was used for a phased roll out by percentage of users.
The release reached a percentage of 50% before it was observed that, due to the way percentage roll out works in django-waffle, the code to create or update VerifiedName records was not working properly. The code was written such that any change to a SoftwareSecurePhotoVerification model instance sent a signal, which was received and handled by the Name Affirmation application. If the VERIFIED_NAME_FLAG was on for the requesting user, a Celery task was launched from the Name Affirmation application to perform the creation of or update to the appropriate VerifiedName model instances based on the verify_student application signal. However, we observed that when SoftwareSecurePhotoVerification records were moved into the "created" or "ready" status, a Celery task in Name Affirmation was created, but when SoftwareSecurePhotoVerification records were moved into the "submitted" status, the corresponding Celery task in Name Affirmation was not created. This caused VerifiedName records to stay in the "pending" state.
The django-waffle waffle flag used by the edx-toggle library implements percentage rollout by setting a cookie in a learner's browser session to assign them to the enabled or disabled group.
It turns out that the code that submits a SoftwareSecurePhotoVerification record, which moves it into the "submitted" state, happens as part of a Celery task in the verify_student application in the edx-platform. Therefore, we believe that because there is no request object in a Celery task, the edx-toggle code is defaulting to the case where there is no request object. In this case, the code checks whether the flag is enabled for everyone when determining whether the flag is enabled. Because of the percentage rollout (i.e. waffle flag not enabled for everyone), the Celery task in Name Affirmation is not created. This behavior was confirmed by logging added as part of https://github.com/edx/edx-name-affirmation/pull/62.
We have determined that we do not need the waffle flag, as we are comfortable that enabling the waffle flag for everyone will fix the issue and are comfortable releasing the feature to all users. For this reason, we are removing references to the flag.
[MST-1130](https://openedx.atlassian.net/browse/MST-1130)
Includes:
- general documentation
- links to individual events definitions and location
- adding examples to events docs
- adding annotations at the trigger location
* Add COURSE_ENROLLMENT_CHANGED: sent after the enrollment update
* Add COURSE_UNENROLLMENT_COMPLETED: sent after the user's unenrollment
* Add CERTIFICATE_CREATED after the user's certificate generation has
been completed
* Add CERTIFICATE_CHANGED: after the certification update has been
completed
* Add CERTIFICATE_REVOKED: after the certificate revocation has been
completed
* Add COHORT_MEMBERSHIP_CHANGED: when a cohort membership update ends
When first building the Certificate Date Override feature, I set up the
CertificateDateOverride model to store the override dates as Dates
instead of DateTimes.
Turns out this is not how edX typically handles dates, and it’s causing
some minor headaches around needing to convert values. Also, using just
Dates causes timezone issues.
MICROBA-1488
Previously, using the bulk delete tool in the Django admin for
`CertificateDateOverride`s would not trigger the post to the credentials
service to delete the corresponding date override stored there. This was
because we were sending the COURSE_CERT_CHANGED signal from an
overridden version of the delete() method. But the Django docs warn:
"Note that the delete() method for an object is not necessarily called
when deleting objects in bulk using a QuerySet or as a result of a
cascading delete. To ensure customized delete logic gets executed, you
can use pre_delete and/or post_delete signals."
Now we use the post_delete signal instead of overriding the delete()
method to make sure the COURSE_CERT_CHANGED signal is sent for both
individual and bulk deletes.
MICROBA-1487
Because we have ATOMIC_REQUESTS turned on, the COURSE_CERT_CHANGED
signal was being sent *before* the date override was committed to the
database. This means that the date override data sent to credentials on
CertificateDateOverride save() was always one commit behind.
Django provides an on_commit() function to allow us to perform actions
only after a transaction is completed successfully. I believe this
is when we want this signal to be sent.
This is an alternative to 1) disabling atomic transactions for a view
(honestly, I’m not sure which view we would target anyway), or 2)
passing more data down through the signal.
Toward MICROBA-1423
When sending a GeneratedCertificate to Credentials, send the associated
CertificateDateOverride (if there is one), or else None. This
will be triggered after any save of a GeneratedCertificate, and after
any save or deletion of a single CertificateDateOverride.
Credentials will eventually store its own copy of this date override, or
edit or remove exiting date overrides.
If the verified name feature is enabled and the user has their
preference set to use verified name for certificates, create and
display certificates with their verified name rather than their
profile name.
Add a new model (and corresponding migration) to the certificates
app in the LMS: CertificateDateOverride. The model will eventually be
used to allow course teams / support to override the "Issued On" date
for a given course certificate.
The `may_certify` function is used to determine whether a learner should
be able to see that their certificate is ready. It is therefore entirely
in the certificates domain. This remove may_certifiy and
may_certify_for_course from the xmodule and course_overview
apps and into the certificates app. The xmodule may_certify was not called
outside of tests prior to this, and the other's were easily moved.
In order to avoid circular imports, this also moved
certificate_info_for_user out of the model code and into an API
[MICROBA-1289]
* Remove use of the CourseOverview model directly in the Certificates app
* Introduce a few new Python API functions in the `course_overview` and `catalog` django apps to replace functionality in the Certificates app.
[MICROBA-1012]
- update python files to reference the allowlist instead of whitelist
As part of the Certificates v2 work, and in an effort to make the codebase more inclusive, we are moving away from using the term "whitelist" in favor of "allowlist". This PR is part of our renaming efforts across the Certificates Django app, and other Django apps that make use of Certificates functionality.
[MICROBA-1075]
- Adds a new `edx.certificate.revoked` event to the LMS.
- Refactor of our certificate revocation functions in the GeneratedCertificate model.
This new event will be emit when a GeneratedCertificate with the status of `downloadable` is revoked (through the `invalidate(), mark_notpassing(), or mark_unverified() functions). Event will have a `source` field that will allow us how our certificates are being revoked from learners and can be broken down in the following way:
*Invalidate*
- allowlist_removal
- certificate_generation
- certificate_regeneration
- certificate_service
- certificate_invalidation_list
- bulk_certificate_regeneration
*mark_notpassing*
- certificate_generation
- notpassing_signal
*unverified*
- certificate_generation
* Change order of object Manager declarations in the GeneratedCertificate model. Declaration order matters and It appears that having the "base" `objects` declaration after the other Custom Managers creates an issue where I cannot access `objects` in a migration without this change.
* As part of the manager migration there is a cleanup function that resets the `error_reason` field to an empty string for all certificates in the `downloadable` state that currently have an error listed in the record.
[MB-1077]
- Stop removing the verify_uuid when revoking course certificates. In v2 of course certificates this will allow us to retain the same URL to the certificate. If a learner's certificate is revoked (from being invalidated or no longer passing the course) the UUID will remain intact and the URL will not change.
- Look for an existing cert for the user in a course-run during generation. If one exists, use the UUID from the existing certificate when updating the record.
- Stop generating a `key` for v2 web-certificates. This is not needed for web-certs (this is used in PDF cert generation).
[MICROBA-1100]
* Remove `cert_allowlist_generation` management command. This has been replaced by the `cert_generation` management command which can handle generation of allowlist and v2 certificates.
* Remove AllowListGenerationConfiguration configuration model.
[MICROBA-1100]
* Add CertificationGenerationCommandConfiguration model that will store the command arguments for the `cert_generation` mgmt command
* Add ability to add entries to the CertificationGenerationCommandConfiguration through Django admin
* Update mgmt command with ability to read arguments from the config model/database
* Fix failing test in `test_cert_generation.py`
* Add new test for missing `users` argument in `test_cert_generation.py`
[MICROBA-1011]
- Add data migration to remove the duplicate certificate allowlist entries before adding the new constraint to the CertificateWhitelist model.
- Add `unique_together` constraint to the CertificateWhitelist model. A learner should only have a single allowlist entry per course-run.