From 860add291434f8cdd37c4937d421eb237230483a Mon Sep 17 00:00:00 2001 From: Justin Hynes Date: Wed, 26 Jul 2023 13:19:46 +0000 Subject: [PATCH] feat: add new program certificate events GitHub Issue: https://github.com/openedx/openedx-events/issues/250 Internal 2U ticket: APER-2625 This PR adds new `OpenEdxPublicSignal` events triggered by program certificate generation and revocation. - Adds a `PROGRAM_CERTIFICATE_AWARDED` event - Adds a `PROGRAM_CERTIFICATE_REVOKED` event - Adds new `ProgramCertificateData` and `ProgramData` data classes to support the new events --- CHANGELOG.rst | 11 ++- openedx_events/__init__.py | 2 +- ...program+certificate+awarded+v1_schema.avsc | 96 +++++++++++++++++++ ...program+certificate+revoked+v1_schema.avsc | 96 +++++++++++++++++++ openedx_events/learning/data.py | 37 +++++++ openedx_events/learning/signals.py | 21 ++++ .../commands/generate_avro_schemas.py | 6 +- 7 files changed, 263 insertions(+), 6 deletions(-) create mode 100644 openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+program+certificate+awarded+v1_schema.avsc create mode 100644 openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+program+certificate+revoked+v1_schema.avsc diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4b79379d..00606e4f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,10 +14,17 @@ Change Log Unreleased ---------- +[8.4.0] - 2023-07-20 +-------------------- +Added +~~~~~ +* Added new ``PROGRAM_CERTIFICATE_AWARDED`` and ``PROGRAM_CERTIFICATE_REVOKED`` events in learning subdomain +* Added new ``ProgramCertificateData`` and ``ProgramData`` data classes supporting the new program certificate events + [8.3.0] - 2023-07-10 -------------------- -Changed -~~~~~~~ +Added +~~~~~ * Added new XBLOCK_CREATED and XBLOCK_UPDATED events in content_authoring. * Added new COURSE_CREATED event in content_authoring. * Added new CONTENT_LIBRARY_CREATED, CONTENT_LIBRARY_UPDATED and CONTENT_LIBRARY_DELETED events in content_authoring. diff --git a/openedx_events/__init__.py b/openedx_events/__init__.py index 5dbea635..570a33cf 100644 --- a/openedx_events/__init__.py +++ b/openedx_events/__init__.py @@ -5,4 +5,4 @@ more information about the project. """ -__version__ = "8.3.0" +__version__ = "8.4.0" diff --git a/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+program+certificate+awarded+v1_schema.avsc b/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+program+certificate+awarded+v1_schema.avsc new file mode 100644 index 00000000..456d334a --- /dev/null +++ b/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+program+certificate+awarded+v1_schema.avsc @@ -0,0 +1,96 @@ +{ + "name": "CloudEvent", + "type": "record", + "doc": "Avro Event Format for CloudEvents created with openedx_events/schema", + "fields": [ + { + "name": "program_certificate", + "type": { + "name": "ProgramCertificateData", + "type": "record", + "fields": [ + { + "name": "user", + "type": { + "name": "UserData", + "type": "record", + "fields": [ + { + "name": "id", + "type": "long" + }, + { + "name": "is_active", + "type": "boolean" + }, + { + "name": "pii", + "type": { + "name": "UserPersonalData", + "type": "record", + "fields": [ + { + "name": "username", + "type": "string" + }, + { + "name": "email", + "type": "string" + }, + { + "name": "name", + "type": "string" + } + ] + } + } + ] + } + }, + { + "name": "program", + "type": { + "name": "ProgramData", + "type": "record", + "fields": [ + { + "name": "uuid", + "type": "string" + }, + { + "name": "title", + "type": "string" + }, + { + "name": "program_type", + "type": "string" + } + ] + } + }, + { + "name": "uuid", + "type": "string" + }, + { + "name": "status", + "type": "string" + }, + { + "name": "url", + "type": "string" + }, + { + "name": "certificate_available_date", + "type": [ + "null", + "string" + ], + "default": null + } + ] + } + } + ], + "namespace": "org.openedx.learning.program.certificate.awarded.v1" +} \ No newline at end of file diff --git a/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+program+certificate+revoked+v1_schema.avsc b/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+program+certificate+revoked+v1_schema.avsc new file mode 100644 index 00000000..2be4ad9c --- /dev/null +++ b/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+program+certificate+revoked+v1_schema.avsc @@ -0,0 +1,96 @@ +{ + "name": "CloudEvent", + "type": "record", + "doc": "Avro Event Format for CloudEvents created with openedx_events/schema", + "fields": [ + { + "name": "program_certificate", + "type": { + "name": "ProgramCertificateData", + "type": "record", + "fields": [ + { + "name": "user", + "type": { + "name": "UserData", + "type": "record", + "fields": [ + { + "name": "id", + "type": "long" + }, + { + "name": "is_active", + "type": "boolean" + }, + { + "name": "pii", + "type": { + "name": "UserPersonalData", + "type": "record", + "fields": [ + { + "name": "username", + "type": "string" + }, + { + "name": "email", + "type": "string" + }, + { + "name": "name", + "type": "string" + } + ] + } + } + ] + } + }, + { + "name": "program", + "type": { + "name": "ProgramData", + "type": "record", + "fields": [ + { + "name": "uuid", + "type": "string" + }, + { + "name": "title", + "type": "string" + }, + { + "name": "program_type", + "type": "string" + } + ] + } + }, + { + "name": "uuid", + "type": "string" + }, + { + "name": "status", + "type": "string" + }, + { + "name": "url", + "type": "string" + }, + { + "name": "certificate_available_date", + "type": [ + "null", + "string" + ], + "default": null + } + ] + } + } + ], + "namespace": "org.openedx.learning.program.certificate.revoked.v1" +} \ No newline at end of file diff --git a/openedx_events/learning/data.py b/openedx_events/learning/data.py index cda8751e..628eddab 100644 --- a/openedx_events/learning/data.py +++ b/openedx_events/learning/data.py @@ -257,3 +257,40 @@ class UserNotificationData: app_name = attr.ib(type=str) course_key = attr.ib(type=CourseKey) context = attr.ib(type=dict, factory=dict) + + +@attr.s(frozen=True) +class ProgramData: + """ + Attributes defined for the Open edX Program data object. + Arguments: + uuid (str): The UUID of the program (from Course-Discovery) + title (str): The title of the program + program_type (str): The type slug of the program (e.g. professional, microbachelors, micromasters, etc.) + """ + + uuid = attr.ib(type=str) + title = attr.ib(type=str) + program_type = attr.ib(type=str) + + +@attr.s(frozen=True) +class ProgramCertificateData: + """ + Attributes defined for the Open edX Program Certificate data object. + Arguments: + user (UserData): User associated with the Program Certificate + program (ProgramData): Program data associated with the Program Certificate + uuid (str): UUID of the UserCredential record in Credentials + certificate_available_date (datetime): Optional. A DateTime describing when a leaner is allowed to view the + credential + status (str): The status of the credential (e.g. `awarded` or `revoked`) + url (str): A URL to the learner's credential + """ + + user = attr.ib(type=UserData) + program = attr.ib(type=ProgramData) + uuid = attr.ib(type=str) + status = attr.ib(type=str) + url = attr.ib(type=str) + certificate_available_date = attr.ib(type=datetime, default=None) diff --git a/openedx_events/learning/signals.py b/openedx_events/learning/signals.py index dffbd380..5c139937 100644 --- a/openedx_events/learning/signals.py +++ b/openedx_events/learning/signals.py @@ -14,6 +14,7 @@ CourseDiscussionConfigurationData, CourseEnrollmentData, PersistentCourseGradeData, + ProgramCertificateData, UserData, UserNotificationData, XBlockSkillVerificationData, @@ -91,6 +92,16 @@ } ) +# .. event_type: org.openedx.learning.program.certificate.awarded.v1 +# .. event_name: PROGRAM_CERTIFICATE_AWARDED +# .. event_description: Emit when a program certificate is awarded to a learner +# .. event_data: ProgramCertificateData +PROGRAM_CERTIFICATE_AWARDED = OpenEdxPublicSignal( + event_type="org.openedx.learning.program.certificate.awarded.v1", + data={ + "program_certificate": ProgramCertificateData, + } +) # .. event_type: org.openedx.learning.certificate.changed.v1 # .. event_name: CERTIFICATE_CHANGED @@ -115,6 +126,16 @@ } ) +# .. event_type: org.openedx.learning.program.certificate.revoked.v1 +# .. event_name: PROGRAM_CERTIFICATE_REVOKED +# .. event_description: Emit when a program certificate is revoked from a learner +# .. event_data: ProgramCertificateData +PROGRAM_CERTIFICATE_REVOKED = OpenEdxPublicSignal( + event_type="org.openedx.learning.program.certificate.revoked.v1", + data={ + "program_certificate": ProgramCertificateData, + } +) # .. event_type: org.openedx.learning.cohort_membership.changed.v1 # .. event_name: COHORT_MEMBERSHIP_CHANGED diff --git a/openedx_events/management/commands/generate_avro_schemas.py b/openedx_events/management/commands/generate_avro_schemas.py index 1c092187..58083c49 100644 --- a/openedx_events/management/commands/generate_avro_schemas.py +++ b/openedx_events/management/commands/generate_avro_schemas.py @@ -29,14 +29,14 @@ class Command(BaseCommand): Example:: # one signal - python manage.py cms generate_avro_schemas org.openedx.learning.course.enrollment.changed.v1 + python manage.py generate_avro_schemas org.openedx.learning.course.enrollment.changed.v1 # multiple signals - python manage.py cms generate_avro_schemas org.openedx.learning.course.enrollment.changed.v1 / + python manage.py generate_avro_schemas org.openedx.learning.course.enrollment.changed.v1 / org.openedx.learning.course.unenrollment.completed.v1 # all signals - python manage.py cms generate_avro_schemas --all + python manage.py generate_avro_schemas --all """