Skip to content

Commit

Permalink
MDL-71235 webservices: add activity completion data to web services
Browse files Browse the repository at this point in the history
  • Loading branch information
dcai committed Apr 29, 2021
1 parent 5b315fa commit 4104a24
Show file tree
Hide file tree
Showing 12 changed files with 455 additions and 119 deletions.
23 changes: 17 additions & 6 deletions completion/classes/cm_completion_details.php
Expand Up @@ -40,6 +40,9 @@ class cm_completion_details {
/** @var completion_info The completion info instance for this cm's course. */
protected $completioninfo = null;

/** @var object The completion data. */
protected $completiondata = null;

/** @var cm_info The course module information. */
protected $cminfo = null;

Expand All @@ -62,6 +65,7 @@ class cm_completion_details {
*/
public function __construct(completion_info $completioninfo, cm_info $cminfo, int $userid, bool $returndetails = true) {
$this->completioninfo = $completioninfo;
$this->completiondata = $completioninfo->get_data($cminfo, false, $userid);
$this->cminfo = $cminfo;
$this->userid = $userid;
$this->returndetails = $returndetails;
Expand All @@ -87,7 +91,7 @@ public function get_details(): array {
return [];
}

$completiondata = $this->completioninfo->get_data($this->cminfo, false, $this->userid);
$completiondata = $this->completiondata;
$hasoverride = !empty($this->overridden_by());

$details = [];
Expand Down Expand Up @@ -155,8 +159,7 @@ public function get_details(): array {
* @return int The overall completion state for this course module.
*/
public function get_overall_completion(): int {
$completiondata = $this->completioninfo->get_data($this->cminfo, false, $this->userid);
return (int)$completiondata->completionstate;
return (int)$this->completiondata->completionstate;
}

/**
Expand All @@ -183,8 +186,7 @@ public function is_automatic(): bool {
* @return int|null
*/
public function overridden_by(): ?int {
$completiondata = $this->completioninfo->get_data($this->cminfo);
return isset($completiondata->overrideby) ? (int)$completiondata->overrideby : null;
return isset($this->completiondata->overrideby) ? (int)$this->completiondata->overrideby : null;
}

/**
Expand Down Expand Up @@ -219,6 +221,15 @@ public function show_manual_completion(): bool {
return false;
}

/**
* Completion state timemodified
*
* @return int timestamp
*/
public function get_timemodified(): int {
return (int)$this->completiondata->timemodified;
}

/**
* Generates an instance of this class.
*
Expand All @@ -229,7 +240,7 @@ public function show_manual_completion(): bool {
*/
public static function get_instance(cm_info $cminfo, int $userid, bool $returndetails = true): cm_completion_details {
$course = $cminfo->get_course();
$completioninfo = new completion_info($course);
$completioninfo = new \completion_info($course);
return new self($completioninfo, $cminfo, $userid, $returndetails);
}
}
89 changes: 63 additions & 26 deletions completion/classes/external.php
Expand Up @@ -221,7 +221,7 @@ public static function get_activities_completion_status_parameters() {
* @throws moodle_exception
*/
public static function get_activities_completion_status($courseid, $userid) {
global $CFG, $USER;
global $CFG, $USER, $PAGE;
require_once($CFG->libdir . '/grouplib.php');

$warnings = array();
Expand Down Expand Up @@ -253,25 +253,24 @@ public static function get_activities_completion_status($courseid, $userid) {

$results = array();
foreach ($activities as $activity) {

// Check if current user has visibility on this activity.
if (!$activity->uservisible) {
continue;
}

// Get progress information and state (we must use get_data because it works for all user roles in course).
$activitycompletiondata = $completion->get_data($activity, true, $user->id);

$results[] = array(
'cmid' => $activity->id,
'modname' => $activity->modname,
'instance' => $activity->instance,
'state' => $activitycompletiondata->completionstate,
'timecompleted' => $activitycompletiondata->timemodified,
'tracking' => $activity->completion,
'overrideby' => $activitycompletiondata->overrideby,
'valueused' => core_availability\info::completion_value_used($course, $activity->id)
$exporter = new \core_completion\external\completion_info_exporter(
$course,
$activity,
$userid,
);
$renderer = $PAGE->get_renderer('core');
$data = (array)$exporter->export($renderer);
$results[] = array_merge([
'cmid' => $activity->id,
'modname' => $activity->modname,
'instance' => $activity->instance,
'tracking' => $activity->completion,
], $data);
}

$results = array(
Expand All @@ -292,21 +291,59 @@ public static function get_activities_completion_status_returns() {
array(
'statuses' => new external_multiple_structure(
new external_single_structure(
array(
'cmid' => new external_value(PARAM_INT, 'comment ID'),
[
'cmid' => new external_value(PARAM_INT, 'course module ID'),
'modname' => new external_value(PARAM_PLUGIN, 'activity module name'),
'instance' => new external_value(PARAM_INT, 'instance ID'),
'state' => new external_value(PARAM_INT, 'completion state value:
0 means incomplete, 1 complete,
2 complete pass, 3 complete fail'),
'timecompleted' => new external_value(PARAM_INT, 'timestamp for completed activity'),
'tracking' => new external_value(PARAM_INT, 'type of tracking:
0 means none, 1 manual, 2 automatic'),
'overrideby' => new external_value(PARAM_INT, 'The user id who has overriden the status, or null',
'state' => new external_value(PARAM_INT,
"Completion state value:
0 means incomplete,
1 complete,
2 complete pass,
3 complete fail"
),
'timecompleted' => new external_value(PARAM_INT,
'timestamp for completed activity'),
'tracking' => new external_value(PARAM_INT,
"type of tracking:
0 means none,
1 manual,
2 automatic"
),
'overrideby' => new external_value(PARAM_INT,
'The user id who has overriden the status, or null', VALUE_OPTIONAL),
'valueused' => new external_value(PARAM_BOOL,
'Whether the completion status affects the availability of another activity.',
VALUE_OPTIONAL),
'hascompletion' => new external_value(PARAM_BOOL,
'Whether this activity module has completion enabled',
VALUE_OPTIONAL),
'isautomatic' => new external_value(PARAM_BOOL,
'Whether this activity module instance tracks completion automatically.',
VALUE_OPTIONAL),
'istrackeduser' => new external_value(PARAM_BOOL,
'Whether completion is being tracked for this user.',
VALUE_OPTIONAL),
'uservisible' => new external_value(PARAM_BOOL,
'Whether this activity is visible to the user.',
VALUE_OPTIONAL),
'valueused' => new external_value(PARAM_BOOL, 'Whether the completion status affects the availability
of another activity.', VALUE_OPTIONAL),
), 'Activity'
'details' => new external_multiple_structure(
new external_single_structure(
[
'rulename' => new external_value(PARAM_TEXT, 'Rule name'),
'rulevalue' => new external_single_structure(
[
'status' => new external_value(PARAM_INT, 'Completion status'),
'description' => new external_value(PARAM_TEXT, 'Completion description'),
]
)
]
),
VALUE_DEFAULT,
[]
),

], 'Activity'
), 'List of activities status'
),
'warnings' => new external_warnings()
Expand Down
153 changes: 153 additions & 0 deletions completion/classes/external/completion_info_exporter.php
@@ -0,0 +1,153 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

declare(strict_types=1);

namespace core_completion\external;

defined('MOODLE_INTERNAL') || die();

use renderer_base;

/**
* Completion info exporter
*
* @package core_completion
* @copyright 2021 Dongsheng Cai
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class completion_info_exporter extends \core\external\exporter {
/**
* @var object $course moodle course object
*/
private $course;
/**
* @var object|cm_info $cm course module info
*/
private $cminfo;
/**
* @var int $userid user id
*/
private $userid;

/**
* Constructor for the completion info exporter.
*
* @param object $course course object
* @param object|cm_info $cm course module info
* @param int $userid user id
* @param array $related related values
*/
public function __construct(object $course, object $cm, int $userid, array $related = []) {
$this->course = $course;
$this->cminfo = \cm_info::create($cm);
$this->userid = $userid;
parent::__construct([], $related);
}

/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output): array {
$cmcompletion = \core_completion\cm_completion_details::get_instance($this->cminfo, $this->userid);
$cmcompletiondetails = $cmcompletion->get_details();

$details = [];
foreach ($cmcompletiondetails as $rulename => $rulevalue) {
$details[] = [
'rulename' => $rulename,
'rulevalue' => (array)$rulevalue,
];
}
return [
'state' => $cmcompletion->get_overall_completion(),
'timecompleted' => $cmcompletion->get_timemodified(),
'overrideby' => $cmcompletion->overridden_by(),
'valueused' => \core_availability\info::completion_value_used($this->course, $this->cminfo->id),
'hascompletion' => $cmcompletion->has_completion(),
'isautomatic' => $cmcompletion->is_automatic(),
'istrackeduser' => $cmcompletion->is_tracked_user(),
'overallstatus' => $cmcompletion->get_overall_completion(),
'uservisible' => $this->cminfo->uservisible,
'details' => $details,
];
}

/**
* Return the list of additional properties used only for display.
*
* @return array Keys are the property names, and value their definition.
*/
public static function define_other_properties(): array {
return [
'state' => [
'type' => PARAM_INT,
'description' => 'overall completion state of this course module.',
],
'timecompleted' => [
'type' => PARAM_INT,
'description' => 'course completion timestamp.',
],
'overrideby' => [
'type' => PARAM_INT,
'description' => 'user ID that has overridden the completion state of this activity for the user.',
'null' => NULL_ALLOWED,
],
'valueused' => [
'type' => PARAM_BOOL,
'description' => 'True if module is used in a condition, false otherwise.',
],
'hascompletion' => [
'type' => PARAM_BOOL,
'description' => 'Whether this activity module has completion enabled.'
],
'isautomatic' => [
'type' => PARAM_BOOL,
'description' => 'Whether this activity module instance tracks completion automatically.'
],
'istrackeduser' => [
'type' => PARAM_BOOL,
'description' => 'Checks whether completion is being tracked for this user.'
],
'uservisible' => [
'type' => PARAM_BOOL,
'description' => 'Whether this activity is visible to user.'
],
'details' => [
'multiple' => true,
'description' => 'An array of completion details containing the description and status.',
'type' => [
'rulename' => [
'type' => PARAM_TEXT,
],
'rulevalue' => [
'type' => [
'status' => [
'type' => PARAM_INT,
],
'description' => [
'type' => PARAM_TEXT,
]
]
]
]
],
];
}
}
24 changes: 11 additions & 13 deletions completion/tests/cm_completion_details_test.php
Expand Up @@ -54,7 +54,8 @@ class cm_completion_details_test extends advanced_testcase {
* @param array $completionoptions Completion options (e.g. completionview, completionusegrade, etc.)
* @return cm_completion_details
*/
protected function setup_data(?int $completion, array $completionoptions = []): cm_completion_details {
protected function setup_data(?int $completion, array $completionoptions = [],
object $mockcompletiondata = null): cm_completion_details {
if (is_null($completion)) {
$completion = COMPLETION_TRACKING_AUTOMATIC;
}
Expand All @@ -69,6 +70,12 @@ protected function setup_data(?int $completion, array $completionoptions = []):
->method('is_enabled')
->willReturn($completion);

if (!empty($mockcompletiondata)) {
$this->completioninfo->expects($this->any())
->method('get_data')
->willReturn($mockcompletiondata);
}

// Build a mock cm_info instance.
$mockcminfo = $this->getMockBuilder(cm_info::class)
->disableOriginalConstructor()
Expand Down Expand Up @@ -171,12 +178,8 @@ public function overall_completion_provider(): array {
* @param int $state
*/
public function test_get_overall_completion(int $state) {
$cmcompletion = $this->setup_data(COMPLETION_TRACKING_AUTOMATIC);

$this->completioninfo->expects($this->once())
->method('get_data')
->willReturn((object)['completionstate' => $state]);

$completiondata = (object)['completionstate' => $state];
$cmcompletion = $this->setup_data(COMPLETION_TRACKING_AUTOMATIC, [], $completiondata);
$this->assertEquals($state, $cmcompletion->get_overall_completion());
}

Expand Down Expand Up @@ -274,12 +277,7 @@ public function test_get_details(int $completion, ?int $completionview, ?int $co
$options['completionusegrade'] = true;
}

$cmcompletion = $this->setup_data($completion, $options);

$this->completioninfo->expects($this->any())
->method('get_data')
->willReturn($getdatareturn);

$cmcompletion = $this->setup_data($completion, $options, $getdatareturn);
$this->assertEquals($expecteddetails, $cmcompletion->get_details());
}
}

0 comments on commit 4104a24

Please sign in to comment.