trax-project / moodle-trax-video

This plugin let's you add xAPI videos into your Moodle courses.
GNU General Public License v3.0
3 stars 2 forks source link

statement call gets a 400 error #1

Closed mrik974 closed 4 years ago

mrik974 commented 4 years ago

I'm using Trax Logs and Trax Video plugin (latest versions available for each one). Trax Logs works out of the box and sends all statements (I'm not using H5P for this test) from Moodle to the LRS. But the Trax Video plugin is receiving a 400 response when it tries to send a statement through the Trax Logs proxy.

Here's the statement sent by the browser :

{
    "id": "REDACTED",
    "actor": {
        "mbox": "mailto:4@traxvideo.mod"
    },
    "verb": {
        "id": "http://adlnet.gov/expapi/verbs/initialized",
        "display": {
            "en-US": "initialized"
        }
    },
    "object": {
        "id": "https://REDACTED/xapi/activities/traxvideo/c6297d5b-602b-43c5-80f8-4a1dade8265c/video",
        "definition": {
            "name": {
                "en-US": "Hygiène des mains : pourquoi ? "
            },
            "description": {
                "en-US": ""
            },
            "type": "https://w3id.org/xapi/video/activity-type/video"
        },
        "objectType": "Activity"
    },
    "context": {
        "contextActivities": {
            "category": [{
                "id": "https://w3id.org/xapi/video"
            }]
        },
        "extensions": {
            "https://w3id.org/xapi/video/extensions/completion-threshold": "1.0",
            "https://w3id.org/xapi/video/extensions/length": 301.441451,
            "https://w3id.org/xapi/video/extensions/full-screen": false,
            "https://w3id.org/xapi/video/extensions/screen-size": "2560x1440",
            "https://w3id.org/xapi/video/extensions/video-playback-size": "2186.33x1229.8",
            "https://w3id.org/xapi/video/extensions/cc-enabled": false,
            "https://w3id.org/xapi/video/extensions/speed": "1x",
            "https://w3id.org/xapi/video/extensions/frame-rate": "23.98",
            "https://w3id.org/xapi/video/extensions/quality": "960x400",
            "https://w3id.org/xapi/video/extensions/user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0",
            "https://w3id.org/xapi/video/extensions/volume": 1,
            "https://w3id.org/xapi/video/extensions/session-id": "536c5fc1-c0c1-4eb1-9fda-0f50c9260ab6"
        }
    },
    "timestamp": "2020-06-24T15:51:35.627Z"
} 

I'm still investigating the error, I'll post more comments about it.

sfraysse commented 4 years ago

Hi, What version of Moodle are you using?

mrik974 commented 4 years ago

Hi Sébastien, thank you for your quick answer ! I'm using Moodle 3.8.2

After further investigation, I found out that it's not about the video plugin, who does it job, but about the logs plugin. The LRS I'm actually testing, Learning Locker, receives this object :

{
    "actor": {
        "objectType": "Agent",
        "name": "REDACTED",
        "mbox": "mailto:REDACTED"
    },
    "verb": {
        "id": "https://w3id.org/xapi/video/verbs/paused"
    },
    "object": {
        "id": "https://REDACTED/xapi/activities/traxvideo/e0185619-e6ac-4589-959d-f7a59005ca95/video",
        "definition": {
            "name": {
                "en-US": "Vid\u00e9o : Hygi\u00e8ne des mains : comment, selon quelles techniques ?"
            },
            "type": "https://w3id.org/xapi/video/activity-type/video"
        },
        "objectType": "Activity"
    },
    "result": {
        "extensions": {
            "https://w3id.org/xapi/video/extensions/time": 8.4570000000000007,
            "https://w3id.org/xapi/video/extensions/progress": 0.029999999999999999,
            "https://w3id.org/xapi/video/extensions/played-segments": "0.385[.]2.91[,]2.91[.]3.737[,]3.737[.]5.884[,]5.884[.]6.99[,]6.99[.]8.457"
        }
    },
    "context": {
        "contextActivities": {
            "category": [{
                "id": "https://w3id.org/xapi/video",
                "objectType": "Activity",
                "definition": {
                    "type": "http://adlnet.gov/expapi/activities/profile"
                }
            }, {
                "id": "http://vocab.xapi.fr/categories/vle-profile",
                "definition": {
                    "type": "http://adlnet.gov/expapi/activities/profile"
                }
            }, {
                "id": "http://vocab.xapi.fr/categories/moodle/traxvideo",
                "definition": {
                    "type": "http://adlnet.gov/expapi/activities/profile"
                }
            }, {
                "id": "http://vocab.xapi.fr/categories/inside-learning-unit",
                "definition": {
                    "type": "http://vocab.xapi.fr/activities/granularity-level"
                }
            }],
            "grouping": [{
                "id": "https://REDACTED",
                "definition": {
                    "type": "http://vocab.xapi.fr/activities/system"
                }
            }, {
                "id": "https://REDACTED/xapi/activities/course/80bbc1dd-f4f3-44f3-9f6d-907ccb0367ec",
                "definition": {
                    "type": "http://vocab.xapi.fr/activities/course"
                }
            }],
            "parent": [{
                "id": "https://REDACTED/xapi/activities/traxvideo/e0185619-e6ac-4589-959d-f7a59005ca95",
                "definition": []
            }]
        },
        "extensions": {
            "https://w3id.org/xapi/video/extensions/length": 239.88535100000001,
            "https://w3id.org/xapi/video/extensions/session-id": "c79ec2f1-b118-49d0-abce-1490aeb1e9f8"
        },
        "platform": "Moodle"
    },
    "timestamp": "2020-06-24T16:31:05.863Z",
    "id": "656f4c4e-7558-431c-8870-424a79806779"
}

And it sends this error back to the logs plugin :

{
    "errorId": "022e4006-3332-47f8-8a14-7126196d7c28",
    "warnings": [
        "Expected 'statements.0.context.contextActivities.parent.0.definition' to be 'Object'. Received '[]'"
    ]
}

When I send an object instead of an array with Postman, the statement is stored successfully.

sfraysse commented 4 years ago

Thanks for your feedback. Yes, the parent definition should not be an empty list, but an object with the parent type. I will investigate this issue as soon as possible.

sfraysse commented 4 years ago

I have tested and my Moodle 3.8 installation I don't have this issue.

I have investigated and it looks like Trax Logs can't find a class provided by Trax Video: mod_traxvideo\xapi\vocab\activity_types.

It may be a class autoload issue. Classes located in the classes folder of each plugin should be autoloaded by Moodle after a plugin install. Could you try to reinstall Trax Video to see it solves the problem?

mrik974 commented 4 years ago

My Moodle installation is dockerized, and is regularly restarted. Reinstalling the plugin would mean rebuild the image it is based upon and restart the server, which I've done a few times in the past hours. I also tried purging the cache (based on redis) and got the same results.

Also, I added a simple class test in the file statements_post.php of the trax video plugin, and it appears the class exists and is loaded. Here was my test : if (class_exists('mod_traxvideo\xapi\vocab\activity_types')) { echo "class exists" ; }

Let me know if I can do anything else to try and resolve this issue

sfraysse commented 4 years ago

Thanks for your feedback.

I have a workaround which consist of removing the activity definition if it is an empty array. This will solve the LRS rejection because the activity definition is not required. But this will not explain your issue and the activity type will be missing in your statements. So if we can try something else, it's better.

First, a simple test. In Moodle, when you enter in a Trax Video activity, it should send a navigated-in statement, and the object of this statement should be the Trax Video activity. Could you check this statement and tell me if there is an object.definition.type property? When everything is right, you should have it. But with your problem, you shouldn't. Just to see if it is consistent.

Now if we try to understand. Trax Logs can't find the activity type for Trax Video activities, and only for this kind of activity (others are correct). And it should be provided by the mod_traxvideo\xapi\vocab\activity_types class. That's why I thought this class was not loaded.

Something is going wrong with the logstore_trax\src\activities\activity line 96:

if ($type = $this->types->type($vocabtype, $plugin)) {
    $activity['definition']['type'] = $type;
}

The type(...) method is implemented by the logstore_trax\src\vocab\vocab class, line 100:

public function __call($name, $args) {
    $key = $args[0];
    $plugin = count($args) > 1 ? $args[1] : null;
    $item = $this->get($key, $plugin);
    if (!$item || !isset($item->$name)) return false;
    return $item->$name;
}

which should call the get('traxvideo', 'mod_traxvideo') method of the same class:

public function get(string $key, string $plugin = null) {

    // Internal vocab first.
    if (isset($this->items->$key)) return $this->items->$key;
    if (!isset($plugin)) return false;

    // Plugin vocab.
    $class = '\\' . $plugin . '\\xapi\\vocab\\' . $this->class;
    if (!class_exists($class)) return false;
    $vocab = new $class();
    return $vocab->get($key);
}

which should load the mod_traxvideo\xapi\vocab\activity_types class and then get the traxvideo item, which contains the desired type.

Something is going wrong with this sequence but I can't go further because I can't reproduce the issue. Let me know if you see something...

mrik974 commented 4 years ago

Thank you for your useful explanation. I’m not used to develop in PhP, less for Moodle. But now I have enough to understand what’s going on. I already implemented your workaround and it works fine. I’ll spend a moment tomorrow to give you more information about what’s going on.

mrik974 commented 4 years ago

Hi Sébastien,

I've tried and it seems like the $plugin variable is null when activity->base_activity() is called.

mrik974 commented 4 years ago

OK I've pushed as far as I could, given my poor knowledge of PHP, and the fact that I don't have an IDE.

statements_post.php is called, and calls for $controller->proxy($objecttable) to get the final statement to send.

The controller instanciates a proxy profile of class mod_traxvideo\src\proxy\profile

This proxy looks for the parent object of the statement with the following :

$statement->context->contextActivities->parent = [
            $this->activities->get('traxvideo', $this->activity->id, false, 'module')
        ];

Activities is an object of class logstore_trax\src\services\activities Its method is called with these parameters :

public function get(string $type = "traxvideo", int $mid = 2, bool $full = false,
                        string $model = 'module', string $vocabtype = null, string $plugin = null, $entry = null)

The code goes to :

        // Finally, search in Trax Logs, based on $model.
        if (!class_exists($class)) {
            $class = '\\logstore_trax\\src\\activities\\'.$model;
        }

Where model is module It instanciates a class of type \logstore_trax\src\activities\module and calls for it's get method with the following parameters :

public function get(string $type = "traxvideo", int $mid = 2, string $uuid = "entry uuid", bool $full = false, string $vocabtype = "traxvideo", string $plugin = null)

The plugin variable is still null here. This method calls for the base_activity method with the following params :

 protected function base_activity(string $type = 'traxvideo', string $uuid = 'entry uuid', bool $full = false, string $vocabtype = 'traxvideo', string $plugin = null)

This method contains the following :

        if ($type = $this->types->type($vocabtype, $plugin)) {
            $activity['definition']['type'] = $type;
        }

It calls for logstore_trax\src\vocab\activity_types which extends logstore_trax\src\vocab\vocab and contains the following :

    public function get(string $key, string $plugin = null) {
        // Internal vocab first.
        if (isset($this->items->$key)) return $this->items->$key;
        if (!isset($plugin)) return false;

        // Plugin vocab.
        $class = '\\' . $plugin . '\\xapi\\vocab\\' . $this->class;
        if (!class_exists($class)) return false;
        $vocab = new $class();
        return $vocab->get($key);
    }

It seems that if the variable plugin is null the mod_traxvideo\xapi\vocab\activity_types you mentioned earlier will not be used, and this method will return false. this plugin variable does not seem to be set in any earlier call. Could this be the cause of my bug ?

sfraysse commented 4 years ago

Hi,

Ok, I got it! Your Trax Video plugin is not up-to-date!

In the class mod_traxvideo\src\proxy\profile, you should have:

$statement->context->contextActivities->parent = [
            $this->activities->get('traxvideo', $this->activity->id, false, 'module', 'traxvideo', 'mod_traxvideo')
];

Please, try to update your plugins from the master branch, even if the Moodle plugin version has not changed.

sfraysse commented 4 years ago

Commit from 9 dec 2019: https://github.com/trax-project/moodle-trax-video/commit/eff58369a292f23f6878bb19c68fd3e6f00d8f1a#diff-91cb1952bf1d20c3a34eebfe30221853

mrik974 commented 4 years ago

Just found out what to do, too ! I was downloading the packaged versions of your plugin to make my Docker image, as recommended in your installation instructions. I'll switch to a git pull then. Thanks for your help, I'll close the issue now

sfraysse commented 4 years ago

Sorry for that. I will update the doc.

sfraysse commented 4 years ago

I have just published v0.4 release which includes the commit from december. Sorry again, it was a mistake.

mrik974 commented 4 years ago

That's nothing to worry about. Thank you for fixing it.