diff --git a/docs/getting_started.rst b/docs/getting_started.rst index 6b78e47d..df5d9424 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -236,7 +236,7 @@ xAPI Filters +-------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------+ | event_routing_backends.processors.xapi.transformer.xapi_transformer.get_verb | Intercepts and allows to modify the xAPI actor field, this affects all xAPI events | +-------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------+ -| event_routing_backends.processors.xapi.completion_events.completion_created.get_object | Allows to modify the xAPI object field, this just affects completion events | +| event_routing_backends.processors.xapi.completion_events.base_completion.get_object | Allows to modify the xAPI object field, this just affects completion events | +-------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------+ | event_routing_backends.processors.xapi.enrollment_events.base_enrollment.get_object | Allows to modify the xAPI object field, this just affects enrollment events | +-------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------+ @@ -260,6 +260,8 @@ xAPI Filters +-------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------+ | event_routing_backends.processors.xapi.video_events.base_video.get_object | Allows to modify the xAPI object field, this affects all video events | +-------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------+ +| event_routing_backends.processors.xapi.progress_events.base_progress.get_object | Allows to modify the xAPI object field, this affects all progress events | ++-------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------+ .. _event-tracking: https://github.com/openedx/event-tracking diff --git a/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.completion.chapter.json b/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.completion.chapter.json new file mode 100644 index 00000000..2a9810fd --- /dev/null +++ b/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.completion.chapter.json @@ -0,0 +1,30 @@ +{ + "name": "edx.completion_aggregator.completion.chapter", + "timestamp": "2023-12-05T21:34:52.909063+00:00", + "data": { + "user_id": 4, + "course_id": "course-v1:edX+DemoX+Demo_Course", + "context_key": "course-v1:edX+DemoX+Demo_Course", + "block_id": "block-v1:edX+DemoX+Demo_Course+type@chapter+block@b443e0d6bc4d43c1bed991dbd8a10d42", + "block_type": "course", + "percent": 0.5, + "earned": 5, + "possible": 10 + }, + "context": { + "course_id": "course-v1:edX+DemoX+Demo_Course", + "course_user_tags": {}, + "session": "056aca2a1c6b76742b283e73d3424453", + "user_id": 3, + "username": "edx", + "ip": "172.18.0.1", + "host": "localhost:18000", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + "path": "/courses/course-v1:edX+DemoX+Demo_Course/xblock/block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175/handler/xmodule_handler/problem_check", + "referer": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course+type@vertical+block@dd8110c941b94d929b56841195213797?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view", + "accept_language": "en-US,en;q=0.9,es;q=0.8", + "client_id": null, + "org_id": "edX", + "enterprise_uuid": "" + } +} diff --git a/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.completion.course.json b/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.completion.course.json new file mode 100644 index 00000000..d68487ea --- /dev/null +++ b/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.completion.course.json @@ -0,0 +1,34 @@ +{ + "name": "edx.completion_aggregator.completion.course", + "timestamp": "2023-12-05T21:34:52.909063+00:00", + "data": { + "user_id": 4, + "course_id": "course-v1:edX+DemoX+Demo_Course", + "context_key": "course-v1:edX+DemoX+Demo_Course", + "block_id": "block-v1:edX+DemoX+Demo_Course+type@course+block@course", + "block_type": "course", + "percent": 0.8, + "earned": 8, + "possible": 10 + }, + "context": { + "course_id": "course-v1:edX+DemoX+Demo_Course", + "course_user_tags": {}, + "session": "056aca2a1c6b76742b283e73d3424453", + "user_id": 3, + "username": "edx", + "ip": "172.18.0.1", + "host": "localhost:18000", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + "path": "/courses/course-v1:edX+DemoX+Demo_Course/xblock/block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175/handler/xmodule_handler/problem_check", + "referer": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course+type@vertical+block@dd8110c941b94d929b56841195213797?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view", + "accept_language": "en-US,en;q=0.9,es;q=0.8", + "client_id": null, + "org_id": "edX", + "enterprise_uuid": "", + "module": { + "display_name": "Checkboxes", + "usage_key": "block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175" + } + } +} diff --git a/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.completion.sequential.json b/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.completion.sequential.json new file mode 100644 index 00000000..62c5bfc2 --- /dev/null +++ b/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.completion.sequential.json @@ -0,0 +1,30 @@ +{ + "name": "edx.completion_aggregator.completion.sequential", + "timestamp": "2023-12-05T21:34:52.909063+00:00", + "data": { + "user_id": 4, + "course_id": "course-v1:edX+DemoX+Demo_Course", + "context_key": "course-v1:edX+DemoX+Demo_Course", + "block_id": "block-v1:edX+DemoX+Demo_Course+type@sequential+block@bf1eac10ebb649e3aaf9cc07325f8e04", + "block_type": "course", + "percent": 0.6, + "earned": 6, + "possible": 10 + }, + "context": { + "course_id": "course-v1:edX+DemoX+Demo_Course", + "course_user_tags": {}, + "session": "056aca2a1c6b76742b283e73d3424453", + "user_id": 3, + "username": "edx", + "ip": "172.18.0.1", + "host": "localhost:18000", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + "path": "/courses/course-v1:edX+DemoX+Demo_Course/xblock/block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175/handler/xmodule_handler/problem_check", + "referer": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course+type@vertical+block@dd8110c941b94d929b56841195213797?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view", + "accept_language": "en-US,en;q=0.9,es;q=0.8", + "client_id": null, + "org_id": "edX", + "enterprise_uuid": "" + } +} diff --git a/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.completion.vertical.json b/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.completion.vertical.json new file mode 100644 index 00000000..25999520 --- /dev/null +++ b/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.completion.vertical.json @@ -0,0 +1,30 @@ +{ + "name": "edx.completion_aggregator.completion.vertical", + "timestamp": "2023-12-05T21:34:52.909063+00:00", + "data": { + "user_id": 4, + "course_id": "course-v1:edX+DemoX+Demo_Course", + "context_key": "course-v1:edX+DemoX+Demo_Course", + "block_id": "block-v1:edX+DemoX+Demo_Course+type@vertical+block@e1fabd9fa55f441caa75580f258ffbc3", + "block_type": "course", + "percent": 1, + "earned": 10, + "possible": 10 + }, + "context": { + "course_id": "course-v1:edX+DemoX+Demo_Course", + "course_user_tags": {}, + "session": "056aca2a1c6b76742b283e73d3424453", + "user_id": 3, + "username": "edx", + "ip": "172.18.0.1", + "host": "localhost:18000", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + "path": "/courses/course-v1:edX+DemoX+Demo_Course/xblock/block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175/handler/xmodule_handler/problem_check", + "referer": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course+type@vertical+block@dd8110c941b94d929b56841195213797?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view", + "accept_language": "en-US,en;q=0.9,es;q=0.8", + "client_id": null, + "org_id": "edX", + "enterprise_uuid": "" + } +} diff --git a/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.progress.chapter.json b/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.progress.chapter.json new file mode 100644 index 00000000..73fd6458 --- /dev/null +++ b/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.progress.chapter.json @@ -0,0 +1,30 @@ +{ + "name": "edx.completion_aggregator.progress.chapter", + "timestamp": "2023-12-05T21:34:52.909063+00:00", + "data": { + "user_id": 4, + "course_id": "course-v1:edX+DemoX+Demo_Course", + "context_key": "course-v1:edX+DemoX+Demo_Course", + "block_id": "block-v1:edX+DemoX+Demo_Course+type@chapter+block@b443e0d6bc4d43c1bed991dbd8a10d42", + "block_type": "course", + "percent": 0.5, + "earned": 5, + "possible": 10 + }, + "context": { + "course_id": "course-v1:edX+DemoX+Demo_Course", + "course_user_tags": {}, + "session": "056aca2a1c6b76742b283e73d3424453", + "user_id": 3, + "username": "edx", + "ip": "172.18.0.1", + "host": "localhost:18000", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + "path": "/courses/course-v1:edX+DemoX+Demo_Course/xblock/block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175/handler/xmodule_handler/problem_check", + "referer": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course+type@vertical+block@dd8110c941b94d929b56841195213797?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view", + "accept_language": "en-US,en;q=0.9,es;q=0.8", + "client_id": null, + "org_id": "edX", + "enterprise_uuid": "" + } +} diff --git a/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.progress.course.json b/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.progress.course.json new file mode 100644 index 00000000..3858bc02 --- /dev/null +++ b/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.progress.course.json @@ -0,0 +1,34 @@ +{ + "name": "edx.completion_aggregator.progress.course", + "timestamp": "2023-12-05T21:34:52.909063+00:00", + "data": { + "user_id": 4, + "course_id": "course-v1:edX+DemoX+Demo_Course", + "context_key": "course-v1:edX+DemoX+Demo_Course", + "block_id": "block-v1:edX+DemoX+Demo_Course+type@course+block@course", + "block_type": "course", + "percent": 0.8, + "earned": 8, + "possible": 10 + }, + "context": { + "course_id": "course-v1:edX+DemoX+Demo_Course", + "course_user_tags": {}, + "session": "056aca2a1c6b76742b283e73d3424453", + "user_id": 3, + "username": "edx", + "ip": "172.18.0.1", + "host": "localhost:18000", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + "path": "/courses/course-v1:edX+DemoX+Demo_Course/xblock/block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175/handler/xmodule_handler/problem_check", + "referer": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course+type@vertical+block@dd8110c941b94d929b56841195213797?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view", + "accept_language": "en-US,en;q=0.9,es;q=0.8", + "client_id": null, + "org_id": "edX", + "enterprise_uuid": "", + "module": { + "display_name": "Checkboxes", + "usage_key": "block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175" + } + } +} diff --git a/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.progress.sequential.json b/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.progress.sequential.json new file mode 100644 index 00000000..67963bb2 --- /dev/null +++ b/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.progress.sequential.json @@ -0,0 +1,30 @@ +{ + "name": "edx.completion_aggregator.progress.sequential", + "timestamp": "2023-12-05T21:34:52.909063+00:00", + "data": { + "user_id": 4, + "course_id": "course-v1:edX+DemoX+Demo_Course", + "context_key": "course-v1:edX+DemoX+Demo_Course", + "block_id": "block-v1:edX+DemoX+Demo_Course+type@sequential+block@bf1eac10ebb649e3aaf9cc07325f8e04", + "block_type": "course", + "percent": 0.6, + "earned": 6, + "possible": 10 + }, + "context": { + "course_id": "course-v1:edX+DemoX+Demo_Course", + "course_user_tags": {}, + "session": "056aca2a1c6b76742b283e73d3424453", + "user_id": 3, + "username": "edx", + "ip": "172.18.0.1", + "host": "localhost:18000", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + "path": "/courses/course-v1:edX+DemoX+Demo_Course/xblock/block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175/handler/xmodule_handler/problem_check", + "referer": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course+type@vertical+block@dd8110c941b94d929b56841195213797?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view", + "accept_language": "en-US,en;q=0.9,es;q=0.8", + "client_id": null, + "org_id": "edX", + "enterprise_uuid": "" + } +} diff --git a/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.progress.vertical.json b/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.progress.vertical.json new file mode 100644 index 00000000..959d2c28 --- /dev/null +++ b/event_routing_backends/processors/tests/fixtures/current/edx.completion_aggregator.progress.vertical.json @@ -0,0 +1,30 @@ +{ + "name": "edx.completion_aggregator.progress.vertical", + "timestamp": "2023-12-05T21:34:52.909063+00:00", + "data": { + "user_id": 4, + "course_id": "course-v1:edX+DemoX+Demo_Course", + "context_key": "course-v1:edX+DemoX+Demo_Course", + "block_id": "block-v1:edX+DemoX+Demo_Course+type@vertical+block@e1fabd9fa55f441caa75580f258ffbc3", + "block_type": "course", + "percent": 1, + "earned": 10, + "possible": 10 + }, + "context": { + "course_id": "course-v1:edX+DemoX+Demo_Course", + "course_user_tags": {}, + "session": "056aca2a1c6b76742b283e73d3424453", + "user_id": 3, + "username": "edx", + "ip": "172.18.0.1", + "host": "localhost:18000", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + "path": "/courses/course-v1:edX+DemoX+Demo_Course/xblock/block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175/handler/xmodule_handler/problem_check", + "referer": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course+type@vertical+block@dd8110c941b94d929b56841195213797?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view", + "accept_language": "en-US,en;q=0.9,es;q=0.8", + "client_id": null, + "org_id": "edX", + "enterprise_uuid": "" + } +} diff --git a/event_routing_backends/processors/xapi/event_transformers/__init__.py b/event_routing_backends/processors/xapi/event_transformers/__init__.py index 2383cb8e..8dcf1b0e 100644 --- a/event_routing_backends/processors/xapi/event_transformers/__init__.py +++ b/event_routing_backends/processors/xapi/event_transformers/__init__.py @@ -2,7 +2,10 @@ All xAPI transformers. """ -from event_routing_backends.processors.xapi.event_transformers.completion_events import CompletionCreatedTransformer +from event_routing_backends.processors.xapi.event_transformers.completion_events import ( + CourseCompletionTransformer, + ModuleCompletionTransformer, +) from event_routing_backends.processors.xapi.event_transformers.enrollment_events import ( EnrollmentActivatedTransformer, EnrollmentDeactivatedTransformer, @@ -39,6 +42,11 @@ ProblemEventsTransformer, ProblemSubmittedTransformer, ) +from event_routing_backends.processors.xapi.event_transformers.progress_events import ( + CompletionCreatedTransformer, + CourseProgressTransformer, + ModuleProgressTransformer, +) from event_routing_backends.processors.xapi.event_transformers.video_events import ( VideoCCTransformers, VideoCompletedTransformer, diff --git a/event_routing_backends/processors/xapi/event_transformers/completion_events.py b/event_routing_backends/processors/xapi/event_transformers/completion_events.py index 51917fdc..ca14ad1b 100644 --- a/event_routing_backends/processors/xapi/event_transformers/completion_events.py +++ b/event_routing_backends/processors/xapi/event_transformers/completion_events.py @@ -1,7 +1,7 @@ """ -Transformers for forum related events. +Transformers for completion related events. """ -from tincan import Activity, ActivityDefinition, Extensions, LanguageMap, Result, Verb +from tincan import Activity, ActivityDefinition, LanguageMap, Verb from event_routing_backends.processors.openedx_filters.decorators import openedx_filter from event_routing_backends.processors.xapi import constants @@ -9,46 +9,50 @@ from event_routing_backends.processors.xapi.transformer import XApiTransformer -@XApiTransformersRegistry.register("edx.completion.block_completion.changed") -class CompletionCreatedTransformer(XApiTransformer): +class BaseCompletionTransformer(XApiTransformer): """ - Transformers for event generated when an student completion is created or updated. + Base transformer for completion events. """ - _verb = Verb( - id=constants.XAPI_VERB_PROGRESSED, - display=LanguageMap({constants.EN: constants.PROGRESSED}), + id=constants.XAPI_VERB_COMPLETED, + display=LanguageMap({constants.EN: constants.COMPLETED}), ) - - additional_fields = ('result', ) + object_type = None @openedx_filter( - filter_type="event_routing_backends.processors.xapi.completion_events.completion_created.get_object", + filter_type="event_routing_backends.processors.xapi.completion_events.base_completion.get_object", ) def get_object(self): """ - Get object for xAPI transformed event related to a thread. + Get object for xAPI transformed event. Returns: `Activity` """ + if not self.object_type: + raise NotImplementedError() + return Activity( id=self.get_object_iri("xblock", self.get_data("data.block_id")), definition=ActivityDefinition( - type=constants.XAPI_ACTIVITY_RESOURCE, + type=self.object_type, ), ) - def get_result(self): - """ - Get result for xAPI transformed event related to a thread. - Returns: - `Result` - """ - return Result( - completion=self.get_data("data.completion") == 1.0, - extensions=Extensions( - {constants.XAPI_ACTIVITY_PROGRESS: self.get_data("data.completion")*100} - ), - ) +@XApiTransformersRegistry.register("edx.completion_aggregator.completion.chapter") +@XApiTransformersRegistry.register("edx.completion_aggregator.completion.sequential") +@XApiTransformersRegistry.register("edx.completion_aggregator.completion.vertical") +class ModuleCompletionTransformer(BaseCompletionTransformer): + """ + Transformer for events generated when a user completes a section, subsection or unit. + """ + object_type = constants.XAPI_ACTIVITY_MODULE + + +@XApiTransformersRegistry.register("edx.completion_aggregator.completion.course") +class CourseCompletionTransformer(BaseCompletionTransformer): + """ + Transformer for event generated when a user completes a course. + """ + object_type = constants.XAPI_ACTIVITY_COURSE diff --git a/event_routing_backends/processors/xapi/event_transformers/progress_events.py b/event_routing_backends/processors/xapi/event_transformers/progress_events.py new file mode 100644 index 00000000..ad1697da --- /dev/null +++ b/event_routing_backends/processors/xapi/event_transformers/progress_events.py @@ -0,0 +1,95 @@ +""" +Transformers for progress related events. +""" +from tincan import Activity, ActivityDefinition, Extensions, LanguageMap, Result, Verb + +from event_routing_backends.processors.openedx_filters.decorators import openedx_filter +from event_routing_backends.processors.xapi import constants +from event_routing_backends.processors.xapi.registry import XApiTransformersRegistry +from event_routing_backends.processors.xapi.transformer import XApiTransformer + + +class BaseProgressTransformer(XApiTransformer): + """ + Base transformer for progress events. + """ + _verb = Verb( + id=constants.XAPI_VERB_PROGRESSED, + display=LanguageMap({constants.EN: constants.PROGRESSED}), + ) + object_type = None + additional_fields = ('result', ) + + @openedx_filter( + filter_type="event_routing_backends.processors.xapi.progress_events.base_progress.get_object", + ) + def get_object(self): + """ + Get object for xAPI transformed event. + + Returns: + `Activity` + """ + if not self.object_type: + raise NotImplementedError() + + return Activity( + id=self.get_object_iri("xblock", self.get_data("data.block_id")), + definition=ActivityDefinition( + type=self.object_type, + ), + ) + + def get_result(self): + """ + Get result for xAPI transformed event. + + Returns: + `Result` + """ + return Result( + completion=self.get_data("data.percent") == 1.0, + score={ + "scaled": self.get_data("data.percent") or 0 + } + ) + + +@XApiTransformersRegistry.register("edx.completion.block_completion.changed") +class CompletionCreatedTransformer(BaseProgressTransformer): + """ + Transformers for event generated when BlockCompletion record is created or updated. + """ + object_type = constants.XAPI_ACTIVITY_RESOURCE + + def get_result(self): + """ + Get result for xAPI transformed event. + + Returns: + `Result` + """ + return Result( + completion=self.get_data("data.completion") == 1.0, + extensions=Extensions( + {constants.XAPI_ACTIVITY_PROGRESS: self.get_data("data.completion") * 100} + ), + ) + + +@XApiTransformersRegistry.register("edx.completion_aggregator.progress.chapter") +@XApiTransformersRegistry.register("edx.completion_aggregator.progress.sequential") +@XApiTransformersRegistry.register("edx.completion_aggregator.progress.vertical") +class ModuleProgressTransformer(BaseProgressTransformer): + """ + Transformer for event generated when a user makes progress in a section, subsection or unit. + """ + object_type = constants.XAPI_ACTIVITY_MODULE + + +@XApiTransformersRegistry.register("edx.completion_aggregator.progress.course") +class CourseProgressTransformer(BaseProgressTransformer): + """ + Transformer for event generated when a user makes progress in a course. + """ + object_type = constants.XAPI_ACTIVITY_COURSE diff --git a/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.completion.chapter.json b/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.completion.chapter.json new file mode 100644 index 00000000..eeb4de61 --- /dev/null +++ b/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.completion.chapter.json @@ -0,0 +1,45 @@ +{ + "actor": { + "objectType": "Agent", + "account": { + "name": "32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb", + "homePage": "http://localhost:18000" + } + }, + "id":"484fe8d7-7a5b-52ff-a0ab-3d3d8c1a8b27", + "object":{ + "id":"http://localhost:18000/xblock/block-v1:edX+DemoX+Demo_Course+type@chapter+block@b443e0d6bc4d43c1bed991dbd8a10d42", + "definition":{ + "type":"http://adlnet.gov/expapi/activities/module" + }, + "objectType":"Activity" + }, + "verb":{ + "id":"http://adlnet.gov/expapi/verbs/completed", + "display":{ + "en":"completed" + } + }, + "version":"1.0.3", + "context":{ + "contextActivities":{ + "parent":[ + { + "id":"http://localhost:18000/course/course-v1:edX+DemoX+Demo_Course", + "objectType":"Activity", + "definition":{ + "name":{ + "en-US":"Demonstration Course" + }, + "type":"http://adlnet.gov/expapi/activities/course" + } + } + ] + }, + "extensions":{ + "https://w3id.org/xapi/openedx/extension/transformer-version":"event-routing-backends@1.1.1", + "https://w3id.org/xapi/openedx/extensions/session-id":"056aca2a1c6b76742b283e73d3424453" + } + }, + "timestamp":"2023-12-05T21:34:52.909063+00:00" +} diff --git a/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.completion.course.json b/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.completion.course.json new file mode 100644 index 00000000..a6931c7a --- /dev/null +++ b/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.completion.course.json @@ -0,0 +1,45 @@ +{ + "actor": { + "objectType": "Agent", + "account": { + "name": "32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb", + "homePage": "http://localhost:18000" + } + }, + "id":"484fe8d7-7a5b-52ff-a0ab-3d3d8c1a8b27", + "object":{ + "id":"http://localhost:18000/xblock/block-v1:edX+DemoX+Demo_Course+type@course+block@course", + "definition":{ + "type":"http://adlnet.gov/expapi/activities/course" + }, + "objectType":"Activity" + }, + "verb":{ + "id":"http://adlnet.gov/expapi/verbs/completed", + "display":{ + "en":"completed" + } + }, + "version":"1.0.3", + "context":{ + "contextActivities":{ + "parent":[ + { + "id":"http://localhost:18000/course/course-v1:edX+DemoX+Demo_Course", + "objectType":"Activity", + "definition":{ + "name":{ + "en-US":"Demonstration Course" + }, + "type":"http://adlnet.gov/expapi/activities/course" + } + } + ] + }, + "extensions":{ + "https://w3id.org/xapi/openedx/extension/transformer-version":"event-routing-backends@1.1.1", + "https://w3id.org/xapi/openedx/extensions/session-id":"056aca2a1c6b76742b283e73d3424453" + } + }, + "timestamp":"2023-12-05T21:34:52.909063+00:00" +} diff --git a/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.completion.sequential.json b/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.completion.sequential.json new file mode 100644 index 00000000..d54194b9 --- /dev/null +++ b/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.completion.sequential.json @@ -0,0 +1,45 @@ +{ + "actor": { + "objectType": "Agent", + "account": { + "name": "32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb", + "homePage": "http://localhost:18000" + } + }, + "id":"484fe8d7-7a5b-52ff-a0ab-3d3d8c1a8b27", + "object":{ + "id":"http://localhost:18000/xblock/block-v1:edX+DemoX+Demo_Course+type@sequential+block@bf1eac10ebb649e3aaf9cc07325f8e04", + "definition":{ + "type":"http://adlnet.gov/expapi/activities/module" + }, + "objectType":"Activity" + }, + "verb":{ + "id":"http://adlnet.gov/expapi/verbs/completed", + "display":{ + "en":"completed" + } + }, + "version":"1.0.3", + "context":{ + "contextActivities":{ + "parent":[ + { + "id":"http://localhost:18000/course/course-v1:edX+DemoX+Demo_Course", + "objectType":"Activity", + "definition":{ + "name":{ + "en-US":"Demonstration Course" + }, + "type":"http://adlnet.gov/expapi/activities/course" + } + } + ] + }, + "extensions":{ + "https://w3id.org/xapi/openedx/extension/transformer-version":"event-routing-backends@1.1.1", + "https://w3id.org/xapi/openedx/extensions/session-id":"056aca2a1c6b76742b283e73d3424453" + } + }, + "timestamp":"2023-12-05T21:34:52.909063+00:00" +} diff --git a/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.completion.vertical.json b/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.completion.vertical.json new file mode 100644 index 00000000..97cfccde --- /dev/null +++ b/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.completion.vertical.json @@ -0,0 +1,45 @@ +{ + "actor": { + "objectType": "Agent", + "account": { + "name": "32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb", + "homePage": "http://localhost:18000" + } + }, + "id":"484fe8d7-7a5b-52ff-a0ab-3d3d8c1a8b27", + "object":{ + "id":"http://localhost:18000/xblock/block-v1:edX+DemoX+Demo_Course+type@vertical+block@e1fabd9fa55f441caa75580f258ffbc3", + "definition":{ + "type":"http://adlnet.gov/expapi/activities/module" + }, + "objectType":"Activity" + }, + "verb":{ + "id":"http://adlnet.gov/expapi/verbs/completed", + "display":{ + "en":"completed" + } + }, + "version":"1.0.3", + "context":{ + "contextActivities":{ + "parent":[ + { + "id":"http://localhost:18000/course/course-v1:edX+DemoX+Demo_Course", + "objectType":"Activity", + "definition":{ + "name":{ + "en-US":"Demonstration Course" + }, + "type":"http://adlnet.gov/expapi/activities/course" + } + } + ] + }, + "extensions":{ + "https://w3id.org/xapi/openedx/extension/transformer-version":"event-routing-backends@1.1.1", + "https://w3id.org/xapi/openedx/extensions/session-id":"056aca2a1c6b76742b283e73d3424453" + } + }, + "timestamp":"2023-12-05T21:34:52.909063+00:00" +} diff --git a/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.progress.chapter.json b/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.progress.chapter.json new file mode 100644 index 00000000..4a575518 --- /dev/null +++ b/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.progress.chapter.json @@ -0,0 +1,51 @@ +{ + "actor": { + "objectType": "Agent", + "account": { + "name": "32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb", + "homePage": "http://localhost:18000" + } + }, + "id":"146d5372-1d64-54b1-8c60-b4acaad3c976", + "object":{ + "id":"http://localhost:18000/xblock/block-v1:edX+DemoX+Demo_Course+type@chapter+block@b443e0d6bc4d43c1bed991dbd8a10d42", + "definition":{ + "type":"http://adlnet.gov/expapi/activities/module" + }, + "objectType":"Activity" + }, + "verb":{ + "id":"http://adlnet.gov/expapi/verbs/progressed", + "display":{ + "en":"progressed" + } + }, + "version":"1.0.3", + "context":{ + "contextActivities":{ + "parent":[ + { + "id":"http://localhost:18000/course/course-v1:edX+DemoX+Demo_Course", + "objectType":"Activity", + "definition":{ + "name":{ + "en-US":"Demonstration Course" + }, + "type":"http://adlnet.gov/expapi/activities/course" + } + } + ] + }, + "extensions":{ + "https://w3id.org/xapi/openedx/extension/transformer-version":"event-routing-backends@1.1.1", + "https://w3id.org/xapi/openedx/extensions/session-id":"056aca2a1c6b76742b283e73d3424453" + } + }, + "result":{ + "score":{ + "scaled":0.5 + }, + "completion":false + }, + "timestamp":"2023-12-05T21:34:52.909063+00:00" +} diff --git a/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.progress.course.json b/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.progress.course.json new file mode 100644 index 00000000..e2f58dfe --- /dev/null +++ b/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.progress.course.json @@ -0,0 +1,51 @@ +{ + "actor": { + "objectType": "Agent", + "account": { + "name": "32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb", + "homePage": "http://localhost:18000" + } + }, + "id":"146d5372-1d64-54b1-8c60-b4acaad3c976", + "object":{ + "id":"http://localhost:18000/xblock/block-v1:edX+DemoX+Demo_Course+type@course+block@course", + "definition":{ + "type":"http://adlnet.gov/expapi/activities/course" + }, + "objectType":"Activity" + }, + "verb":{ + "id":"http://adlnet.gov/expapi/verbs/progressed", + "display":{ + "en":"progressed" + } + }, + "version":"1.0.3", + "context":{ + "contextActivities":{ + "parent":[ + { + "id":"http://localhost:18000/course/course-v1:edX+DemoX+Demo_Course", + "objectType":"Activity", + "definition":{ + "name":{ + "en-US":"Demonstration Course" + }, + "type":"http://adlnet.gov/expapi/activities/course" + } + } + ] + }, + "extensions":{ + "https://w3id.org/xapi/openedx/extension/transformer-version":"event-routing-backends@1.1.1", + "https://w3id.org/xapi/openedx/extensions/session-id":"056aca2a1c6b76742b283e73d3424453" + } + }, + "result":{ + "score":{ + "scaled":0.8 + }, + "completion":false + }, + "timestamp":"2023-12-05T21:34:52.909063+00:00" +} diff --git a/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.progress.sequential.json b/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.progress.sequential.json new file mode 100644 index 00000000..fbe78f6b --- /dev/null +++ b/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.progress.sequential.json @@ -0,0 +1,51 @@ +{ + "actor": { + "objectType": "Agent", + "account": { + "name": "32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb", + "homePage": "http://localhost:18000" + } + }, + "id":"146d5372-1d64-54b1-8c60-b4acaad3c976", + "object":{ + "id":"http://localhost:18000/xblock/block-v1:edX+DemoX+Demo_Course+type@sequential+block@bf1eac10ebb649e3aaf9cc07325f8e04", + "definition":{ + "type":"http://adlnet.gov/expapi/activities/module" + }, + "objectType":"Activity" + }, + "verb":{ + "id":"http://adlnet.gov/expapi/verbs/progressed", + "display":{ + "en":"progressed" + } + }, + "version":"1.0.3", + "context":{ + "contextActivities":{ + "parent":[ + { + "id":"http://localhost:18000/course/course-v1:edX+DemoX+Demo_Course", + "objectType":"Activity", + "definition":{ + "name":{ + "en-US":"Demonstration Course" + }, + "type":"http://adlnet.gov/expapi/activities/course" + } + } + ] + }, + "extensions":{ + "https://w3id.org/xapi/openedx/extension/transformer-version":"event-routing-backends@1.1.1", + "https://w3id.org/xapi/openedx/extensions/session-id":"056aca2a1c6b76742b283e73d3424453" + } + }, + "result":{ + "score":{ + "scaled":0.6 + }, + "completion":false + }, + "timestamp":"2023-12-05T21:34:52.909063+00:00" +} diff --git a/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.progress.vertical.json b/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.progress.vertical.json new file mode 100644 index 00000000..c44a4d7c --- /dev/null +++ b/event_routing_backends/processors/xapi/tests/fixtures/expected/edx.completion_aggregator.progress.vertical.json @@ -0,0 +1,51 @@ +{ + "actor": { + "objectType": "Agent", + "account": { + "name": "32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb", + "homePage": "http://localhost:18000" + } + }, + "id":"146d5372-1d64-54b1-8c60-b4acaad3c976", + "object":{ + "id":"http://localhost:18000/xblock/block-v1:edX+DemoX+Demo_Course+type@vertical+block@e1fabd9fa55f441caa75580f258ffbc3", + "definition":{ + "type":"http://adlnet.gov/expapi/activities/module" + }, + "objectType":"Activity" + }, + "verb":{ + "id":"http://adlnet.gov/expapi/verbs/progressed", + "display":{ + "en":"progressed" + } + }, + "version":"1.0.3", + "context":{ + "contextActivities":{ + "parent":[ + { + "id":"http://localhost:18000/course/course-v1:edX+DemoX+Demo_Course", + "objectType":"Activity", + "definition":{ + "name":{ + "en-US":"Demonstration Course" + }, + "type":"http://adlnet.gov/expapi/activities/course" + } + } + ] + }, + "extensions":{ + "https://w3id.org/xapi/openedx/extension/transformer-version":"event-routing-backends@1.1.1", + "https://w3id.org/xapi/openedx/extensions/session-id":"056aca2a1c6b76742b283e73d3424453" + } + }, + "result":{ + "score":{ + "scaled":1.0 + }, + "completion":true + }, + "timestamp":"2023-12-05T21:34:52.909063+00:00" +} diff --git a/event_routing_backends/settings/common.py b/event_routing_backends/settings/common.py index 22fe40d4..2caf6202 100644 --- a/event_routing_backends/settings/common.py +++ b/event_routing_backends/settings/common.py @@ -81,6 +81,14 @@ def plugin_settings(settings): 'edx.special_exam.proctored.attempt.created', 'edx.special_exam.proctored.attempt.submitted', 'edx.completion.block_completion.changed', + 'edx.completion_aggregator.progress.course', + 'edx.completion_aggregator.progress.chapter', + 'edx.completion_aggregator.progress.sequential', + 'edx.completion_aggregator.progress.vertical', + 'edx.completion_aggregator.completion.course', + 'edx.completion_aggregator.completion.chapter', + 'edx.completion_aggregator.completion.sequential', + 'edx.completion_aggregator.completion.vertical', 'edx.forum.thread.created', 'edx.forum.thread.deleted', 'edx.forum.thread.edited',