From f790bd4b125c66543e6e536c3984a46b3b1dc0d7 Mon Sep 17 00:00:00 2001 From: Isaac Lee <124631592+ilee2u@users.noreply.github.com> Date: Thu, 5 Sep 2024 11:54:41 -0400 Subject: [PATCH] feat: Add IDV attempt events (#385) Adding events for status changes to the new VerificationAttempt model planned for edx-platform. --- CHANGELOG.rst | 8 ++ openedx_events/__init__.py | 2 +- ...arning+idv_attempt+approved+v1_schema.avsc | 79 +++++++++++++++++++ ...earning+idv_attempt+created+v1_schema.avsc | 79 +++++++++++++++++++ ...learning+idv_attempt+denied+v1_schema.avsc | 79 +++++++++++++++++++ ...earning+idv_attempt+pending+v1_schema.avsc | 79 +++++++++++++++++++ openedx_events/learning/data.py | 20 +++++ openedx_events/learning/signals.py | 49 ++++++++++++ 8 files changed, 394 insertions(+), 1 deletion(-) create mode 100644 openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+idv_attempt+approved+v1_schema.avsc create mode 100644 openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+idv_attempt+created+v1_schema.avsc create mode 100644 openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+idv_attempt+denied+v1_schema.avsc create mode 100644 openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+idv_attempt+pending+v1_schema.avsc diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c49ab2c7..855dcae7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,6 +14,14 @@ Change Log Unreleased ---------- +[9.13.0] - 2024-09-05 +--------------------- + +Added +~~~~~~~ + +* Added new IDV events ``LEARNER_CREDIT_COURSE_ENROLLMENT_REVOKED``, ``IDV_ATTEMPT_CREATED``, ``IDV_ATTEMPT_PENDING``, ``IDV_ATTEMPT_APPROVED``, and ``IDV_ATTEMPT_DENIED`` in learning. + [9.12.0] - 2024-07-31 --------------------- diff --git a/openedx_events/__init__.py b/openedx_events/__init__.py index 434522ad..445806a0 100644 --- a/openedx_events/__init__.py +++ b/openedx_events/__init__.py @@ -5,4 +5,4 @@ more information about the project. """ -__version__ = "9.12.0" +__version__ = "9.13.0" diff --git a/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+idv_attempt+approved+v1_schema.avsc b/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+idv_attempt+approved+v1_schema.avsc new file mode 100644 index 00000000..e6a186ae --- /dev/null +++ b/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+idv_attempt+approved+v1_schema.avsc @@ -0,0 +1,79 @@ +{ + "name": "CloudEvent", + "type": "record", + "doc": "Avro Event Format for CloudEvents created with openedx_events/schema", + "fields": [ + { + "name": "idv_attempt", + "type": { + "name": "VerificationAttemptData", + "type": "record", + "fields": [ + { + "name": "attempt_id", + "type": "long" + }, + { + "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": "status", + "type": "string" + }, + { + "name": "name", + "type": [ + "null", + "string" + ], + "default": null + }, + { + "name": "expiration_date", + "type": [ + "null", + "string" + ], + "default": null + } + ] + } + } + ], + "namespace": "org.openedx.learning.idv_attempt.approved.v1" +} \ No newline at end of file diff --git a/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+idv_attempt+created+v1_schema.avsc b/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+idv_attempt+created+v1_schema.avsc new file mode 100644 index 00000000..665f18ad --- /dev/null +++ b/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+idv_attempt+created+v1_schema.avsc @@ -0,0 +1,79 @@ +{ + "name": "CloudEvent", + "type": "record", + "doc": "Avro Event Format for CloudEvents created with openedx_events/schema", + "fields": [ + { + "name": "idv_attempt", + "type": { + "name": "VerificationAttemptData", + "type": "record", + "fields": [ + { + "name": "attempt_id", + "type": "long" + }, + { + "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": "status", + "type": "string" + }, + { + "name": "name", + "type": [ + "null", + "string" + ], + "default": null + }, + { + "name": "expiration_date", + "type": [ + "null", + "string" + ], + "default": null + } + ] + } + } + ], + "namespace": "org.openedx.learning.idv_attempt.created.v1" +} \ No newline at end of file diff --git a/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+idv_attempt+denied+v1_schema.avsc b/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+idv_attempt+denied+v1_schema.avsc new file mode 100644 index 00000000..4059bcf4 --- /dev/null +++ b/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+idv_attempt+denied+v1_schema.avsc @@ -0,0 +1,79 @@ +{ + "name": "CloudEvent", + "type": "record", + "doc": "Avro Event Format for CloudEvents created with openedx_events/schema", + "fields": [ + { + "name": "idv_attempt", + "type": { + "name": "VerificationAttemptData", + "type": "record", + "fields": [ + { + "name": "attempt_id", + "type": "long" + }, + { + "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": "status", + "type": "string" + }, + { + "name": "name", + "type": [ + "null", + "string" + ], + "default": null + }, + { + "name": "expiration_date", + "type": [ + "null", + "string" + ], + "default": null + } + ] + } + } + ], + "namespace": "org.openedx.learning.idv_attempt.denied.v1" +} \ No newline at end of file diff --git a/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+idv_attempt+pending+v1_schema.avsc b/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+idv_attempt+pending+v1_schema.avsc new file mode 100644 index 00000000..6e3d639b --- /dev/null +++ b/openedx_events/event_bus/avro/tests/schemas/org+openedx+learning+idv_attempt+pending+v1_schema.avsc @@ -0,0 +1,79 @@ +{ + "name": "CloudEvent", + "type": "record", + "doc": "Avro Event Format for CloudEvents created with openedx_events/schema", + "fields": [ + { + "name": "idv_attempt", + "type": { + "name": "VerificationAttemptData", + "type": "record", + "fields": [ + { + "name": "attempt_id", + "type": "long" + }, + { + "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": "status", + "type": "string" + }, + { + "name": "name", + "type": [ + "null", + "string" + ], + "default": null + }, + { + "name": "expiration_date", + "type": [ + "null", + "string" + ], + "default": null + } + ] + } + } + ], + "namespace": "org.openedx.learning.idv_attempt.pending.v1" +} \ No newline at end of file diff --git a/openedx_events/learning/data.py b/openedx_events/learning/data.py index ba22df9a..962dda35 100644 --- a/openedx_events/learning/data.py +++ b/openedx_events/learning/data.py @@ -589,3 +589,23 @@ class BadgeData: uuid = attr.ib(type=str) user = attr.ib(type=UserData) template = attr.ib(type=BadgeTemplateData) + + +@attr.s(frozen=True) +class VerificationAttemptData: + """ + Attributes defined for the Open edX IDV attempt data object. + + Arguments: + attempt_id (int): the id of the verification attempt + user (User): the user (usually a learner) performing the verification attempt. + status (string): the status of the verification attempt. + name (string): the name being ID verified. Defaults to None. + expiration_datetime (datetime, optional): When the verification attempt expires. Defaults to None. + """ + + attempt_id = attr.ib(type=int) + user = attr.ib(type=UserData) + status = attr.ib(type=str) + name = attr.ib(type=str, default=None) + expiration_date = attr.ib(type=datetime, default=None) diff --git a/openedx_events/learning/signals.py b/openedx_events/learning/signals.py index d3970066..68fd2118 100644 --- a/openedx_events/learning/signals.py +++ b/openedx_events/learning/signals.py @@ -25,6 +25,7 @@ ProgramCertificateData, UserData, UserNotificationData, + VerificationAttemptData, XBlockSkillVerificationData, ) from openedx_events.tooling import OpenEdxPublicSignal @@ -401,3 +402,51 @@ "badge": BadgeData, } ) + + +# .. event_type: org.openedx.learning.idv_attempt.created.v1 +# .. event_name: IDV_ATTEMPT_CREATED +# .. event_description: Emitted when an IDV attempt is created +# .. event_data: VerificationAttemptData +IDV_ATTEMPT_CREATED = OpenEdxPublicSignal( + event_type="org.openedx.learning.idv_attempt.created.v1", + data={ + "idv_attempt": VerificationAttemptData, + } +) + + +# .. event_type: org.openedx.learning.idv_attempt.pending.v1 +# .. event_name: IDV_ATTEMPT_PENDING +# .. event_description: Emitted when an IDV attempt is marked as pending +# .. event_data: VerificationAttemptData +IDV_ATTEMPT_PENDING = OpenEdxPublicSignal( + event_type="org.openedx.learning.idv_attempt.pending.v1", + data={ + "idv_attempt": VerificationAttemptData, + } +) + + +# .. event_type: org.openedx.learning.idv_attempt.approved.v1 +# .. event_name: IDV_ATTEMPT_APPROVED +# .. event_description: Emitted when an IDV attempt is approved +# .. event_data: VerificationAttemptData +IDV_ATTEMPT_APPROVED = OpenEdxPublicSignal( + event_type="org.openedx.learning.idv_attempt.approved.v1", + data={ + "idv_attempt": VerificationAttemptData, + } +) + + +# .. event_type: org.openedx.learning.idv_attempt.denied.v1 +# .. event_name: IDV_ATTEMPT_DENIED +# .. event_description: Emitted when an IDV attempt is denied +# .. event_data: VerificationAttemptData +IDV_ATTEMPT_DENIED = OpenEdxPublicSignal( + event_type="org.openedx.learning.idv_attempt.denied.v1", + data={ + "idv_attempt": VerificationAttemptData, + } +)