moodle-logstore_xapi icon indicating copy to clipboard operation
moodle-logstore_xapi copied to clipboard

unable to log "delete" events

Open burkeallen opened this issue 7 years ago • 9 comments

Description

  • when acting on deleted events (eg. course_category_deleted) the read_store_record() function does not return a record from the store and therefore does not populate and ata into the $model.

Version

  • master branch version 2017061100

Steps to reproduce the bug

  1. Added event to capture course_category_deleted, delete a course category, can trace calls going into Repository.php and down to the protected function read_store_record($type, array $query)
  2. When $this->store->get_record($type, $query); $model is evaluated to false and throws the exception 'Record not found.'

Expected behaviour

  • Feature should return the $model populated with the course_category data relative to the deleted course category.

Actual behaviour

  • $model is evaluated to false and throws the exception 'Record not found.'

burkeallen avatar Dec 27 '17 19:12 burkeallen

I imagine this is going to be a limitation of Moodle that we can't get around in this plugin. Once Moodle deletes something, it removes all the records of it from the database so we can't get data about it.

I think we could still send a statement though - it would just be limited in the amount of data included (e.g. the object might be just an id, but hopefully the definition would have been sent in an earlier statement).

garemoko avatar Dec 28 '17 10:12 garemoko

Yes, I can see a valid Object ID coming through, but then the get_record() call returns nothing. so I think you are right in that Moodle is firing the event after the actual record is deleted. It would be great if they also fired a "before_delete" event.

would the best course of action be to check for "deleted" events and if that is the case do not preform the get_record but simply add the object id to the model and the return?

I can make that modification and then when I am done adding all the "other" category types to the plugin, submit a pull request to incorporate it all into the plugin.

I am working on adding the following events to the plugin

core\event\course_category_created core\event\course_category_deleted core\event\course_category_updated  core\event\cohort_created core\event\cohort_deleted core\event\cohort_member_added core\event\cohort_member_removed core\event\cohort_updated core\event\course_created core\event\course_deleted core\event\course_restored core\event\course_updated core\event\group_created core\event\group_deleted core\event\group_member_added core\event\group_member_removed core\event\group_updated core\event\grouping_created core\event\grouping_deleted core\event\grouping_updated core\event\role_assigned core\event\role_capabilities_updated core\event\role_deleted core\event\role_unassigned core\event\user_deleted core\event\user_enrolment_deleted core\event\user_enrolment_updated

burkeallen avatar Dec 28 '17 13:12 burkeallen

would the best course of action be to check for "deleted" events and if that is the case do not preform the get_record but simply add the object id to the model and the return?

Yes, just don't have the deleted events try to fill out data from the database.

Note that it's also possible that any event could have underlying data deleted before the cron job runs to send xapi statements. E.g, somebody creates then immediately deletes a course before the create event is track. So the plugin needs to fail gracefully in that scenario.

garemoko avatar Dec 28 '17 14:12 garemoko

"somebody creates then immediately deletes a course before the create event is track."

that seems like a major deficiency of the current plugin. the plugin should ideally create and locally store the events/statements realtime and then submit the locally stored statements upon execution of the cron job. is anyone really using this in "production" anywhere at the moment? if so what has been there experience with winding up with "missing" statements?

presently the plugin will throw an exception if it tries to read any record from the DB and that record is not found. so i the scenario you described, "somebody creates then immediately deletes a course before the create event is track." the create event would never get logged.

burkeallen avatar Dec 28 '17 17:12 burkeallen

I thought that I had implemented something at some point that would skip events where data was not found, but maybe that's been broken since then or perhaps it never got merged in. I hit it when using the plugin to process historical logs, so some of the data was going back quite far.

There is a mode for the plugin where the events can be sent in real time, however that means the learner's browser is left waiting for the plugin to do it's thing (including waiting for the response from the LRS) which can impact on the user experience.

I think what you are suggesting is to have the expander run in real time to store all the data in a database, and then have the translator and emitter run as a scheduled task? Hopefully that could happen fast enough not to impact the learner experience since it's not making any HTTP requests and then the translator and emitter would always have the data as it was when the event fired.

Seems like a good idea but would be a lot of work to implement, and then if it did take too long so as to impact the user experience you'd have to revert that work.

This wouldn't fix the case of stuff that's deleted immediately before the event fires; you'd have to flag that one up to Moodle core.

garemoko avatar Dec 28 '17 17:12 garemoko

by commenting out this code in the read_store_record function in the expander\src\repsotiory.php directory.

// if ($model === false) { // throw new Exception('Record not found.'); // }

I can then get the code to execute all the way through to the Emitter Class. However, I can not get the deleted event statement to make its way into the LRS. and no PHP errors are being thrown.

this is an example of the object being returned by my Emitter Class by the public function read(array $opts)

Array ( [actor] => Array ( [name] => Admin User [mbox] => [email protected] )

[context] => Array
    (
        [platform] => Moodle
        [language] => en
        [extensions] => Array
            (
                [http://lrs.learninglocker.net/define/extensions/moodle_logstore_standard_log] => Array
                    (
                        [eventname] => \core\event\course_deleted
                        [component] => core
                        [action] => deleted
                        [target] => course
                        [objecttable] => course
                        [objectid] => 3
                        [crud] => d
                        [edulevel] => 1
                        [contextid] => 54
                        [contextlevel] => 50
                        [contextinstanceid] => 3
                        [userid] => 2
                        [courseid] => 3
                        [relateduserid] => 
                        [anonymous] => 0
                        [other] => a:3:{s:9:"shortname";s:13:"test course 2";s:8:"fullname";s:23:"test course 2 full name";s:8:"idnumber";s:7:"test002";}
                        [timecreated] => 1514486791
                        [origin] => web
                        [ip] => 172.18.0.1
                        [realuserid] => 
                    )

                [http://lrs.learninglocker.net/define/extensions/info] => stdClass Object
                    (
                        [https://moodle.org/] => 3.4 (Build: 20171113)
                    )

            )

        [contextActivities] => Array
            (
                [grouping] => Array
                    (
                        [0] => Array
                            (
                                [id] => http://novodev.in
                                [definition] => Array
                                    (
                                        [type] => http://id.tincanapi.com/activitytype/site
                                        [name] => Array
                                            (
                                                [en] => "New Site"
                                            )

                                        [description] => Array
                                            (
                                                [en] => Moodle powered by Bitnami
                                            )

                                        [extensions] => Array
                                            (
                                                [http://lrs.learninglocker.net/define/extensions/moodle_course] => stdClass Object
                                                    (
                                                        [id] => 1
                                                        [category] => 0
                                                        [sortorder] => 1
                                                        [fullname] => "New Site"
                                                        [shortname] => "New Site"
                                                        [idnumber] => 
                                                        [summary] => Moodle powered by Bitnami
                                                        [summaryformat] => 0
                                                        [format] => site
                                                        [showgrades] => 1
                                                        [newsitems] => 3
                                                        [startdate] => 0
                                                        [enddate] => 0
                                                        [marker] => 0
                                                        [maxbytes] => 0
                                                        [legacyfiles] => 0
                                                        [showreports] => 0
                                                        [visible] => 1
                                                        [visibleold] => 1
                                                        [groupmode] => 0
                                                        [groupmodeforce] => 0
                                                        [defaultgroupingid] => 0
                                                        [lang] => 
                                                        [calendartype] => 
                                                        [theme] => 
                                                        [timecreated] => 1513961511
                                                        [timemodified] => 1513961571
                                                        [requested] => 0
                                                        [enablecompletion] => 0
                                                        [completionnotify] => 0
                                                        [cacherev] => 1513961571
                                                        [type] => site
                                                        [url] => http://novodev.in
                                                    )

                                            )

                                    )

                            )

                    )

                [category] => Array
                    (
                        [0] => Array
                            (
                                [id] => http://moodle.org
                                [definition] => Array
                                    (
                                        [type] => http://id.tincanapi.com/activitytype/source
                                        [name] => Array
                                            (
                                                [en] => Moodle
                                            )

                                        [description] => Array
                                            (
                                                [en] => Moodle is a open source learning platform designed to provide educators, administrators and learners with a single robust, secure and integrated system to create personalised learning environments.
                                            )

                                    )

                            )

                    )

            )

    )

[timestamp] => 2017-12-28T13:46:31-05:00
[verb] => Array
    (
        [id] => http://activitystrea.ms/schema/1.0/delete
        [display] => Array
            (
                [en] => course deleted
            )

    )

)

burkeallen avatar Dec 28 '17 18:12 burkeallen

You can add an event listener, that will take the data saved in the snapshot, if one is taken, and you then can do what you need to with that event. This means you can add a listener; take the data from the snapshot; and record the event to the LRS. However, you need to have the event listener use the snap shot, and not the record since it is no longer there. So it sounds like if we want to record delete events then an update to the code has to be employed in order to use the snap shot and not the record.

https://docs.moodle.org/dev/Event_2#Record_caching

However, not sure how to implement without adding listeners for the deletion event.

caperneoignis avatar Feb 22 '18 20:02 caperneoignis

Interesting, thanks for that @caperneoignis 👍

ryasmi avatar Feb 23 '18 10:02 ryasmi

Here's how the event_monitor does it: https://github.com/moodle/moodle/blob/master/admin/tool/monitor/db/events.php

davidpesce avatar Mar 05 '18 01:03 davidpesce

Closing this. Happy to review a PR if this is sent in, but there hasn't been demand for this functionality.

davidpesce avatar Sep 18 '23 14:09 davidpesce