Skip to content

Commit

Permalink
feat: add xapi transformer for exam attempts events (#349)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ian2012 committed Sep 19, 2023
1 parent a04d6c1 commit b1869a0
Show file tree
Hide file tree
Showing 22 changed files with 770 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,5 @@ requirements/private.in
requirements/private.txt

.idea

test_output/
8 changes: 8 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ Change Log
Unreleased
~~~~~~~~~~
[6.1.0]

* Add support for exam attempts events

[6.0.0]

* Do not send events for unknown users

[5.5.6]

* upgrading deprecated `djfernet` with `django-fernet-fields-v2`
Expand Down
14 changes: 14 additions & 0 deletions docs/event-mapping/Supported_events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ Forum events
* `edx.forum.comment.reported`_ | edX `sample <../../event_routing_backends/processors/tests/fixtures/current/edx.forum.comment.reported.json>`__ | xAPI `map <./xAPI_mapping.rst#edx.forum.comment.reported>`__ , `sample <../../event_routing_backends/processors/xapi/tests/fixtures/expected/edx.forum.comment.reported.json>`__
* `edx.forum.comment.unreported`_ | edX `sample <../../event_routing_backends/processors/tests/fixtures/current/edx.forum.comment.unreported.json>`__ | xAPI `map <./xAPI_mapping.rst#edx.forum.comment.unreported>`__ , `sample <../../event_routing_backends/processors/xapi/tests/fixtures/expected/edx.forum.comment.unreported.json>`__

Exam events
------------------

* `edx.special_exam.timed.attempt.created`_ | edX `sample <../../event_routing_backends/processors/tests/fixtures/current/edx.special_exam.timed.attempt.started.json>`__ | xAPI `map <./xAPI_mapping.rst#edx-special-exam-timed-attempt-started>`__ , `sample <../../event_routing_backends/processors/xapi/tests/fixtures/expected/edx.special_exam.timed.attempt.started.json>`__
* `edx.special_exam.timed.attempt.submitted`_ | edX `sample <../../event_routing_backends/processors/tests/fixtures/current/edx.special_exam.timed.attempt.submitted.json>`__ | xAPI `map <./xAPI_mapping.rst#edx-special-exam-timed-attempt-submitted>`__ , `sample <../../event_routing_backends/processors/xapi/tests/fixtures/expected/edx.special_exam.timed.attempt.submitted.json>`__
* `edx.special_exam.proctored.attempt.created`_ | edX `sample <../../event_routing_backends/processors/tests/fixtures/current/edx.special_exam.proctored.attempt.started.json>`__ | xAPI `map <./xAPI_mapping.rst#edx-special-exam-proctored-attempt-started>`__ , `sample <../../event_routing_backends/processors/xapi/tests/fixtures/expected/edx.special_exam.proctored.attempt.started.json>`__
* `edx.special_exam.proctored.attempt.submitted`_ | edX `sample <../../event_routing_backends/processors/tests/fixtures/current/edx.special_exam.proctored.attempt.submitted.json>`__ | xAPI `map <./xAPI_mapping.rst#edx-special-exam-proctored-attempt-submitted>`__ , `sample <../../event_routing_backends/processors/xapi/tests/fixtures/expected/edx.special_exam.proctored.attempt.submitted.json>`__
* `edx.special_exam.practice.attempt.submitted`_ | edX `sample <../../event_routing_backends/processors/tests/fixtures/current/edx.special_exam.practice.attempt.submitted.json>`__ | xAPI `map <./xAPI_mapping.rst#edx-special-exam-practice-attempt-submitted>`__ , `sample <../../event_routing_backends/processors/xapi/tests/fixtures/expected/edx.special_exam.practice.attempt.submitted.json>`__

.. _edx.course.enrollment.activated: http://edx.readthedocs.io/projects/devdata/en/latest/internal_data_formats/tracking_logs/student_event_types.html#edx-course-enrollment-activated-and-edx-course-enrollment-deactivated
.. _edx.course.enrollment.deactivated: http://edx.readthedocs.io/projects/devdata/en/latest/internal_data_formats/tracking_logs/student_event_types.html#edx-course-enrollment-activated-and-edx-course-enrollment-deactivated
Expand Down Expand Up @@ -115,3 +123,9 @@ Forum events
.. _edx.forum.comment.deleted: https://docs.openedx.org/en/latest/developers/references/internal_data_formats/tracking_logs/student_event_types.html#edx-forum-comment-deleted
.. _edx.forum.comment.reported: https://docs.openedx.org/en/latest/developers/references/internal_data_formats/tracking_logs/student_event_types.html#edx-forum-comment-reported
.. _edx.forum.comment.unreported: https://docs.openedx.org/en/latest/developers/references/internal_data_formats/tracking_logs/student_event_types.html#edx-forum-comment-unreported
.. _edx.special_exam.timed.attempt.created: https://docs.openedx.org/en/latest/developers/references/internal_data_formats/tracking_logs/student_event_types.html#edx-special-exam-proctored-attempt-created-edx-special-exam-practice-attempt-created-and-edx-special-exam-timed-attempt-created
.. _edx.special_exam.timed.attempt.submitted: https://docs.openedx.org/en/latest/developers/references/internal_data_formats/tracking_logs/student_event_types.html#edx-special-exam-proctored-attempt-submitted-edx-special-exam-practice-attempt-submitted-and-edx-special-exam-timed-attempt-submitted
.. _edx.special_exam.proctored.attempt.created: https://docs.openedx.org/en/latest/developers/references/internal_data_formats/tracking_logs/student_event_types.html#edx-special-exam-proctored-attempt-created-edx-special-exam-practice-attempt-created-and-edx-special-exam-timed-attempt-created
.. _edx.special_exam.proctored.attempt.submitted: https://docs.openedx.org/en/latest/developers/references/internal_data_formats/tracking_logs/student_event_types.html#edx-special-exam-proctored-attempt-submitted-edx-special-exam-practice-attempt-submitted-and-edx-special-exam-timed-attempt-submitted
.. _edx.special_exam.practice.attempt.created: https://docs.openedx.org/en/latest/developers/references/internal_data_formats/tracking_logs/student_event_types.html#edx-special-exam-proctored-attempt-created-edx-special-exam-practice-attempt-created-and-edx-special-exam-timed-attempt-created
.. _edx.special_exam.practice.attempt.submitted: https://docs.openedx.org/en/latest/developers/references/internal_data_formats/tracking_logs/student_event_types.html#edx-special-exam-proctored-attempt-submitted-edx-special-exam-practice-attempt-submitted-and-edx-special-exam-timed-attempt-submitted
2 changes: 1 addition & 1 deletion event_routing_backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
Various backends for receiving edX LMS events..
"""

__version__ = '6.0.0'
__version__ = '6.1.0'
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "edx.special_exam.practice.attempt.created",
"context": {
"user_id": 3,
"path": "/api/edx_proctoring/v1/proctored_exam/attempt",
"course_id": "course-v1:edX+DemoX+Demo_Course",
"org_id": "edX",
"enterprise_uuid": ""
},
"username": "student",
"session": "1c7862f091c5d7232ad3d7cf558f6e80",
"ip": "172.18.0.1",
"agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
"host": "localhost:18000",
"referer": "http://localhost:18000",
"accept_language": "en-US,en;q=0.9",
"event": {
"exam_id": 1,
"exam_content_id": "block-v1:edX+DemoX+Demo_Course+type@sequential+block@5b4bf8d7d41c4070b299abefed74155e",
"exam_name": "Subsection",
"exam_default_time_limit_mins": 60,
"exam_is_proctored": false,
"exam_is_practice_exam": true,
"exam_is_active": true,
"attempt_id": 1,
"attempt_user_id": 3,
"attempt_started_at": null,
"attempt_completed_at": null,
"attempt_code": "438AD672-DE2C-4F0B-8876-35444E7DD746",
"attempt_allowed_time_limit_mins": null,
"attempt_status": "created",
"attempt_event_elapsed_time_secs": null
},
"time": "2023-09-08T15:58:04.833393+00:00",
"event_type": "edx.special_exam.timed.attempt.created",
"event_source": "server",
"page": null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "edx.special_exam.practice.attempt.submitted",
"context": {
"user_id": 3,
"path": "/api/edx_proctoring/v1/proctored_exam/attempt/1",
"course_id": "course-v1:edX+DemoX+Demo_Course",
"org_id": "edX",
"enterprise_uuid": ""
},
"username": "student",
"session": "1c7862f091c5d7232ad3d7cf558f6e80",
"ip": "172.18.0.1",
"agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
"host": "localhost:18000",
"referer": "http://localhost:18000",
"accept_language": "en-US,en;q=0.9",
"event": {
"exam_id": 1,
"exam_content_id": "block-v1:edX+DemoX+Demo_Course+type@sequential+block@5b4bf8d7d41c4070b299abefed74155e",
"exam_name": "Subsection",
"exam_default_time_limit_mins": 60,
"exam_is_proctored": false,
"exam_is_practice_exam": true,
"exam_is_active": true,
"attempt_id": 1,
"attempt_user_id": 3,
"attempt_started_at": "2023-09-08T15:58:04.838598+00:00",
"attempt_completed_at": "2023-09-08T16:20:53.577698+00:00",
"attempt_code": "438AD672-DE2C-4F0B-8876-35444E7DD746",
"attempt_allowed_time_limit_mins": 60,
"attempt_status": "submitted",
"attempt_event_elapsed_time_secs": 1368.7657
},
"time": "2023-09-08T16:20:53.604375+00:00",
"event_type": "edx.special_exam.timed.attempt.submitted",
"event_source": "server",
"page": null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "edx.special_exam.proctored.attempt.created",
"context": {
"user_id": 3,
"path": "/api/edx_proctoring/v1/proctored_exam/attempt",
"course_id": "course-v1:edX+DemoX+Demo_Course",
"org_id": "edX",
"enterprise_uuid": ""
},
"username": "student",
"session": "1c7862f091c5d7232ad3d7cf558f6e80",
"ip": "172.18.0.1",
"agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
"host": "localhost:18000",
"referer": "http://localhost:18000",
"accept_language": "en-US,en;q=0.9",
"event": {
"exam_id": 1,
"exam_content_id": "block-v1:edX+DemoX+Demo_Course+type@sequential+block@5b4bf8d7d41c4070b299abefed74155e",
"exam_name": "Subsection",
"exam_default_time_limit_mins": 60,
"exam_is_proctored": true,
"exam_is_practice_exam": false,
"exam_is_active": true,
"attempt_id": 1,
"attempt_user_id": 3,
"attempt_started_at": null,
"attempt_completed_at": null,
"attempt_code": "438AD672-DE2C-4F0B-8876-35444E7DD746",
"attempt_allowed_time_limit_mins": null,
"attempt_status": "created",
"attempt_event_elapsed_time_secs": null
},
"time": "2023-09-08T15:58:04.833393+00:00",
"event_type": "edx.special_exam.timed.attempt.created",
"event_source": "server",
"page": null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "edx.special_exam.proctored.attempt.submitted",
"context": {
"user_id": 3,
"path": "/api/edx_proctoring/v1/proctored_exam/attempt/1",
"course_id": "course-v1:edX+DemoX+Demo_Course",
"org_id": "edX",
"enterprise_uuid": ""
},
"username": "student",
"session": "1c7862f091c5d7232ad3d7cf558f6e80",
"ip": "172.18.0.1",
"agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
"host": "localhost:18000",
"referer": "http://localhost:18000",
"accept_language": "en-US,en;q=0.9",
"event": {
"exam_id": 1,
"exam_content_id": "block-v1:edX+DemoX+Demo_Course+type@sequential+block@5b4bf8d7d41c4070b299abefed74155e",
"exam_name": "Subsection",
"exam_default_time_limit_mins": 60,
"exam_is_proctored": true,
"exam_is_practice_exam": false,
"exam_is_active": true,
"attempt_id": 1,
"attempt_user_id": 3,
"attempt_started_at": "2023-09-08T15:58:04.838598+00:00",
"attempt_completed_at": "2023-09-08T16:20:53.577698+00:00",
"attempt_code": "438AD672-DE2C-4F0B-8876-35444E7DD746",
"attempt_allowed_time_limit_mins": 60,
"attempt_status": "submitted",
"attempt_event_elapsed_time_secs": 1368.7657
},
"time": "2023-09-08T16:20:53.604375+00:00",
"event_type": "edx.special_exam.timed.attempt.submitted",
"event_source": "server",
"page": null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "edx.special_exam.timed.attempt.created",
"context": {
"user_id": 3,
"path": "/api/edx_proctoring/v1/proctored_exam/attempt",
"course_id": "course-v1:edX+DemoX+Demo_Course",
"org_id": "edX",
"enterprise_uuid": ""
},
"username": "student",
"session": "1c7862f091c5d7232ad3d7cf558f6e80",
"ip": "172.18.0.1",
"agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
"host": "localhost:18000",
"referer": "http://localhost:18000",
"accept_language": "en-US,en;q=0.9",
"event": {
"exam_id": 1,
"exam_content_id": "block-v1:edX+DemoX+Demo_Course+type@sequential+block@5b4bf8d7d41c4070b299abefed74155e",
"exam_name": "Subsection",
"exam_default_time_limit_mins": 60,
"exam_is_proctored": false,
"exam_is_practice_exam": false,
"exam_is_active": true,
"attempt_id": 1,
"attempt_user_id": 3,
"attempt_started_at": null,
"attempt_completed_at": null,
"attempt_code": "438AD672-DE2C-4F0B-8876-35444E7DD746",
"attempt_allowed_time_limit_mins": null,
"attempt_status": "created",
"attempt_event_elapsed_time_secs": null
},
"time": "2023-09-08T15:58:04.833393+00:00",
"event_type": "edx.special_exam.timed.attempt.created",
"event_source": "server",
"page": null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "edx.special_exam.timed.attempt.submitted",
"context": {
"user_id": 3,
"path": "/api/edx_proctoring/v1/proctored_exam/attempt/1",
"course_id": "course-v1:edX+DemoX+Demo_Course",
"org_id": "edX",
"enterprise_uuid": ""
},
"username": "student",
"session": "1c7862f091c5d7232ad3d7cf558f6e80",
"ip": "172.18.0.1",
"agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
"host": "localhost:18000",
"referer": "http://localhost:18000",
"accept_language": "en-US,en;q=0.9",
"event": {
"exam_id": 1,
"exam_content_id": "block-v1:edX+DemoX+Demo_Course+type@sequential+block@5b4bf8d7d41c4070b299abefed74155e",
"exam_name": "Subsection",
"exam_default_time_limit_mins": 60,
"exam_is_proctored": false,
"exam_is_practice_exam": false,
"exam_is_active": true,
"attempt_id": 1,
"attempt_user_id": 3,
"attempt_started_at": "2023-09-08T15:58:04.838598+00:00",
"attempt_completed_at": "2023-09-08T16:20:53.577698+00:00",
"attempt_code": "438AD672-DE2C-4F0B-8876-35444E7DD746",
"attempt_allowed_time_limit_mins": 60,
"attempt_status": "submitted",
"attempt_event_elapsed_time_secs": 1368.7657
},
"time": "2023-09-08T16:20:53.604375+00:00",
"event_type": "edx.special_exam.timed.attempt.submitted",
"event_source": "server",
"page": null
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,13 @@ def test_event_transformer(self, event_filename, mocked_uuid):
self.registry.get_transformer(original_event).transform()
else:
actual_transformed_event = self.registry.get_transformer(original_event).transform()
self.compare_events(actual_transformed_event, expected_event)
try:
self.compare_events(actual_transformed_event, expected_event)
except Exception as e: # pragma: no cover
with open(f"test_output/generated.{event_filename}.json", "w") as actual_transformed_event_file:
actual_transformed_event_file.write(actual_transformed_event.to_json())

with open(f"test_output/expected.{event_filename}.json", "w") as expected_event_file:
json.dump(expected_event, expected_event_file, indent=4)

raise e
12 changes: 12 additions & 0 deletions event_routing_backends/processors/xapi/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@
XAPI_ACTIVITY_ATTEMPT = 'http://id.tincanapi.com/extension/attempt-id'
XAPI_ACTIVITY_GRADE_CLASSIFICATION = 'http://www.tincanapi.co.uk/activitytypes/grade_classification'
XAPI_ACTIVITY_GRADE = 'http://www.tincanapi.co.uk/extensions/result/classification'
XAPI_ACTIVITY_TIMED_ASSESSMENT = 'https://w3id.org/xapi/openedx/activity/timed-assessment'
XAPI_ACTIVITY_PRACTICE_ASSESSMENT = 'https://w3id.org/xapi/openedx/activity/practice-assessment'
XAPI_ACTIVITY_PROCTORED_ASSESSMENT = 'https://w3id.org/xapi/openedx/activity/proctored-assessment'
# xAPI context
XAPI_CONTEXT_VIDEO_LENGTH = 'https://w3id.org/xapi/video/extensions/length'
XAPI_CONTEXT_VIDEO_CC_LANGUAGE = 'https://w3id.org/xapi/video/extensions/cc-subtitle-lang'
Expand All @@ -58,6 +61,15 @@
XAPI_CONTEXT_COMPLETION_THRESHOLD = 'https://w3id.org/xapi/video/extensions/completion-threshold'
XAPI_CONTEXT_SESSION_ID = 'https://w3id.org/xapi/openedx/extensions/session-id'

XAPI_ACTIVITY_TIME_LIMIT = 'https://w3id.org/xapi/acrossx/extensions/time-limit'

XAPI_ACTIVITY_EXAM_ATTEMPT = 'http://adlnet.gov/expapi/activities/attempt'

XAPI_CONTEXT_ATTEMPT_STARTED = 'https://w3id.org/xapi/openedx/extension/attempt-started'
XAPI_CONTEXT_ATTEMPT_COMPLETED = 'https://w3id.org/xapi/openedx/extension/attempt-completed'
XAPI_CONTEXT_DURATION = 'http://id.tincanapi.com/extension/duration'
XAPI_CONTEXT_CODE = 'https://w3id.org/xapi/openedx/extension/code'

# xAPI result
XAPI_RESULT_VIDEO_TIME = 'https://w3id.org/xapi/video/extensions/time'
XAPI_RESULT_VIDEO_TIME_FROM = 'https://w3id.org/xapi/video/extensions/time-from'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
EnrollmentActivatedTransformer,
EnrollmentDeactivatedTransformer,
)
from event_routing_backends.processors.xapi.event_transformers.exam_events import (
PracticeExamCreatedTransformer,
PracticeExamSubmittedTransformer,
ProctoredExamCreatedTransformer,
ProctoredExamSubmittedTransformer,
TimedExamCreatedTransformer,
TimedExamSubmittedTransformer,
)
from event_routing_backends.processors.xapi.event_transformers.forum_events import (
ThreadCreatedTransformer,
ThreadDeletedTransformer,
Expand Down
Loading

0 comments on commit b1869a0

Please sign in to comment.