mirror of
https://github.com/moodle/moodle.git
synced 2025-03-14 04:30:15 +01:00
Merge branch 'MDL-59256_master' of git://github.com/dmonllao/moodle
This commit is contained in:
commit
5243e93661
@ -156,11 +156,12 @@ function tool_analytics_calculate_course_dates($course, $options) {
|
||||
$format = course_get_format($course);
|
||||
$formatoptions = $format->get_format_options();
|
||||
|
||||
if ($course->format === 'weeks' && $formatoptions['automaticenddate']) {
|
||||
// Special treatment for weeks with automatic end date.
|
||||
// Change this for a course formats API level call in MDL-60702.
|
||||
if (method_exists($format, 'update_end_date') && $formatoptions['automaticenddate']) {
|
||||
// Special treatment for weeks-based formats with automatic end date.
|
||||
|
||||
if ($options['update']) {
|
||||
format_weeks::update_end_date($course->id);
|
||||
$format::update_end_date($course->id);
|
||||
$course->enddate = $DB->get_field('course', 'enddate', array('id' => $course->id));
|
||||
$notification .= PHP_EOL . ' ' . get_string('weeksenddateautomaticallyset', 'tool_analytics') . ': ' .
|
||||
userdate($course->enddate);
|
||||
|
@ -547,172 +547,6 @@ class course implements \core_analytics\analysable {
|
||||
return $grades;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guesses all activities that were available during a period of time.
|
||||
*
|
||||
* @param string $activitytype
|
||||
* @param int $starttime
|
||||
* @param int $endtime
|
||||
* @param \stdClass $student
|
||||
* @return array
|
||||
*/
|
||||
public function get_activities($activitytype, $starttime, $endtime, $student = false) {
|
||||
|
||||
// Var $student may not be available, default to not calculating dynamic data.
|
||||
$studentid = -1;
|
||||
if ($student) {
|
||||
$studentid = $student->id;
|
||||
}
|
||||
$modinfo = get_fast_modinfo($this->get_course_data(), $studentid);
|
||||
$activities = $modinfo->get_instances_of($activitytype);
|
||||
|
||||
$timerangeactivities = array();
|
||||
foreach ($activities as $activity) {
|
||||
if (!$this->completed_by($activity, $starttime, $endtime)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$timerangeactivities[$activity->context->id] = $activity;
|
||||
}
|
||||
|
||||
return $timerangeactivities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Was the activity supposed to be completed during the provided time range?.
|
||||
*
|
||||
* @param \cm_info $activity
|
||||
* @param int $starttime
|
||||
* @param int $endtime
|
||||
* @return bool
|
||||
*/
|
||||
protected function completed_by(\cm_info $activity, $starttime, $endtime) {
|
||||
|
||||
// We can't check uservisible because:
|
||||
// - Any activity with available until would not be counted.
|
||||
// - Sites may block student's course view capabilities once the course is closed.
|
||||
|
||||
// Students can not view hidden activities by default, this is not reliable 100% but accurate in most of the cases.
|
||||
if ($activity->visible === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We skip activities that were not yet visible or their 'until' was not in this $starttime - $endtime range.
|
||||
if ($activity->availability) {
|
||||
$info = new \core_availability\info_module($activity);
|
||||
$activityavailability = $this->availability_completed_by($info, $starttime, $endtime);
|
||||
if ($activityavailability === false) {
|
||||
return false;
|
||||
} else if ($activityavailability === true) {
|
||||
// This activity belongs to this time range.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// We skip activities in sections that were not yet visible or their 'until' was not in this $starttime - $endtime range.
|
||||
$section = $activity->get_modinfo()->get_section_info($activity->sectionnum);
|
||||
if ($section->availability) {
|
||||
$info = new \core_availability\info_section($section);
|
||||
$sectionavailability = $this->availability_completed_by($info, $starttime, $endtime);
|
||||
if ($sectionavailability === false) {
|
||||
return false;
|
||||
} else if ($sectionavailability === true) {
|
||||
// This activity belongs to this section time range.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// When the course is using format weeks we use the week's end date.
|
||||
$format = course_get_format($activity->get_modinfo()->get_course());
|
||||
if ($this->get_course_data()->format === 'weeks') {
|
||||
$dates = $format->get_section_dates($section);
|
||||
|
||||
// We need to consider the +2 hours added by get_section_dates.
|
||||
// Avoid $starttime <= $dates->end because $starttime may be the start of the next week.
|
||||
if ($starttime < ($dates->end - 7200) && $endtime >= ($dates->end - 7200)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($activity->sectionnum == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->get_end() || !$this->get_start()) {
|
||||
debugging('Activities which due date is in a time range can not be calculated ' .
|
||||
'if the course doesn\'t have start and end date', DEBUG_DEVELOPER);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!course_format_uses_sections($this->get_course_data()->format)) {
|
||||
// If it does not use sections and there are no availability conditions to access it it is available
|
||||
// and we can not magically classify it into any other time range than this one.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Split the course duration in the number of sections and consider the end of each section the due
|
||||
// date of all activities contained in that section.
|
||||
$formatoptions = $format->get_format_options();
|
||||
if (!empty($formatoptions['numsections'])) {
|
||||
$nsections = $formatoptions['numsections'];
|
||||
} else {
|
||||
// There are course format that use sections but without numsections, we fallback to the number
|
||||
// of cached sections in get_section_info_all, not that accurate though.
|
||||
$coursesections = $activity->get_modinfo()->get_section_info_all();
|
||||
$nsections = count($coursesections);
|
||||
if (isset($coursesections[0])) {
|
||||
// We don't count section 0 if it exists.
|
||||
$nsections--;
|
||||
}
|
||||
}
|
||||
|
||||
$courseduration = $this->get_end() - $this->get_start();
|
||||
$sectionduration = round($courseduration / $nsections);
|
||||
$activitysectionenddate = $this->get_start() + ($sectionduration * $activity->sectionnum);
|
||||
if ($activitysectionenddate > $starttime && $activitysectionenddate <= $endtime) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the activity/section should have been completed during the provided period according to its availability rules.
|
||||
*
|
||||
* @param \core_availability\info $info
|
||||
* @param int $starttime
|
||||
* @param int $endtime
|
||||
* @return bool|null
|
||||
*/
|
||||
protected function availability_completed_by(\core_availability\info $info, $starttime, $endtime) {
|
||||
|
||||
$dateconditions = $info->get_availability_tree()->get_all_children('\availability_date\condition');
|
||||
foreach ($dateconditions as $condition) {
|
||||
// Availability API does not allow us to check from / to dates nicely, we need to be naughty.
|
||||
$conditiondata = $condition->save();
|
||||
|
||||
if ($conditiondata->d === \availability_date\condition::DIRECTION_FROM &&
|
||||
$conditiondata->t > $endtime) {
|
||||
// Skip this activity if any 'from' date is later than the end time.
|
||||
return false;
|
||||
|
||||
} else if ($conditiondata->d === \availability_date\condition::DIRECTION_UNTIL &&
|
||||
($conditiondata->t < $starttime || $conditiondata->t > $endtime)) {
|
||||
// Skip activity if any 'until' date is not in $starttime - $endtime range.
|
||||
return false;
|
||||
} else if ($conditiondata->d === \availability_date\condition::DIRECTION_UNTIL &&
|
||||
$conditiondata->t < $endtime && $conditiondata->t > $starttime) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// This can be interpreted as 'the activity was available but we don't know if its expected completion date
|
||||
// was during this period.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by get_user_ids to extract the user id.
|
||||
*
|
||||
|
@ -35,6 +35,13 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*/
|
||||
abstract class community_of_inquiry_activity extends linear {
|
||||
|
||||
/**
|
||||
* instancedata
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $instancedata = array();
|
||||
|
||||
/**
|
||||
* @var \core_analytics\course
|
||||
*/
|
||||
@ -478,8 +485,8 @@ abstract class community_of_inquiry_activity extends linear {
|
||||
// Samples are at cm level or below.
|
||||
$useractivities = array(\context_module::instance($cm->id)->id => $cm);
|
||||
} else {
|
||||
// All course activities.
|
||||
$useractivities = $this->course->get_activities($this->get_activity_type(), $starttime, $endtime, $user);
|
||||
// Activities that should be completed during this time period.
|
||||
$useractivities = $this->get_activities($starttime, $endtime, $user);
|
||||
}
|
||||
|
||||
return $useractivities;
|
||||
@ -741,6 +748,250 @@ abstract class community_of_inquiry_activity extends linear {
|
||||
$this->grades = $course->get_student_grades($courseactivities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guesses all activities that were available during a period of time.
|
||||
*
|
||||
* @param int $starttime
|
||||
* @param int $endtime
|
||||
* @param \stdClass|false $student
|
||||
* @return array
|
||||
*/
|
||||
protected function get_activities($starttime, $endtime, $student = false) {
|
||||
|
||||
$activitytype = $this->get_activity_type();
|
||||
|
||||
// Var $student may not be available, default to not calculating dynamic data.
|
||||
$studentid = -1;
|
||||
if ($student) {
|
||||
$studentid = $student->id;
|
||||
}
|
||||
$modinfo = get_fast_modinfo($this->course->get_course_data(), $studentid);
|
||||
$activities = $modinfo->get_instances_of($activitytype);
|
||||
|
||||
$timerangeactivities = array();
|
||||
foreach ($activities as $activity) {
|
||||
|
||||
if (!$this->activity_completed_by($activity, $starttime, $endtime, $student)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$timerangeactivities[$activity->context->id] = $activity;
|
||||
}
|
||||
|
||||
return $timerangeactivities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Was the activity supposed to be completed during the provided time range?.
|
||||
*
|
||||
* @param \cm_info $activity
|
||||
* @param int $starttime
|
||||
* @param int $endtime
|
||||
* @param \stdClass|false $student
|
||||
* @return bool
|
||||
*/
|
||||
protected function activity_completed_by(\cm_info $activity, $starttime, $endtime, $student = false) {
|
||||
|
||||
// We can't check uservisible because:
|
||||
// - Any activity with available until would not be counted.
|
||||
// - Sites may block student's course view capabilities once the course is closed.
|
||||
|
||||
// Students can not view hidden activities by default, this is not reliable 100% but accurate in most of the cases.
|
||||
if ($activity->visible === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Give priority to the different methods activities have to set a "due" date.
|
||||
$return = $this->activity_type_completed_by($activity, $starttime, $endtime, $student);
|
||||
if (!is_null($return)) {
|
||||
// Method activity_type_completed_by returns null if there is no due date method or there is but it is not set.
|
||||
return $return;
|
||||
}
|
||||
|
||||
// We skip activities that were not yet visible or their 'until' was not in this $starttime - $endtime range.
|
||||
if ($activity->availability) {
|
||||
$info = new \core_availability\info_module($activity);
|
||||
$activityavailability = $this->availability_completed_by($info, $starttime, $endtime);
|
||||
if ($activityavailability === false) {
|
||||
return false;
|
||||
} else if ($activityavailability === true) {
|
||||
// This activity belongs to this time range.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// We skip activities in sections that were not yet visible or their 'until' was not in this $starttime - $endtime range.
|
||||
$section = $activity->get_modinfo()->get_section_info($activity->sectionnum);
|
||||
if ($section->availability) {
|
||||
$info = new \core_availability\info_section($section);
|
||||
$sectionavailability = $this->availability_completed_by($info, $starttime, $endtime);
|
||||
if ($sectionavailability === false) {
|
||||
return false;
|
||||
} else if ($sectionavailability === true) {
|
||||
// This activity belongs to this section time range.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// When the course is using format weeks we use the week's end date.
|
||||
$format = course_get_format($activity->get_modinfo()->get_course());
|
||||
// We should change this in MDL-60702.
|
||||
if (method_exists($format, 'get_section_dates')) {
|
||||
$dates = $format->get_section_dates($section);
|
||||
|
||||
// We need to consider the +2 hours added by get_section_dates.
|
||||
// Avoid $starttime <= $dates->end because $starttime may be the start of the next week.
|
||||
if ($starttime < ($dates->end - 7200) && $endtime >= ($dates->end - 7200)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($activity->sectionnum == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->course->get_end() || !$this->course->get_start()) {
|
||||
debugging('Activities which due date is in a time range can not be calculated ' .
|
||||
'if the course doesn\'t have start and end date', DEBUG_DEVELOPER);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!course_format_uses_sections($this->course->get_course_data()->format)) {
|
||||
// If it does not use sections and there are no availability conditions to access it it is available
|
||||
// and we can not magically classify it into any other time range than this one.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Split the course duration in the number of sections and consider the end of each section the due
|
||||
// date of all activities contained in that section.
|
||||
$formatoptions = $format->get_format_options();
|
||||
if (!empty($formatoptions['numsections'])) {
|
||||
$nsections = $formatoptions['numsections'];
|
||||
} else {
|
||||
// There are course format that use sections but without numsections, we fallback to the number
|
||||
// of cached sections in get_section_info_all, not that accurate though.
|
||||
$coursesections = $activity->get_modinfo()->get_section_info_all();
|
||||
$nsections = count($coursesections);
|
||||
if (isset($coursesections[0])) {
|
||||
// We don't count section 0 if it exists.
|
||||
$nsections--;
|
||||
}
|
||||
}
|
||||
|
||||
$courseduration = $this->course->get_end() - $this->course->get_start();
|
||||
$sectionduration = round($courseduration / $nsections);
|
||||
$activitysectionenddate = $this->course->get_start() + ($sectionduration * $activity->sectionnum);
|
||||
if ($activitysectionenddate > $starttime && $activitysectionenddate <= $endtime) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the activity is due or it has been closed during this period, false if during another period, null if no due time.
|
||||
*
|
||||
* It can be overwritten by activities that allow teachers to set a due date or a time close separately
|
||||
* from Moodle availability system. Note that in most of the cases overwriting get_timeclose_field should
|
||||
* be enough.
|
||||
*
|
||||
* Returns true or false if the time close date falls into the provided time range. Null otherwise.
|
||||
*
|
||||
* @param \cm_info $activity
|
||||
* @param int $starttime
|
||||
* @param int $endtime
|
||||
* @param \stdClass|false $student
|
||||
* @return null
|
||||
*/
|
||||
protected function activity_type_completed_by(\cm_info $activity, $starttime, $endtime, $student = false) {
|
||||
|
||||
$fieldname = $this->get_timeclose_field();
|
||||
if (!$fieldname) {
|
||||
// This activity type do not have its own availability control.
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->fill_instance_data($activity);
|
||||
$instance = $this->instancedata[$activity->instance];
|
||||
|
||||
if (!$instance->{$fieldname}) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($starttime < $instance->{$fieldname} && $endtime >= $instance->{$fieldname}) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the field that controls activity availability.
|
||||
*
|
||||
* Should be overwritten by activities that allow teachers to set a due date or a time close separately
|
||||
* from Moodle availability system.
|
||||
*
|
||||
* Just 1 field will not be enough for all cases, but for the most simple ones without
|
||||
* overrides and stuff like that.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function get_timeclose_field() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the activity/section should have been completed during the provided period according to its availability rules.
|
||||
*
|
||||
* @param \core_availability\info $info
|
||||
* @param int $starttime
|
||||
* @param int $endtime
|
||||
* @return bool|null
|
||||
*/
|
||||
protected function availability_completed_by(\core_availability\info $info, $starttime, $endtime) {
|
||||
|
||||
$dateconditions = $info->get_availability_tree()->get_all_children('\availability_date\condition');
|
||||
foreach ($dateconditions as $condition) {
|
||||
// Availability API does not allow us to check from / to dates nicely, we need to be naughty.
|
||||
$conditiondata = $condition->save();
|
||||
|
||||
if ($conditiondata->d === \availability_date\condition::DIRECTION_FROM &&
|
||||
$conditiondata->t > $endtime) {
|
||||
// Skip this activity if any 'from' date is later than the end time.
|
||||
return false;
|
||||
|
||||
} else if ($conditiondata->d === \availability_date\condition::DIRECTION_UNTIL &&
|
||||
($conditiondata->t < $starttime || $conditiondata->t > $endtime)) {
|
||||
// Skip activity if any 'until' date is not in $starttime - $endtime range.
|
||||
return false;
|
||||
} else if ($conditiondata->d === \availability_date\condition::DIRECTION_UNTIL &&
|
||||
$conditiondata->t < $endtime && $conditiondata->t > $starttime) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// This can be interpreted as 'the activity was available but we don't know if its expected completion date
|
||||
// was during this period.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills in activity instance data.
|
||||
*
|
||||
* @param \cm_info $cm
|
||||
* @return void
|
||||
*/
|
||||
protected function fill_instance_data(\cm_info $cm) {
|
||||
global $DB;
|
||||
|
||||
if (!isset($this->instancedata[$cm->instance])) {
|
||||
$this->instancedata[$cm->instance] = $DB->get_record($this->get_activity_type(), array('id' => $cm->instance),
|
||||
'*', MUST_EXIST);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines indicator type.
|
||||
*
|
||||
|
@ -15,7 +15,7 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Unit tests for course activities.
|
||||
* Unit tests for activities completed by classification.
|
||||
*
|
||||
* @package core_analytics
|
||||
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
|
||||
@ -25,13 +25,13 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Unit tests for course activities
|
||||
* Unit tests for activities completed by classification.
|
||||
*
|
||||
* @package core_analytics
|
||||
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_analytics_course_activities_testcase extends advanced_testcase {
|
||||
class community_of_inquiry_activities_completed_by_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* availability_levels
|
||||
@ -59,8 +59,6 @@ class core_analytics_course_activities_testcase extends advanced_testcase {
|
||||
// Forum1 is ignored as section 0 does not count.
|
||||
$forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
|
||||
|
||||
$courseman = new \core_analytics\course($course);
|
||||
|
||||
$modinfo = get_fast_modinfo($course, $stu1->id);
|
||||
$cm = $modinfo->get_cm($forum->cmid);
|
||||
|
||||
@ -93,20 +91,23 @@ class core_analytics_course_activities_testcase extends advanced_testcase {
|
||||
|
||||
$cm = $modinfo->get_cm($forum->cmid);
|
||||
|
||||
$course = new \core_analytics\course($course);
|
||||
list($indicator, $method) = $this->instantiate_indicator('mod_forum', $course);
|
||||
|
||||
// Condition from after provided end time.
|
||||
$this->assertCount(0, $courseman->get_activities('forum', strtotime('2015-10-20 00:00:00 GMT'),
|
||||
$this->assertCount(0, $method->invoke($indicator, strtotime('2015-10-20 00:00:00 GMT'),
|
||||
strtotime('2015-10-21 00:00:00 GMT'), $stu1));
|
||||
|
||||
// Condition until before provided start time
|
||||
$this->assertCount(0, $courseman->get_activities('forum', strtotime('2015-10-25 00:00:00 GMT'),
|
||||
$this->assertCount(0, $method->invoke($indicator, strtotime('2015-10-25 00:00:00 GMT'),
|
||||
strtotime('2015-10-26 00:00:00 GMT'), $stu1));
|
||||
|
||||
// Condition until after provided end time.
|
||||
$this->assertCount(0, $courseman->get_activities('forum', strtotime('2015-10-22 00:00:00 GMT'),
|
||||
$this->assertCount(0, $method->invoke($indicator, strtotime('2015-10-22 00:00:00 GMT'),
|
||||
strtotime('2015-10-23 00:00:00 GMT'), $stu1));
|
||||
|
||||
// Condition until after provided start time and before provided end time.
|
||||
$this->assertCount(1, $courseman->get_activities('forum', strtotime('2015-10-22 00:00:00 GMT'),
|
||||
$this->assertCount(1, $method->invoke($indicator, strtotime('2015-10-22 00:00:00 GMT'),
|
||||
strtotime('2015-10-25 00:00:00 GMT'), $stu1));
|
||||
}
|
||||
|
||||
@ -138,7 +139,8 @@ class core_analytics_course_activities_testcase extends advanced_testcase {
|
||||
$forum5 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
|
||||
array('section' => 4));
|
||||
|
||||
$courseman = new \core_analytics\course($course);
|
||||
$course = new \core_analytics\course($course);
|
||||
list($indicator, $method) = $this->instantiate_indicator('mod_forum', $course);
|
||||
|
||||
$this->setUser($stu1);
|
||||
|
||||
@ -146,10 +148,10 @@ class core_analytics_course_activities_testcase extends advanced_testcase {
|
||||
$second = $startdate + WEEKSECS;
|
||||
$third = $startdate + (WEEKSECS * 2);
|
||||
$forth = $startdate + (WEEKSECS * 3);
|
||||
$this->assertCount(1, $courseman->get_activities('forum', $first, $first + WEEKSECS, $stu1));
|
||||
$this->assertCount(1, $courseman->get_activities('forum', $second, $second + WEEKSECS, $stu1));
|
||||
$this->assertCount(0, $courseman->get_activities('forum', $third, $third + WEEKSECS, $stu1));
|
||||
$this->assertCount(2, $courseman->get_activities('forum', $forth, $forth + WEEKSECS, $stu1));
|
||||
$this->assertCount(1, $method->invoke($indicator, $first, $first + WEEKSECS, $stu1));
|
||||
$this->assertCount(1, $method->invoke($indicator, $second, $second + WEEKSECS, $stu1));
|
||||
$this->assertCount(0, $method->invoke($indicator, $third, $third + WEEKSECS, $stu1));
|
||||
$this->assertCount(2, $method->invoke($indicator, $forth, $forth + WEEKSECS, $stu1));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -189,7 +191,8 @@ class core_analytics_course_activities_testcase extends advanced_testcase {
|
||||
$forum6 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
|
||||
array('section' => 12));
|
||||
|
||||
$courseman = new \core_analytics\course($course);
|
||||
$course = new \core_analytics\course($course);
|
||||
list($indicator, $method) = $this->instantiate_indicator('mod_forum', $course);
|
||||
|
||||
$this->setUser($stu1);
|
||||
|
||||
@ -199,10 +202,10 @@ class core_analytics_course_activities_testcase extends advanced_testcase {
|
||||
$second = $startdate + $duration;
|
||||
$third = $startdate + ($duration * 2);
|
||||
$forth = $startdate + ($duration * 3);
|
||||
$this->assertCount(1, $courseman->get_activities('forum', $first, $first + $duration, $stu1));
|
||||
$this->assertCount(1, $courseman->get_activities('forum', $second, $second + $duration, $stu1));
|
||||
$this->assertCount(1, $courseman->get_activities('forum', $third, $third + $duration, $stu1));
|
||||
$this->assertCount(2, $courseman->get_activities('forum', $forth, $forth + $duration, $stu1));
|
||||
$this->assertCount(1, $method->invoke($indicator, $first, $first + $duration, $stu1));
|
||||
$this->assertCount(1, $method->invoke($indicator, $second, $second + $duration, $stu1));
|
||||
$this->assertCount(1, $method->invoke($indicator, $third, $third + $duration, $stu1));
|
||||
$this->assertCount(2, $method->invoke($indicator, $forth, $forth + $duration, $stu1));
|
||||
|
||||
// Split the course in as many parts as sections.
|
||||
$duration = ($enddate - $startdate) / $numsections;
|
||||
@ -210,15 +213,94 @@ class core_analytics_course_activities_testcase extends advanced_testcase {
|
||||
// The -1 because section 1 start represents the course start.
|
||||
$timeranges[$i] = $startdate + ($duration * ($i - 1));
|
||||
}
|
||||
$this->assertCount(1, $courseman->get_activities('forum', $timeranges[1], $timeranges[1] + $duration, $stu1));
|
||||
$this->assertCount(1, $courseman->get_activities('forum', $timeranges[4], $timeranges[4] + $duration, $stu1));
|
||||
$this->assertCount(1, $courseman->get_activities('forum', $timeranges[8], $timeranges[8] + $duration, $stu1));
|
||||
$this->assertCount(1, $courseman->get_activities('forum', $timeranges[10], $timeranges[10] + $duration, $stu1));
|
||||
$this->assertCount(1, $courseman->get_activities('forum', $timeranges[12], $timeranges[12] + $duration, $stu1));
|
||||
$this->assertCount(1, $method->invoke($indicator, $timeranges[1], $timeranges[1] + $duration, $stu1));
|
||||
$this->assertCount(1, $method->invoke($indicator, $timeranges[4], $timeranges[4] + $duration, $stu1));
|
||||
$this->assertCount(1, $method->invoke($indicator, $timeranges[8], $timeranges[8] + $duration, $stu1));
|
||||
$this->assertCount(1, $method->invoke($indicator, $timeranges[10], $timeranges[10] + $duration, $stu1));
|
||||
$this->assertCount(1, $method->invoke($indicator, $timeranges[12], $timeranges[12] + $duration, $stu1));
|
||||
|
||||
// Nothing here.
|
||||
$this->assertCount(0, $courseman->get_activities('forum', $timeranges[2], $timeranges[2] + $duration, $stu1));
|
||||
$this->assertCount(0, $courseman->get_activities('forum', $timeranges[3], $timeranges[3] + $duration, $stu1));
|
||||
$this->assertCount(0, $method->invoke($indicator, $timeranges[2], $timeranges[2] + $duration, $stu1));
|
||||
$this->assertCount(0, $method->invoke($indicator, $timeranges[3], $timeranges[3] + $duration, $stu1));
|
||||
}
|
||||
|
||||
/**
|
||||
* test_get_activities_with_specific_restrictions
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_get_activities_with_specific_restrictions() {
|
||||
|
||||
list($course, $stu1) = $this->setup_course();
|
||||
|
||||
$end = strtotime('2015-10-24 00:00:00 GMT');
|
||||
|
||||
// 1 with time close, one without.
|
||||
$params = array('course' => $course->id);
|
||||
$assign1 = $this->getDataGenerator()->create_module('assign', $params);
|
||||
$params['duedate'] = $end;
|
||||
$assign2 = $this->getDataGenerator()->create_module('assign', $params);
|
||||
|
||||
// 1 with time close, one without.
|
||||
$params = array('course' => $course->id);
|
||||
$choice1 = $this->getDataGenerator()->create_module('choice', $params);
|
||||
$params['timeclose'] = $end;
|
||||
$choice1 = $this->getDataGenerator()->create_module('choice', $params);
|
||||
|
||||
// 1 with time close, one without.
|
||||
$params = array('course' => $course->id);
|
||||
$data1 = $this->getDataGenerator()->create_module('data', $params);
|
||||
$params['timeavailableto'] = $end;
|
||||
$data1 = $this->getDataGenerator()->create_module('data', $params);
|
||||
|
||||
// 1 with time close, one without.
|
||||
$params = array('course' => $course->id);
|
||||
$feedback1 = $this->getDataGenerator()->create_module('feedback', $params);
|
||||
$params['timeclose'] = $end;
|
||||
$feedback1 = $this->getDataGenerator()->create_module('feedback', $params);
|
||||
|
||||
// 1 with time close, one without.
|
||||
$params = array('course' => $course->id);
|
||||
$lesson1 = $this->getDataGenerator()->create_module('lesson', $params);
|
||||
$params['deadline'] = $end;
|
||||
$lesson1 = $this->getDataGenerator()->create_module('lesson', $params);
|
||||
|
||||
// 1 with time close, one without.
|
||||
$params = array('course' => $course->id);
|
||||
$quiz1 = $this->getDataGenerator()->create_module('quiz', $params);
|
||||
$params['timeclose'] = $end;
|
||||
$quiz1 = $this->getDataGenerator()->create_module('quiz', $params);
|
||||
|
||||
// 1 with time close, one without.
|
||||
$params = array('course' => $course->id);
|
||||
$scorm1 = $this->getDataGenerator()->create_module('scorm', $params);
|
||||
$params['timeclose'] = $end;
|
||||
$scorm1 = $this->getDataGenerator()->create_module('scorm', $params);
|
||||
|
||||
// 1 with time close, one without.
|
||||
$params = array('course' => $course->id);
|
||||
$workshop1 = $this->getDataGenerator()->create_module('workshop', $params);
|
||||
$params['submissionend'] = $end;
|
||||
$workshop1 = $this->getDataGenerator()->create_module('workshop', $params);
|
||||
|
||||
$course = new \core_analytics\course($course);
|
||||
|
||||
$activitytypes = array('mod_assign', 'mod_choice', 'mod_data', 'mod_feedback', 'mod_lesson',
|
||||
'mod_quiz', 'mod_scorm', 'mod_workshop');
|
||||
foreach ($activitytypes as $activitytype) {
|
||||
|
||||
list($indicator, $method) = $this->instantiate_indicator($activitytype, $course);
|
||||
|
||||
$message = $activitytype . ' activity type returned activities do not match expected size';
|
||||
$this->assertCount(0, $method->invoke($indicator, strtotime('2015-10-20 00:00:00 GMT'),
|
||||
strtotime('2015-10-21 00:00:00 GMT'), $stu1), $message);
|
||||
|
||||
$this->assertCount(0, $method->invoke($indicator, strtotime('2015-10-25 00:00:00 GMT'),
|
||||
strtotime('2015-10-26 00:00:00 GMT'), $stu1), $message);
|
||||
|
||||
$this->assertCount(1, $method->invoke($indicator, strtotime('2015-10-22 00:00:00 GMT'),
|
||||
strtotime('2015-10-25 00:00:00 GMT'), $stu1), $message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -243,4 +325,27 @@ class core_analytics_course_activities_testcase extends advanced_testcase {
|
||||
return array($course, $stu1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the module cognitive depth indicator and the reflection method.
|
||||
*
|
||||
* @param string $modulename
|
||||
* @param \core_analytics\course $course
|
||||
* @return array
|
||||
*/
|
||||
private function instantiate_indicator($modulename, \core_analytics\course $course) {
|
||||
|
||||
$classname = '\\' . $modulename . '\analytics\indicator\cognitive_depth';
|
||||
$indicator = new $classname();
|
||||
$class = new ReflectionClass($indicator);
|
||||
|
||||
$property = $class->getProperty('course');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue($indicator, $course);
|
||||
|
||||
$method = new ReflectionMethod($indicator, 'get_activities');
|
||||
$method->setAccessible(true);
|
||||
|
||||
return array($indicator, $method);
|
||||
}
|
||||
|
||||
}
|
@ -53,4 +53,14 @@ abstract class activity_base extends \core_analytics\local\indicator\community_o
|
||||
// We need the grade to be released to the student to consider that feedback has been provided.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the field that controls activity availability.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function get_timeclose_field() {
|
||||
return 'duedate';
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,13 +35,6 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*/
|
||||
abstract class activity_base extends \core_analytics\local\indicator\community_of_inquiry_activity {
|
||||
|
||||
/**
|
||||
* choicedata
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $choicedata = array();
|
||||
|
||||
/**
|
||||
* feedback_viewed_events
|
||||
*
|
||||
@ -51,21 +44,6 @@ abstract class activity_base extends \core_analytics\local\indicator\community_o
|
||||
return array('\mod_choice\event\course_module_viewed', '\mod_choice\event\answer_updated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills choice activities data.
|
||||
*
|
||||
* @param \cm_info $cm
|
||||
* @return void
|
||||
*/
|
||||
protected function fill_choice_data(\cm_info $cm) {
|
||||
global $DB;
|
||||
|
||||
if (!isset($this->choicedata[$cm->instance])) {
|
||||
$this->choicedata[$cm->instance] = $DB->get_record('choice', array('id' => $cm->instance),
|
||||
'id, showresults, timeclose', MUST_EXIST);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* feedback_viewed
|
||||
*
|
||||
@ -78,17 +56,26 @@ abstract class activity_base extends \core_analytics\local\indicator\community_o
|
||||
protected function feedback_viewed(\cm_info $cm, $contextid, $userid, $after = null) {
|
||||
|
||||
// If results are shown after they answer a write action counts as feedback viewed.
|
||||
if ($this->choicedata[$cm->instance]->showresults == 1) {
|
||||
if ($this->instancedata[$cm->instance]->showresults == 1) {
|
||||
// The user id will be enough for any_write_log.
|
||||
$user = (object)['id' => $userid];
|
||||
return $this->any_write_log($contextid, $user);
|
||||
}
|
||||
|
||||
$after = null;
|
||||
if ($this->choicedata[$cm->instance]->timeclose) {
|
||||
$after = $this->choicedata[$cm->instance]->timeclose;
|
||||
if ($this->instancedata[$cm->instance]->timeclose) {
|
||||
$after = $this->instancedata[$cm->instance]->timeclose;
|
||||
}
|
||||
|
||||
return $this->feedback_post_action($cm, $contextid, $userid, $this->feedback_viewed_events(), $after);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the field that controls activity availability.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function get_timeclose_field() {
|
||||
return 'timeclose';
|
||||
}
|
||||
}
|
||||
|
@ -51,9 +51,9 @@ class cognitive_depth extends activity_base {
|
||||
}
|
||||
|
||||
public function get_cognitive_depth_level(\cm_info $cm) {
|
||||
$this->fill_choice_data($cm);
|
||||
$this->fill_instance_data($cm);
|
||||
|
||||
if ($this->choicedata[$cm->instance]->showresults == 0 || $this->choicedata[$cm->instance]->showresults == 4) {
|
||||
if ($this->instancedata[$cm->instance]->showresults == 0 || $this->instancedata[$cm->instance]->showresults == 4) {
|
||||
// Results are not shown to students or are always shown.
|
||||
return self::COGNITIVE_LEVEL_2;
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ class social_breadth extends activity_base {
|
||||
}
|
||||
|
||||
public function get_social_breadth_level(\cm_info $cm) {
|
||||
$this->fill_choice_data($cm);
|
||||
$this->fill_instance_data($cm);
|
||||
return self::SOCIAL_LEVEL_2;
|
||||
}
|
||||
}
|
||||
|
@ -34,4 +34,13 @@ defined('MOODLE_INTERNAL') || die();
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class activity_base extends \core_analytics\local\indicator\community_of_inquiry_activity {
|
||||
|
||||
/**
|
||||
* Returns the name of the field that controls activity availability.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function get_timeclose_field() {
|
||||
return 'timeavailableto';
|
||||
}
|
||||
}
|
||||
|
@ -35,25 +35,6 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*/
|
||||
abstract class activity_base extends \core_analytics\local\indicator\community_of_inquiry_activity {
|
||||
|
||||
/**
|
||||
* @var int[] Tiny cache to hold feedback instance - publish_stats field relation.
|
||||
*/
|
||||
protected $publishstats = array();
|
||||
|
||||
/**
|
||||
* fill_publishstats
|
||||
*
|
||||
* @param \cm_info $cm
|
||||
* @return void
|
||||
*/
|
||||
protected function fill_publishstats(\cm_info $cm) {
|
||||
global $DB;
|
||||
|
||||
if (!isset($this->publishstats[$cm->instance])) {
|
||||
$this->publishstats[$cm->instance] = $DB->get_field('feedback', 'publish_stats', array('id' => $cm->instance));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwritten to mark as viewed if stats are published.
|
||||
*
|
||||
@ -65,11 +46,20 @@ abstract class activity_base extends \core_analytics\local\indicator\community_o
|
||||
*/
|
||||
protected function feedback_viewed(\cm_info $cm, $contextid, $userid, $after = null) {
|
||||
// If stats are published any write action counts as viewed feedback.
|
||||
if (!empty($this->publishstats[$cm->instance])) {
|
||||
if (!empty($this->instancedata[$cm->instance]->publish_stats)) {
|
||||
$user = (object)['id' => $userid];
|
||||
return $this->any_write_log($contextid, $user);
|
||||
}
|
||||
|
||||
return parent::feedback_viewed($cm, $contextid, $userid, $after);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the field that controls activity availability.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function get_timeclose_field() {
|
||||
return 'timeclose';
|
||||
}
|
||||
}
|
||||
|
@ -51,9 +51,9 @@ class cognitive_depth extends activity_base {
|
||||
}
|
||||
|
||||
public function get_cognitive_depth_level(\cm_info $cm) {
|
||||
$this->fill_publishstats($cm);
|
||||
$this->fill_instance_data($cm);
|
||||
|
||||
if (!empty($this->publishstats[$cm->instance])) {
|
||||
if (!empty($this->instancedata[$cm->instance]->publish_stats)) {
|
||||
// If stats are published we count that the user viewed feedback.
|
||||
return self::COGNITIVE_LEVEL_3;
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ class social_breadth extends activity_base {
|
||||
}
|
||||
|
||||
public function get_social_breadth_level(\cm_info $cm) {
|
||||
$this->fill_publishstats($cm);
|
||||
$this->fill_instance_data($cm);
|
||||
|
||||
return self::SOCIAL_LEVEL_2;
|
||||
}
|
||||
|
@ -53,4 +53,13 @@ abstract class activity_base extends \core_analytics\local\indicator\community_o
|
||||
// We don't need to check grades as we get the feedback while completing the activity.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the field that controls activity availability.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function get_timeclose_field() {
|
||||
return 'deadline';
|
||||
}
|
||||
}
|
||||
|
@ -53,4 +53,13 @@ abstract class activity_base extends \core_analytics\local\indicator\community_o
|
||||
protected function feedback_viewed_events() {
|
||||
return array('\mod_quiz\event\course_module_viewed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the field that controls activity availability.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function get_timeclose_field() {
|
||||
return 'timeclose';
|
||||
}
|
||||
}
|
||||
|
@ -44,4 +44,13 @@ abstract class activity_base extends \core_analytics\local\indicator\community_o
|
||||
// Any view after the data graded counts as feedback viewed.
|
||||
return array('\mod_scorm\event\course_module_viewed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the field that controls activity availability.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function get_timeclose_field() {
|
||||
return 'timeclose';
|
||||
}
|
||||
}
|
||||
|
@ -52,4 +52,13 @@ abstract class activity_base extends \core_analytics\local\indicator\community_o
|
||||
protected function feedback_viewed_events() {
|
||||
return array('\mod_workshop\event\course_module_viewed', '\mod_workshop\event\submission_viewed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the field that controls activity availability.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function get_timeclose_field() {
|
||||
return 'submissionend';
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user