mirror of
https://github.com/moodle/moodle.git
synced 2025-08-07 01:46:45 +02:00
Merge branch 'dashboard-split-overview-block' of https://github.com/ryanwyllie/moodle
This commit is contained in:
commit
20f9b981f9
137 changed files with 5871 additions and 2450 deletions
1
course/amd/build/repository.min.js
vendored
Normal file
1
course/amd/build/repository.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
define(["jquery","core/ajax"],function(a,b){var c=function(a,c,d,e){var f={classification:a};"undefined"!=typeof c&&(f.limit=c),"undefined"!=typeof d&&(f.offset=d),"undefined"!=typeof e&&(f.sort=e);var g={methodname:"core_course_get_enrolled_courses_by_timeline_classification",args:f};return b.call([g])[0]};return{getEnrolledCoursesByTimelineClassification:c}});
|
63
course/amd/src/repository.js
Normal file
63
course/amd/src/repository.js
Normal file
|
@ -0,0 +1,63 @@
|
|||
// 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/>.
|
||||
|
||||
/**
|
||||
* A javascript module to handle course ajax actions.
|
||||
*
|
||||
* @module core_course/repository
|
||||
* @copyright 2018 Ryan Wyllie <ryan@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/ajax'], function($, Ajax) {
|
||||
|
||||
/**
|
||||
* Get the list of courses that the logged in user is enrolled in for a given
|
||||
* timeline classification.
|
||||
*
|
||||
* @param {string} classification past, inprogress, or future
|
||||
* @param {int} limit Only return this many results
|
||||
* @param {int} offset Skip this many results from the start of the result set
|
||||
* @param {string} sort Column to sort by and direction, e.g. 'shortname asc'
|
||||
* @return {object} jQuery promise resolved with courses.
|
||||
*/
|
||||
var getEnrolledCoursesByTimelineClassification = function(classification, limit, offset, sort) {
|
||||
var args = {
|
||||
classification: classification
|
||||
};
|
||||
|
||||
if (typeof limit !== 'undefined') {
|
||||
args.limit = limit;
|
||||
}
|
||||
|
||||
if (typeof offset !== 'undefined') {
|
||||
args.offset = offset;
|
||||
}
|
||||
|
||||
if (typeof sort !== 'undefined') {
|
||||
args.sort = sort;
|
||||
}
|
||||
|
||||
var request = {
|
||||
methodname: 'core_course_get_enrolled_courses_by_timeline_classification',
|
||||
args: args
|
||||
};
|
||||
|
||||
return Ajax.call([request])[0];
|
||||
};
|
||||
|
||||
return {
|
||||
getEnrolledCoursesByTimelineClassification: getEnrolledCoursesByTimelineClassification
|
||||
};
|
||||
});
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
use core_course\external\course_summary_exporter;
|
||||
|
||||
require_once("$CFG->libdir/externallib.php");
|
||||
|
||||
/**
|
||||
|
@ -3553,4 +3555,114 @@ class core_course_external extends external_api {
|
|||
public static function edit_section_returns() {
|
||||
return new external_value(PARAM_RAW, 'Additional data for javascript (JSON-encoded string)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of method parameters
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function get_enrolled_courses_by_timeline_classification_parameters() {
|
||||
return new external_function_parameters(
|
||||
array(
|
||||
'classification' => new external_value(PARAM_ALPHA, 'future, inprogress, or past'),
|
||||
'limit' => new external_value(PARAM_INT, 'Result set limit', VALUE_DEFAULT, 0),
|
||||
'offset' => new external_value(PARAM_INT, 'Result set offset', VALUE_DEFAULT, 0),
|
||||
'sort' => new external_value(PARAM_TEXT, 'Sort string', VALUE_DEFAULT, null)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get courses matching the given timeline classification.
|
||||
*
|
||||
* NOTE: The offset applies to the unfiltered full set of courses before the classification
|
||||
* filtering is done.
|
||||
* E.g.
|
||||
* If the user is enrolled in 5 courses:
|
||||
* c1, c2, c3, c4, and c5
|
||||
* And c4 and c5 are 'future' courses
|
||||
*
|
||||
* If a request comes in for future courses with an offset of 1 it will mean that
|
||||
* c1 is skipped (because the offset applies *before* the classification filtering)
|
||||
* and c4 and c5 will be return.
|
||||
*
|
||||
* @param string $classification past, inprogress, or future
|
||||
* @param int $limit Result set limit
|
||||
* @param int $offset Offset the full course set before timeline classification is applied
|
||||
* @param string $sort SQL sort string for results
|
||||
* @return array list of courses and warnings
|
||||
* @throws invalid_parameter_exception
|
||||
*/
|
||||
public static function get_enrolled_courses_by_timeline_classification(
|
||||
string $classification,
|
||||
int $limit = 0,
|
||||
int $offset = 0,
|
||||
string $sort = null
|
||||
) {
|
||||
global $CFG, $PAGE, $USER;
|
||||
require_once($CFG->dirroot . '/course/lib.php');
|
||||
|
||||
$params = self::validate_parameters(self::get_enrolled_courses_by_timeline_classification_parameters(),
|
||||
array(
|
||||
'classification' => $classification,
|
||||
'limit' => $limit,
|
||||
'offset' => $offset,
|
||||
'sort' => $sort,
|
||||
)
|
||||
);
|
||||
|
||||
$classification = $params['classification'];
|
||||
$limit = $params['limit'];
|
||||
$offset = $params['offset'];
|
||||
$sort = $params['sort'];
|
||||
|
||||
switch($classification) {
|
||||
case COURSE_TIMELINE_PAST:
|
||||
break;
|
||||
case COURSE_TIMELINE_INPROGRESS:
|
||||
break;
|
||||
case COURSE_TIMELINE_FUTURE:
|
||||
break;
|
||||
default:
|
||||
throw new invalid_parameter_exception('Invalid classification');
|
||||
}
|
||||
|
||||
self::validate_context(context_user::instance($USER->id));
|
||||
|
||||
$requiredproperties = course_summary_exporter::define_properties();
|
||||
$fields = join(',', array_keys($requiredproperties));
|
||||
$courses = course_get_enrolled_courses_for_logged_in_user(0, $offset, $sort, $fields);
|
||||
list($filteredcourses, $processedcount) = course_filter_courses_by_timeline_classification(
|
||||
$courses,
|
||||
$classification,
|
||||
$limit
|
||||
);
|
||||
|
||||
$renderer = $PAGE->get_renderer('core');
|
||||
$formattedcourses = array_map(function($course) use ($renderer) {
|
||||
context_helper::preload_from_record($course);
|
||||
$context = context_course::instance($course->id);
|
||||
$exporter = new course_summary_exporter($course, ['context' => $context]);
|
||||
return $exporter->export($renderer);
|
||||
}, $filteredcourses);
|
||||
|
||||
return [
|
||||
'courses' => $formattedcourses,
|
||||
'nextoffset' => $offset + $processedcount
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of method result value
|
||||
*
|
||||
* @return external_description
|
||||
*/
|
||||
public static function get_enrolled_courses_by_timeline_classification_returns() {
|
||||
return new external_single_structure(
|
||||
array(
|
||||
'courses' => new external_multiple_structure(course_summary_exporter::get_read_structure(), 'Course'),
|
||||
'nextoffset' => new external_value(PARAM_INT, 'Offset for the next request')
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
121
course/lib.php
121
course/lib.php
|
@ -58,6 +58,7 @@ define('MOD_CLASS_RESOURCE', 1);
|
|||
define('COURSE_TIMELINE_PAST', 'past');
|
||||
define('COURSE_TIMELINE_INPROGRESS', 'inprogress');
|
||||
define('COURSE_TIMELINE_FUTURE', 'future');
|
||||
define('COURSE_DB_QUERY_LIMIT', 1000);
|
||||
|
||||
function make_log_url($module, $url) {
|
||||
switch ($module) {
|
||||
|
@ -4113,6 +4114,126 @@ function course_classify_start_date($course) {
|
|||
return $startdate->getTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Group a list of courses into either past, future, or in progress.
|
||||
*
|
||||
* The return value will be an array indexed by the COURSE_TIMELINE_* constants
|
||||
* with each value being an array of courses in that group.
|
||||
* E.g.
|
||||
* [
|
||||
* COURSE_TIMELINE_PAST => [... list of past courses ...],
|
||||
* COURSE_TIMELINE_FUTURE => [],
|
||||
* COURSE_TIMELINE_INPROGRESS => []
|
||||
* ]
|
||||
*
|
||||
* @param array $courses List of courses to be grouped.
|
||||
* @return array
|
||||
*/
|
||||
function course_classify_courses_for_timeline(array $courses) {
|
||||
return array_reduce($courses, function($carry, $course) {
|
||||
$classification = course_classify_for_timeline($course);
|
||||
array_push($carry[$classification], $course);
|
||||
|
||||
return $carry;
|
||||
}, [
|
||||
COURSE_TIMELINE_PAST => [],
|
||||
COURSE_TIMELINE_FUTURE => [],
|
||||
COURSE_TIMELINE_INPROGRESS => []
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of enrolled courses for the current user.
|
||||
*
|
||||
* This function returns a Generator. The courses will be loaded from the database
|
||||
* in chunks rather than a single query.
|
||||
*
|
||||
* @param int $limit Restrict result set to this amount
|
||||
* @param int $offset Skip this number of records from the start of the result set
|
||||
* @param string|null $sort SQL string for sorting
|
||||
* @param string|null $fields SQL string for fields to be returned
|
||||
* @param int $dbquerylimit The number of records to load per DB request
|
||||
* @return Generator
|
||||
*/
|
||||
function course_get_enrolled_courses_for_logged_in_user(
|
||||
int $limit = 0,
|
||||
int $offset = 0,
|
||||
string $sort = null,
|
||||
string $fields = null,
|
||||
int $dbquerylimit = COURSE_DB_QUERY_LIMIT
|
||||
) : Generator {
|
||||
|
||||
$haslimit = !empty($limit);
|
||||
$recordsloaded = 0;
|
||||
$querylimit = (!$haslimit || $limit > $dbquerylimit) ? $dbquerylimit : $limit;
|
||||
|
||||
while ($courses = enrol_get_my_courses($fields, $sort, $querylimit, [], false, $offset)) {
|
||||
yield from $courses;
|
||||
|
||||
$recordsloaded += $querylimit;
|
||||
|
||||
if (count($courses) < $querylimit) {
|
||||
break;
|
||||
}
|
||||
if ($haslimit && $recordsloaded >= $limit) {
|
||||
break;
|
||||
}
|
||||
|
||||
$offset += $querylimit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the given $courses for any that match the given $classification up to the specified
|
||||
* $limit.
|
||||
*
|
||||
* This function will return the subset of courses that match the classification as well as the
|
||||
* number of courses it had to process to build that subset.
|
||||
*
|
||||
* It is recommended that for larger sets of courses this function is given a Generator that loads
|
||||
* the courses from the database in chunks.
|
||||
*
|
||||
* @param array|Traversable $courses List of courses to process
|
||||
* @param string $classification One of the COURSE_TIMELINE_* constants
|
||||
* @param int $limit Limit the number of results to this amount
|
||||
* @return array First value is the filtered courses, second value is the number of courses processed
|
||||
*/
|
||||
function course_filter_courses_by_timeline_classification(
|
||||
$courses,
|
||||
string $classification,
|
||||
int $limit = 0
|
||||
) : array {
|
||||
|
||||
if (!in_array($classification, [COURSE_TIMELINE_PAST, COURSE_TIMELINE_INPROGRESS, COURSE_TIMELINE_FUTURE])) {
|
||||
$message = 'Classification must be one of COURSE_TIMELINE_PAST, '
|
||||
. 'COURSE_TIMELINE_INPROGRESS or COURSE_TIMELINE_FUTURE';
|
||||
throw new moodle_exception($message);
|
||||
}
|
||||
|
||||
$filteredcourses = [];
|
||||
$numberofcoursesprocessed = 0;
|
||||
$filtermatches = 0;
|
||||
|
||||
foreach ($courses as $course) {
|
||||
$numberofcoursesprocessed++;
|
||||
|
||||
if ($classification == course_classify_for_timeline($course)) {
|
||||
$filteredcourses[] = $course;
|
||||
$filtermatches++;
|
||||
}
|
||||
|
||||
if ($limit && $filtermatches >= $limit) {
|
||||
// We've found the number of requested courses. No need to continue searching.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the number of filtered courses as well as the number of courses that were searched
|
||||
// in order to find the matching courses. This allows the calling code to do some kind of
|
||||
// pagination.
|
||||
return [$filteredcourses, $numberofcoursesprocessed];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check module updates since a given time.
|
||||
* This function checks for updates in the module config, file areas, completion, grades, comments and ratings.
|
||||
|
|
|
@ -4227,4 +4227,494 @@ class core_course_courselib_testcase extends advanced_testcase {
|
|||
assign_capability('moodle/backup:downloadfile', CAP_ALLOW, $teacherrole->id, $context);
|
||||
$this->assertFalse(can_download_from_backup_filearea('testing', $context, $user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cases for the course_classify_courses_for_timeline test.
|
||||
*/
|
||||
public function get_course_classify_courses_for_timeline_test_cases() {
|
||||
$now = time();
|
||||
$day = 86400;
|
||||
|
||||
return [
|
||||
'no courses' => [
|
||||
'coursesdata' => [],
|
||||
'expected' => [
|
||||
COURSE_TIMELINE_PAST => [],
|
||||
COURSE_TIMELINE_FUTURE => [],
|
||||
COURSE_TIMELINE_INPROGRESS => []
|
||||
]
|
||||
],
|
||||
'only past' => [
|
||||
'coursesdata' => [
|
||||
[
|
||||
'shortname' => 'past1',
|
||||
'startdate' => $now - ($day * 2),
|
||||
'enddate' => $now - $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'past2',
|
||||
'startdate' => $now - ($day * 2),
|
||||
'enddate' => $now - $day
|
||||
]
|
||||
],
|
||||
'expected' => [
|
||||
COURSE_TIMELINE_PAST => ['past1', 'past2'],
|
||||
COURSE_TIMELINE_FUTURE => [],
|
||||
COURSE_TIMELINE_INPROGRESS => []
|
||||
]
|
||||
],
|
||||
'only in progress' => [
|
||||
'coursesdata' => [
|
||||
[
|
||||
'shortname' => 'inprogress1',
|
||||
'startdate' => $now - $day,
|
||||
'enddate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'inprogress2',
|
||||
'startdate' => $now - $day,
|
||||
'enddate' => $now + $day
|
||||
]
|
||||
],
|
||||
'expected' => [
|
||||
COURSE_TIMELINE_PAST => [],
|
||||
COURSE_TIMELINE_FUTURE => [],
|
||||
COURSE_TIMELINE_INPROGRESS => ['inprogress1', 'inprogress2']
|
||||
]
|
||||
],
|
||||
'only future' => [
|
||||
'coursesdata' => [
|
||||
[
|
||||
'shortname' => 'future1',
|
||||
'startdate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'future2',
|
||||
'startdate' => $now + $day
|
||||
]
|
||||
],
|
||||
'expected' => [
|
||||
COURSE_TIMELINE_PAST => [],
|
||||
COURSE_TIMELINE_FUTURE => ['future1', 'future2'],
|
||||
COURSE_TIMELINE_INPROGRESS => []
|
||||
]
|
||||
],
|
||||
'combination' => [
|
||||
'coursesdata' => [
|
||||
[
|
||||
'shortname' => 'past1',
|
||||
'startdate' => $now - ($day * 2),
|
||||
'enddate' => $now - $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'past2',
|
||||
'startdate' => $now - ($day * 2),
|
||||
'enddate' => $now - $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'inprogress1',
|
||||
'startdate' => $now - $day,
|
||||
'enddate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'inprogress2',
|
||||
'startdate' => $now - $day,
|
||||
'enddate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'future1',
|
||||
'startdate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'future2',
|
||||
'startdate' => $now + $day
|
||||
]
|
||||
],
|
||||
'expected' => [
|
||||
COURSE_TIMELINE_PAST => ['past1', 'past2'],
|
||||
COURSE_TIMELINE_FUTURE => ['future1', 'future2'],
|
||||
COURSE_TIMELINE_INPROGRESS => ['inprogress1', 'inprogress2']
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the course_classify_courses_for_timeline function.
|
||||
*
|
||||
* @dataProvider get_course_classify_courses_for_timeline_test_cases()
|
||||
* @param array $coursesdata Courses to create
|
||||
* @param array $expected Expected test results.
|
||||
*/
|
||||
public function test_course_classify_courses_for_timeline($coursesdata, $expected) {
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
$courses = array_map(function($coursedata) use ($generator) {
|
||||
return $generator->create_course($coursedata);
|
||||
}, $coursesdata);
|
||||
|
||||
sort($expected[COURSE_TIMELINE_PAST]);
|
||||
sort($expected[COURSE_TIMELINE_FUTURE]);
|
||||
sort($expected[COURSE_TIMELINE_INPROGRESS]);
|
||||
|
||||
$results = course_classify_courses_for_timeline($courses);
|
||||
|
||||
$actualpast = array_map(function($result) {
|
||||
return $result->shortname;
|
||||
}, $results[COURSE_TIMELINE_PAST]);
|
||||
|
||||
$actualfuture = array_map(function($result) {
|
||||
return $result->shortname;
|
||||
}, $results[COURSE_TIMELINE_FUTURE]);
|
||||
|
||||
$actualinprogress = array_map(function($result) {
|
||||
return $result->shortname;
|
||||
}, $results[COURSE_TIMELINE_INPROGRESS]);
|
||||
|
||||
sort($actualpast);
|
||||
sort($actualfuture);
|
||||
sort($actualinprogress);
|
||||
|
||||
$this->assertEquals($expected[COURSE_TIMELINE_PAST], $actualpast);
|
||||
$this->assertEquals($expected[COURSE_TIMELINE_FUTURE], $actualfuture);
|
||||
$this->assertEquals($expected[COURSE_TIMELINE_INPROGRESS], $actualinprogress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cases for the course_get_enrolled_courses_for_logged_in_user tests.
|
||||
*/
|
||||
public function get_course_get_enrolled_courses_for_logged_in_user_test_cases() {
|
||||
$buildexpectedresult = function($limit, $offset) {
|
||||
$result = [];
|
||||
for ($i = $offset; $i < $offset + $limit; $i++) {
|
||||
$result[] = "testcourse{$i}";
|
||||
}
|
||||
return $result;
|
||||
};
|
||||
|
||||
return [
|
||||
'zero records' => [
|
||||
'dbquerylimit' => 3,
|
||||
'totalcourses' => 0,
|
||||
'limit' => 0,
|
||||
'offset' => 0,
|
||||
'expecteddbqueries' => 1,
|
||||
'expectedresult' => $buildexpectedresult(0, 0)
|
||||
],
|
||||
'less than query limit' => [
|
||||
'dbquerylimit' => 3,
|
||||
'totalcourses' => 2,
|
||||
'limit' => 0,
|
||||
'offset' => 0,
|
||||
'expecteddbqueries' => 1,
|
||||
'expectedresult' => $buildexpectedresult(2, 0)
|
||||
],
|
||||
'more than query limit' => [
|
||||
'dbquerylimit' => 3,
|
||||
'totalcourses' => 7,
|
||||
'limit' => 0,
|
||||
'offset' => 0,
|
||||
'expecteddbqueries' => 3,
|
||||
'expectedresult' => $buildexpectedresult(7, 0)
|
||||
],
|
||||
'limit less than query limit' => [
|
||||
'dbquerylimit' => 3,
|
||||
'totalcourses' => 7,
|
||||
'limit' => 2,
|
||||
'offset' => 0,
|
||||
'expecteddbqueries' => 1,
|
||||
'expectedresult' => $buildexpectedresult(2, 0)
|
||||
],
|
||||
'limit less than query limit with offset' => [
|
||||
'dbquerylimit' => 3,
|
||||
'totalcourses' => 7,
|
||||
'limit' => 2,
|
||||
'offset' => 2,
|
||||
'expecteddbqueries' => 1,
|
||||
'expectedresult' => $buildexpectedresult(2, 2)
|
||||
],
|
||||
'limit less than total' => [
|
||||
'dbquerylimit' => 3,
|
||||
'totalcourses' => 9,
|
||||
'limit' => 6,
|
||||
'offset' => 0,
|
||||
'expecteddbqueries' => 2,
|
||||
'expectedresult' => $buildexpectedresult(6, 0)
|
||||
],
|
||||
'less results than limit' => [
|
||||
'dbquerylimit' => 4,
|
||||
'totalcourses' => 9,
|
||||
'limit' => 20,
|
||||
'offset' => 0,
|
||||
'expecteddbqueries' => 3,
|
||||
'expectedresult' => $buildexpectedresult(9, 0)
|
||||
],
|
||||
'less results than limit exact divisible' => [
|
||||
'dbquerylimit' => 3,
|
||||
'totalcourses' => 9,
|
||||
'limit' => 20,
|
||||
'offset' => 0,
|
||||
'expecteddbqueries' => 4,
|
||||
'expectedresult' => $buildexpectedresult(9, 0)
|
||||
],
|
||||
'less results than limit with offset' => [
|
||||
'dbquerylimit' => 3,
|
||||
'totalcourses' => 9,
|
||||
'limit' => 10,
|
||||
'offset' => 5,
|
||||
'expecteddbqueries' => 2,
|
||||
'expectedresult' => $buildexpectedresult(4, 5)
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the course_get_enrolled_courses_for_logged_in_user function.
|
||||
*
|
||||
* @dataProvider get_course_get_enrolled_courses_for_logged_in_user_test_cases()
|
||||
* @param int $dbquerylimit Number of records to load per DB request
|
||||
* @param int $totalcourses Number of courses to create
|
||||
* @param int $limit Maximum number of results to get.
|
||||
* @param int $offset Skip this number of results from the start of the result set.
|
||||
* @param int $expecteddbqueries The number of DB queries expected during the test.
|
||||
* @param array $expectedresult Expected test results.
|
||||
*/
|
||||
public function test_course_get_enrolled_courses_for_logged_in_user(
|
||||
$dbquerylimit,
|
||||
$totalcourses,
|
||||
$limit,
|
||||
$offset,
|
||||
$expecteddbqueries,
|
||||
$expectedresult
|
||||
) {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
$student = $generator->create_user();
|
||||
|
||||
for ($i = 0; $i < $totalcourses; $i++) {
|
||||
$shortname = "testcourse{$i}";
|
||||
$course = $generator->create_course(['shortname' => $shortname]);
|
||||
$generator->enrol_user($student->id, $course->id, 'student');
|
||||
}
|
||||
|
||||
$this->setUser($student);
|
||||
|
||||
$initialquerycount = $DB->perf_get_queries();
|
||||
$courses = course_get_enrolled_courses_for_logged_in_user($limit, $offset, 'shortname ASC', 'shortname', $dbquerylimit);
|
||||
|
||||
// Loop over the result set to force the lazy loading to kick in so that we can check the
|
||||
// number of DB queries.
|
||||
$actualresult = array_map(function($course) {
|
||||
return $course->shortname;
|
||||
}, iterator_to_array($courses, false));
|
||||
|
||||
sort($expectedresult);
|
||||
|
||||
$this->assertEquals($expectedresult, $actualresult);
|
||||
$this->assertEquals($expecteddbqueries, $DB->perf_get_queries() - $initialquerycount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cases for the course_filter_courses_by_timeline_classification tests.
|
||||
*/
|
||||
public function get_course_filter_courses_by_timeline_classification_test_cases() {
|
||||
$now = time();
|
||||
$day = 86400;
|
||||
|
||||
$coursedata = [
|
||||
[
|
||||
'shortname' => 'apast',
|
||||
'startdate' => $now - ($day * 2),
|
||||
'enddate' => $now - $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'bpast',
|
||||
'startdate' => $now - ($day * 2),
|
||||
'enddate' => $now - $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'cpast',
|
||||
'startdate' => $now - ($day * 2),
|
||||
'enddate' => $now - $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'dpast',
|
||||
'startdate' => $now - ($day * 2),
|
||||
'enddate' => $now - $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'epast',
|
||||
'startdate' => $now - ($day * 2),
|
||||
'enddate' => $now - $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'ainprogress',
|
||||
'startdate' => $now - $day,
|
||||
'enddate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'binprogress',
|
||||
'startdate' => $now - $day,
|
||||
'enddate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'cinprogress',
|
||||
'startdate' => $now - $day,
|
||||
'enddate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'dinprogress',
|
||||
'startdate' => $now - $day,
|
||||
'enddate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'einprogress',
|
||||
'startdate' => $now - $day,
|
||||
'enddate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'afuture',
|
||||
'startdate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'bfuture',
|
||||
'startdate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'cfuture',
|
||||
'startdate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'dfuture',
|
||||
'startdate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'efuture',
|
||||
'startdate' => $now + $day
|
||||
]
|
||||
];
|
||||
|
||||
// Raw enrolled courses result set should be returned in this order:
|
||||
// afuture, ainprogress, apast, bfuture, binprogress, bpast, cfuture, cinprogress, cpast,
|
||||
// dfuture, dinprogress, dpast, efuture, einprogress, epast
|
||||
//
|
||||
// By classification the offset values for each record should be:
|
||||
// COURSE_TIMELINE_FUTURE
|
||||
// 0 (afuture), 3 (bfuture), 6 (cfuture), 9 (dfuture), 12 (efuture)
|
||||
// COURSE_TIMELINE_INPROGRESS
|
||||
// 1 (ainprogress), 4 (binprogress), 7 (cinprogress), 10 (dinprogress), 13 (einprogress)
|
||||
// COURSE_TIMELINE_PAST
|
||||
// 2 (apast), 5 (bpast), 8 (cpast), 11 (dpast), 14 (epast).
|
||||
return [
|
||||
'empty set' => [
|
||||
'coursedata' => [],
|
||||
'classification' => COURSE_TIMELINE_FUTURE,
|
||||
'limit' => 2,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => [],
|
||||
'expectedprocessedcount' => 0
|
||||
],
|
||||
// COURSE_TIMELINE_FUTURE.
|
||||
'future not limit no offset' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => COURSE_TIMELINE_FUTURE,
|
||||
'limit' => 0,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['afuture', 'bfuture', 'cfuture', 'dfuture', 'efuture'],
|
||||
'expectedprocessedcount' => 15
|
||||
],
|
||||
'future no offset' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => COURSE_TIMELINE_FUTURE,
|
||||
'limit' => 2,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['afuture', 'bfuture'],
|
||||
'expectedprocessedcount' => 4
|
||||
],
|
||||
'future offset' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => COURSE_TIMELINE_FUTURE,
|
||||
'limit' => 2,
|
||||
'offset' => 2,
|
||||
'expectedcourses' => ['bfuture', 'cfuture'],
|
||||
'expectedprocessedcount' => 5
|
||||
],
|
||||
'future exact limit' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => COURSE_TIMELINE_FUTURE,
|
||||
'limit' => 5,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['afuture', 'bfuture', 'cfuture', 'dfuture', 'efuture'],
|
||||
'expectedprocessedcount' => 13
|
||||
],
|
||||
'future limit less results' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => COURSE_TIMELINE_FUTURE,
|
||||
'limit' => 10,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['afuture', 'bfuture', 'cfuture', 'dfuture', 'efuture'],
|
||||
'expectedprocessedcount' => 15
|
||||
],
|
||||
'future limit less results with offset' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => COURSE_TIMELINE_FUTURE,
|
||||
'limit' => 10,
|
||||
'offset' => 5,
|
||||
'expectedcourses' => ['cfuture', 'dfuture', 'efuture'],
|
||||
'expectedprocessedcount' => 10
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the course_filter_courses_by_timeline_classification function.
|
||||
*
|
||||
* @dataProvider get_course_filter_courses_by_timeline_classification_test_cases()
|
||||
* @param array $coursedata Course test data to create.
|
||||
* @param string $classification Timeline classification.
|
||||
* @param int $limit Maximum number of results to return.
|
||||
* @param int $offset Results to skip at the start of the result set.
|
||||
* @param string[] $expectedcourses Expected courses in results.
|
||||
* @param int $expectedprocessedcount Expected number of course records to be processed.
|
||||
*/
|
||||
public function test_course_filter_courses_by_timeline_classification(
|
||||
$coursedata,
|
||||
$classification,
|
||||
$limit,
|
||||
$offset,
|
||||
$expectedcourses,
|
||||
$expectedprocessedcount
|
||||
) {
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
$courses = array_map(function($coursedata) use ($generator) {
|
||||
return $generator->create_course($coursedata);
|
||||
}, $coursedata);
|
||||
|
||||
$student = $generator->create_user();
|
||||
|
||||
foreach ($courses as $course) {
|
||||
$generator->enrol_user($student->id, $course->id, 'student');
|
||||
}
|
||||
|
||||
$this->setUser($student);
|
||||
|
||||
$coursesgenerator = course_get_enrolled_courses_for_logged_in_user(0, $offset, 'shortname ASC', 'shortname');
|
||||
list($result, $processedcount) = course_filter_courses_by_timeline_classification(
|
||||
$coursesgenerator,
|
||||
$classification,
|
||||
$limit
|
||||
);
|
||||
|
||||
$actual = array_map(function($course) {
|
||||
return $course->shortname;
|
||||
}, $result);
|
||||
|
||||
$this->assertEquals($expectedcourses, $actual);
|
||||
$this->assertEquals($expectedprocessedcount, $processedcount);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2341,4 +2341,218 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
|||
$this->assertCount(1, $result['warnings']);
|
||||
$this->assertEquals(-2, $result['warnings'][0]['itemid']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cases for the get_enrolled_courses_by_timeline_classification test.
|
||||
*/
|
||||
public function get_get_enrolled_courses_by_timeline_classification_test_cases() {
|
||||
$now = time();
|
||||
$day = 86400;
|
||||
|
||||
$coursedata = [
|
||||
[
|
||||
'shortname' => 'apast',
|
||||
'startdate' => $now - ($day * 2),
|
||||
'enddate' => $now - $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'bpast',
|
||||
'startdate' => $now - ($day * 2),
|
||||
'enddate' => $now - $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'cpast',
|
||||
'startdate' => $now - ($day * 2),
|
||||
'enddate' => $now - $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'dpast',
|
||||
'startdate' => $now - ($day * 2),
|
||||
'enddate' => $now - $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'epast',
|
||||
'startdate' => $now - ($day * 2),
|
||||
'enddate' => $now - $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'ainprogress',
|
||||
'startdate' => $now - $day,
|
||||
'enddate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'binprogress',
|
||||
'startdate' => $now - $day,
|
||||
'enddate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'cinprogress',
|
||||
'startdate' => $now - $day,
|
||||
'enddate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'dinprogress',
|
||||
'startdate' => $now - $day,
|
||||
'enddate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'einprogress',
|
||||
'startdate' => $now - $day,
|
||||
'enddate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'afuture',
|
||||
'startdate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'bfuture',
|
||||
'startdate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'cfuture',
|
||||
'startdate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'dfuture',
|
||||
'startdate' => $now + $day
|
||||
],
|
||||
[
|
||||
'shortname' => 'efuture',
|
||||
'startdate' => $now + $day
|
||||
]
|
||||
];
|
||||
|
||||
// Raw enrolled courses result set should be returned in this order:
|
||||
// afuture, ainprogress, apast, bfuture, binprogress, bpast, cfuture, cinprogress, cpast,
|
||||
// dfuture, dinprogress, dpast, efuture, einprogress, epast
|
||||
//
|
||||
// By classification the offset values for each record should be:
|
||||
// COURSE_TIMELINE_FUTURE
|
||||
// 0 (afuture), 3 (bfuture), 6 (cfuture), 9 (dfuture), 12 (efuture)
|
||||
// COURSE_TIMELINE_INPROGRESS
|
||||
// 1 (ainprogress), 4 (binprogress), 7 (cinprogress), 10 (dinprogress), 13 (einprogress)
|
||||
// COURSE_TIMELINE_PAST
|
||||
// 2 (apast), 5 (bpast), 8 (cpast), 11 (dpast), 14 (epast).
|
||||
//
|
||||
// NOTE: The offset applies to the unfiltered full set of courses before the classification
|
||||
// filtering is done.
|
||||
// E.g. In our example if an offset of 2 is given then it would mean the first
|
||||
// two courses (afuture, ainprogress) are ignored.
|
||||
return [
|
||||
'empty set' => [
|
||||
'coursedata' => [],
|
||||
'classification' => 'future',
|
||||
'limit' => 2,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => [],
|
||||
'expectednextoffset' => 0
|
||||
],
|
||||
// COURSE_TIMELINE_FUTURE.
|
||||
'future not limit no offset' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'future',
|
||||
'limit' => 0,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['afuture', 'bfuture', 'cfuture', 'dfuture', 'efuture'],
|
||||
'expectednextoffset' => 15
|
||||
],
|
||||
'future no offset' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'future',
|
||||
'limit' => 2,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['afuture', 'bfuture'],
|
||||
'expectednextoffset' => 4
|
||||
],
|
||||
'future offset' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'future',
|
||||
'limit' => 2,
|
||||
'offset' => 2,
|
||||
'expectedcourses' => ['bfuture', 'cfuture'],
|
||||
'expectednextoffset' => 7
|
||||
],
|
||||
'future exact limit' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'future',
|
||||
'limit' => 5,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['afuture', 'bfuture', 'cfuture', 'dfuture', 'efuture'],
|
||||
'expectednextoffset' => 13
|
||||
],
|
||||
'future limit less results' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'future',
|
||||
'limit' => 10,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['afuture', 'bfuture', 'cfuture', 'dfuture', 'efuture'],
|
||||
'expectednextoffset' => 15
|
||||
],
|
||||
'future limit less results with offset' => [
|
||||
'coursedata' => $coursedata,
|
||||
'classification' => 'future',
|
||||
'limit' => 10,
|
||||
'offset' => 5,
|
||||
'expectedcourses' => ['cfuture', 'dfuture', 'efuture'],
|
||||
'expectednextoffset' => 15
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the get_enrolled_courses_by_timeline_classification function.
|
||||
*
|
||||
* @dataProvider get_get_enrolled_courses_by_timeline_classification_test_cases()
|
||||
* @param array $coursedata Courses to create
|
||||
* @param string $classification Timeline classification
|
||||
* @param int $limit Maximum number of results
|
||||
* @param int $offset Offset the unfiltered courses result set by this amount
|
||||
* @param array $expectedcourses Expected courses in result
|
||||
* @param int $expectednextoffset Expected next offset value in result
|
||||
*/
|
||||
public function test_get_enrolled_courses_by_timeline_classification(
|
||||
$coursedata,
|
||||
$classification,
|
||||
$limit,
|
||||
$offset,
|
||||
$expectedcourses,
|
||||
$expectednextoffset
|
||||
) {
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
$courses = array_map(function($coursedata) use ($generator) {
|
||||
return $generator->create_course($coursedata);
|
||||
}, $coursedata);
|
||||
|
||||
$student = $generator->create_user();
|
||||
|
||||
foreach ($courses as $course) {
|
||||
$generator->enrol_user($student->id, $course->id, 'student');
|
||||
}
|
||||
|
||||
$this->setUser($student);
|
||||
|
||||
// NOTE: The offset applies to the unfiltered full set of courses before the classification
|
||||
// filtering is done.
|
||||
// E.g. In our example if an offset of 2 is given then it would mean the first
|
||||
// two courses (afuture, ainprogress) are ignored.
|
||||
$result = core_course_external::get_enrolled_courses_by_timeline_classification(
|
||||
$classification,
|
||||
$limit,
|
||||
$offset,
|
||||
'shortname ASC'
|
||||
);
|
||||
$result = external_api::clean_returnvalue(
|
||||
core_course_external::get_enrolled_courses_by_timeline_classification_returns(),
|
||||
$result
|
||||
);
|
||||
|
||||
$actual = array_map(function($course) {
|
||||
return $course['shortname'];
|
||||
}, $result['courses']);
|
||||
|
||||
$this->assertEquals($expectedcourses, $actual);
|
||||
$this->assertEquals($expectednextoffset, $result['nextoffset']);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue