Repository: openfun/marsha

Issue:
Create XAPI statements for live video
## Feature Request

**Is your feature request related to a problem or unsupported use case? Please describe.**

When a video is a live all the existing XAPI statement are sent like a regular videos. Some events should not be sent and some data can't be computed

**Describe the solution you'd like**

Change the activity-type to `http://id.tincanapi.com/activitytype/webinar`
Send statement for those events : 
- initialized
- play
- pause
- interacted

Also, do not send video length info, we can't have it. The completion threshold can not be computed too.



Discussion:

Resolving patch:
diff --git a/src/backend/marsha/core/xapi.py b/src/backend/marsha/core/xapi.py
--- a/src/backend/marsha/core/xapi.py
+++ b/src/backend/marsha/core/xapi.py
@@ -52,6 +52,12 @@ def __init__(self, video, statement, lti_user):
 
         homepage = video.playlist.consumer_site.domain
 
+        activity_type = "https://w3id.org/xapi/video/activity-type/video"
+
+        # When the video is a live we change the activity to webinar
+        if video.live_state is not None:
+            activity_type = "http://id.tincanapi.com/activitytype/webinar"
+
         if re.match(r"^http(s?):\/\/.*", homepage) is None:
             homepage = f"http://{homepage}"
 
@@ -70,7 +76,7 @@ def __init__(self, video, statement, lti_user):
 
         statement["object"] = {
             "definition": {
-                "type": "https://w3id.org/xapi/video/activity-type/video",
+                "type": activity_type,
                 "name": {
                     to_locale(settings.LANGUAGE_CODE).replace("_", "-"): video.title
                 },


Test patch:
diff --git a/src/backend/marsha/core/tests/test_xapi.py b/src/backend/marsha/core/tests/test_xapi.py
--- a/src/backend/marsha/core/tests/test_xapi.py
+++ b/src/backend/marsha/core/tests/test_xapi.py
@@ -3,6 +3,7 @@
 
 from django.test import TestCase, override_settings
 
+from ..defaults import RAW, RUNNING
 from ..factories import VideoFactory
 from ..lti import LTIUser
 from ..xapi import XAPI, XAPIStatement, requests
@@ -174,6 +175,88 @@ def test_xapi_statement_enrich_statement(self):
         self.assertEqual(statement["id"], base_statement["id"])
         self.assertEqual(statement["result"], base_statement["result"])
 
+    def test_xapi_statement_live_video(self):
+        """A live video should send a webinar activity type."""
+        video = VideoFactory(
+            id="68333c45-4b8c-4018-a195-5d5e1706b838",
+            playlist__consumer_site__domain="example.com",
+            title="test video xapi",
+            live_state=RUNNING,
+            live_type=RAW,
+        )
+
+        mock_token_user = mock.MagicMock()
+        mock_token = mock.MagicMock()
+        type(mock_token).payload = mock.PropertyMock(
+            return_value={
+                "user": {
+                    "id": "foo",
+                },
+                "course": {
+                    "school_name": "ufr",
+                    "course_name": "mathematics",
+                    "course_run": "001",
+                },
+            }
+        )
+        type(mock_token_user).token = mock.PropertyMock(return_value=mock_token)
+
+        lti_user = LTIUser(mock_token_user)
+
+        base_statement = {
+            "context": {
+                "extensions": {
+                    "https://w3id.org/xapi/video/extensions/session-id": "a6151456-18b7-"
+                    "43b4-8452-2037fed588df"
+                }
+            },
+            "result": {
+                "extensions": {
+                    "https://w3id.org/xapi/video/extensions/time-from": 0,
+                    "https://w3id.org/xapi/video/extensions/time-to": 0,
+                    "https://w3id.org/xapi/video/extensions/length": 104.304,
+                    "https://w3id.org/xapi/video/extensions/progress": 0,
+                    "https://w3id.org/xapi/video/extensions/played-segments": "0",
+                }
+            },
+            "verb": {
+                "display": {"en-US": "seeked"},
+                "id": "https://w3id.org/xapi/video/verbs/seeked",
+            },
+            "id": "17dfcd44-b3e0-403d-ab96-e3ef7da616d4",
+        }
+
+        xapi_statement = XAPIStatement(video, base_statement, lti_user)
+        statement = xapi_statement.get_statement()
+
+        self.assertIsNotNone(statement["timestamp"])
+        self.assertEqual(
+            statement["actor"],
+            {
+                "objectType": "Agent",
+                "account": {"name": "foo", "homePage": "http://example.com"},
+            },
+        )
+        self.assertEqual(
+            statement["object"],
+            {
+                "definition": {
+                    "type": "http://id.tincanapi.com/activitytype/webinar",
+                    "name": {"en-US": "test video xapi"},
+                    "extensions": {
+                        "https://w3id.org/xapi/acrossx/extensions/school": "ufr",
+                        "http://adlnet.gov/expapi/activities/course": "mathematics",
+                        "http://adlnet.gov/expapi/activities/module": "001",
+                    },
+                },
+                "id": "uuid://68333c45-4b8c-4018-a195-5d5e1706b838",
+                "objectType": "Activity",
+            },
+        )
+        self.assertEqual(statement["verb"], base_statement["verb"])
+        self.assertEqual(statement["id"], base_statement["id"])
+        self.assertEqual(statement["result"], base_statement["result"])
+
 
 class XAPITest(TestCase):
     """Test the xapi module."""