Uploaded image for project: 'Moodle'
  1. Moodle
  2. MDL-54809

Immediate LTI Provider grading



    • Affected Branches:


      We use Moodle to power a very large LTI provider system (600k tools, 8k courses, still just in pilot), and the current cron system for sending back grades takes forever. It could easily take an hour to send everything with the current loops and queries. I optimized them for our setup and it can still take 3 minutes. Running the cron every 5 minutes, that's still too long of a delay to wait for grades to show up.

      Our solution for getting over this issue was to update grade_update() in gradelib.php to send the grades immediately. We'll be updating the LTI provider plugin cron job to then only act as a fallback and send grades that failed to go back immediately for whatever reason.

      Now that LTI provider is part of core, this is likely code that a lot of other people can make use of if it was part of core. I don't have a check to see if the LTI provider plugin is enabled, since we're still on 3.0.3+, but that would likely make sense before this made it in to core.

      The code updates are very simple, and just in gradelib.php.

      On line 2 of the file add

      use moodle\local\ltiprovider as ltiprovider;

      In grade_update(), on line 295 (of the version we're running), inside of if (!$failed), before return GRADE_UPDATE_OK, add the following code

              $float_grade = $rawgrade / $grade_item->grademax;
              $query = "SELECT sourceid, serviceurl, consumerkey, consumersecret FROM {local_ltiprovider_user} llu
                  LEFT JOIN {local_ltiprovider} ll on ll.id=llu.toolid
                  LEFT JOIN {context} c on c.id=ll.contextid
                  LEFT JOIN {course_modules} cm on cm.id=c.instanceid
                  LEFT JOIN {modules} m on m.id=cm.module
                  WHERE cm.course=? AND m.name=? AND cm.instance=? AND llu.userid=?";
              $user = $DB->get_record_sql($query, [$courseid, $itemmodule, $iteminstance, $userid]);
              $body = local_ltiprovider_create_service_body($user->sourceid, $float_grade);
              $response = ltiprovider\sendOAuthBodyPOST('POST', $user->serviceurl, $user->consumerkey, $user->consumersecret, 'application/xml', $body);
              if (strpos(strtolower($response), 'success') !== false) {
                  $DB->set_field('local_ltiprovider_user', 'lastgrade', intval($rawgrade), array('id' => $userid));

      It's basically taken from the LTI provider plugin, with a couple minor changes. Again, to go in to core I'd wrap it in a check that the LTI enrolment plugin is active. All of our grades are also LTI, so a check if $user actually returns something would be smart for systems that have people using Moodle directly along with LTI users, so it's not erroring trying to send back grades on non-LTI activities.




            Unassigned Unassigned
            keliix06 Doyle Lewis
            Component watchers:
            Amaia Anabitarte, Carlos Escobedo, Ferran Recio, Ilya Tregubov, Sara Arjona (@sarjona), Adrian Greeve, Jake Dallimore, Mathew May, Mihail Geshoski, Peter Dias, Sujith Haridasan, Adrian Greeve, Jake Dallimore, Mathew May, Mihail Geshoski, Peter Dias, Sujith Haridasan
            1 Vote for this issue
            8 Start watching this issue