Proyectos de Subversion Moodle

Rev

Autoría | Ultima modificación | Ver Log |

<?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/>.

/**
 * Library providing functions that implement the module's features.
 *
 * @package     mod_subcourse
 * @copyright   2017 David Mudrák <david@moodle.com>
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

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

require_once($CFG->libdir.'/gradelib.php');

/**
 * Returns the list of courses the grades can be taken from
 *
 * Returned are courses in which the user has permission to view the grade
 * book. Never returns the current course (as a course cannot be a subcourse of
 * itself) and the site course (the front page course). If the userid is not
 * passed, the current user is expected.
 *
 * @param int $userid Id of user for which we want to get the list of courses
 * @return array list of course records
 */
function subcourse_available_courses($userid = null) {
    global $COURSE, $USER;

    $courses = [];

    if (empty($userid)) {
        $userid = $USER->id;
    }

    $fields = 'fullname,shortname,idnumber,category,visible,sortorder';
    $mycourses = get_user_capability_course('moodle/grade:viewall', $userid, true, $fields, 'sortorder');

    if ($mycourses) {
        $ignorecourses = [$COURSE->id, SITEID];
        foreach ($mycourses as $mycourse) {
            if (in_array($mycourse->id, $ignorecourses)) {
                continue;
            }
            $courses[] = $mycourse;
        }
    }

    return $courses;
}

/**
 * Fetches grade_item info and grades from the referenced course
 *
 * Returned structure is
 *  object(
 *      ->grades = array[userid] of object(->userid ->rawgrade ->feedback ->feedbackformat ->hidden)
 *      ->grademax
 *      ->grademin
 *      ->itemname
 *      ...
 *  )
 *
 * @param int $subcourseid ID of subcourse instance
 * @param int $refcourseid ID of referenced course
 * @param bool $gradeitemonly If true, fetch only grade item info without grades
 * @param int|array $userids If fetching grades, limit only to this user(s), defaults to all.
 * @param bool $fetchpercentage Re-calculate the grade value so that the displayed percentage matches the original.
 * @return stdClass containing grades array and gradeitem info
 */
function subcourse_fetch_refgrades($subcourseid, $refcourseid, $gradeitemonly = false, $userids = [], $fetchpercentage = false) {

    if (empty($refcourseid)) {
        throw new coding_exception('Empty referenced course id');
    }

    $fetchedfields = subcourse_get_fetched_item_fields();

    $return = new stdClass();
    $return->grades = [];

    $refgradeitem = grade_item::fetch_course_item($refcourseid);

    // Get grade_item info.
    foreach ($fetchedfields as $property) {
        if (isset($refgradeitem->$property)) {
            $return->$property = $refgradeitem->$property;
        } else {
            $return->$property = null;
        }
    }

    // If the remote grade_item is non-global scale, do not fetch grades - they can't be used.
    if (($refgradeitem->gradetype == GRADE_TYPE_SCALE) && (!subcourse_is_global_scale($refgradeitem->scaleid))) {
        $gradeitemonly = true;
        debugging(get_string('errlocalremotescale', 'subcourse'));
        $return->localremotescale = true;
    }

    if (!$gradeitemonly) {
        // Get grades.

        if (!is_array($userids)) {
            $userids = [$userids];
        }

        $cm = get_coursemodule_from_instance("subcourse", $subcourseid);
        $context = context_module::instance($cm->id);

        $users = get_users_by_capability($context, 'mod/subcourse:begraded', 'u.id,u.lastname',
                                         'u.lastname', '', '', '', '', false, true);

        foreach ($users as $user) {
            if ($userids && !in_array($user->id, $userids)) {
                continue;
            }

            $grade = new grade_grade(['itemid' => $refgradeitem->id, 'userid' => $user->id]);

            $return->grades[$user->id] = new stdClass();
            $return->grades[$user->id]->userid = $user->id;
            $return->grades[$user->id]->feedback = $grade->feedback;
            $return->grades[$user->id]->feedbackformat = $grade->feedbackformat;
            $return->grades[$user->id]->hidden = $grade->hidden;

            if ($grade->finalgrade === null) {
                // No grade set yet.
                $return->grades[$user->id]->rawgrade = null;

            } else if (empty($fetchpercentage)) {
                // Fetch the raw value of the final grade in the referenced course.
                $return->grades[$user->id]->rawgrade = $grade->finalgrade;

            } else {
                // Re-calculate the value so that the displayed percentage matches.
                // This may make difference when there are excluded grades in the referenced course.
                if ($grade->rawgrademax > 0) {
                    $ratio = ($grade->finalgrade - $grade->rawgrademin) / ($grade->rawgrademax - $grade->rawgrademin);
                    $fakevalue = $return->grademin + $ratio * ($return->grademax - $return->grademin);
                    $return->grades[$user->id]->rawgrade = grade_floatval($fakevalue);

                } else {
                    $return->grades[$user->id]->rawgrade = 0;
                }
            }
        }
    }

    return $return;
}

/**
 * Create or update grade item and grades for given subcourse
 *
 * @param int $courseid     ID of referencing course (the course containing the instance of
 * subcourse)
 * @param int $subcourseid  ID of subcourse instance
 * @param int $refcourseid  ID of referenced course (the course to take grades from)
 * @param str $itemname     Set the itemname
 * @param bool $gradeitemonly If true, fetch only grade item info without grades
 * @param bool $reset Reset grades in gradebook
 * @param int|array $userids If fetching grades, limit only to this user(s), defaults to all.
 * @param bool $fetchpercentage Re-calculate the grade value so that the displayed percentage matches the original.
 * @return int GRADE_UPDATE_OK etc
 */
function subcourse_grades_update($courseid, $subcourseid, $refcourseid, $itemname = null,
        $gradeitemonly = false, $reset = false, $userids = [], $fetchpercentage = null) {
    global $DB;

    if (empty($refcourseid)) {
        return GRADE_UPDATE_FAILED;
    }

    if (!$DB->record_exists('course', ['id' => $refcourseid])) {
        return GRADE_UPDATE_FAILED;
    }

    if (!$gradeitemonly && $fetchpercentage === null) {
        debugging('Performance: The caller should provide the fetchpercentage value to avoid an extra DB call.', DEBUG_DEVELOPER);
        $fetchpercentage = $DB->get_field('subcourse', 'fetchpercentage', ['id' => $subcourseid]);
    }

    $fetchedfields = subcourse_get_fetched_item_fields();

    $refgrades = subcourse_fetch_refgrades($subcourseid, $refcourseid, $gradeitemonly, $userids, $fetchpercentage);

    if (!empty($refgrades->localremotescale)) {
        // Unable to fetch remote grades - local scale is used in the remote course.
        return GRADE_UPDATE_FAILED;
    }

    $params = [];

    foreach ($fetchedfields as $property) {
        if (isset($refgrades->$property)) {
            $params[$property] = $refgrades->$property;
        }
    }
    if (!empty($itemname)) {
        $params['itemname'] = $itemname;
    }

    $grades = $refgrades->grades;

    if ($reset) {
        $params['reset'] = true;
        $grades = null;
    }

    $result = grade_update('mod/subcourse', $courseid, 'mod', 'subcourse', $subcourseid, 0, $grades, $params);

    // The {@see grade_update()} does not change the grade hidden state so we need to perform it manually now.
    if (!$gradeitemonly && $result == GRADE_UPDATE_OK) {
        $gi = grade_item::fetch([
            'source' => 'mod/subcourse',
            'courseid' => $courseid,
            'itemtype' => 'mod',
            'itemmodule' => 'subcourse',
            'iteminstance' => $subcourseid,
            'itemnumber' => 0
        ]);

        $gs = grade_grade::fetch_all(['itemid' => $gi->id]);

        if (!empty($gs)) {
            foreach ($gs as $g) {
                if (isset($refgrades->grades[$g->userid])) {
                    if ($refgrades->grades[$g->userid]->hidden != $g->hidden) {
                        $g->grade_item = $gi;
                        $g->set_hidden($refgrades->grades[$g->userid]->hidden);
                    }
                }
            }
        }
    }

    return $result;
}

/**
 * Checks if a remote scale can be re-used, i.e. if it is global (standard, server wide) scale
 *
 * @param mixed $scaleid ID of the scale
 * @return boolean True if scale is global, false if not.
 */
function subcourse_is_global_scale($scaleid) {
    global $DB;

    if (!is_numeric($scaleid)) {
        throw new moodle_exception('errnonnumeric', 'subcourse');
    }

    if (!$DB->get_record('scale', ['id' => $scaleid, 'courseid' => 0], 'id')) {
        // No such scale with courseid 0.
        return false;
    } else {
        // Found the global scale.
        return true;
    }
}

/**
 * Updates the timefetched timestamp for given subcourses
 *
 * @param array|int $subcourseids ID of subcourse instance or array of IDs
 * @param mixed $time The timestamp, defaults to the current time
 * @return bool
 */
function subcourse_update_timefetched($subcourseids, $time = null) {
    global $DB;

    if (empty($subcourseids)) {
        return false;
    }
    if (is_numeric($subcourseids)) {
        $subcourseids = [$subcourseids];
    }
    if (!is_array($subcourseids)) {
        return false;
    }
    if (is_null($time)) {
        $time = time();
    }
    if (!is_numeric($time)) {
        return false;
    }
    list($sql, $params) = $DB->get_in_or_equal($subcourseids);
    $DB->set_field_select('subcourse', 'timefetched', $time, "id $sql", $params);

    return true;
}

/**
 * The list of fields to copy from remote grade_item
 * @return array
 */
function subcourse_get_fetched_item_fields() {
    return ['gradetype', 'grademax', 'grademin', 'scaleid', 'hidden'];
}

/**
 * Return if the user has a grade for the activity and the string representation of the grade.
 *
 * @param stdClass $subcourse Subcourse activity record with id and course properties set
 * @param int $userid User id to get the grade for
 * @return string $strgrade
 */
function subcourse_get_current_grade(stdClass $subcourse, int $userid): ?string {

    $currentgrade = grade_get_grades($subcourse->course, 'mod', 'subcourse', $subcourse->id, $userid);
    $strgrade = null;

    if (!empty($currentgrade->items[0]->grades)) {
        $currentgrade = reset($currentgrade->items[0]->grades);

        if (isset($currentgrade->grade) && !($currentgrade->hidden)) {
            $strgrade = $currentgrade->str_grade;
        }
    }

    return $strgrade;
}

/**
 * Mark the course module as viewed by the user.
 *
 * @param stdClass $subcourse Subcourse record.
 * @param context $context Course module context.
 * @param stdClass $course Course record.
 * @param cm_info|object $cm Course module info.
 */
function subcourse_set_module_viewed(stdClass $subcourse, context $context, stdClass $course, $cm) {
    global $CFG;
    require_once($CFG->libdir . '/completionlib.php');

    $completion = new completion_info($course);
    $completion->set_module_viewed($cm);

    $event = \mod_subcourse\event\course_module_viewed::create([
        'objectid' => $subcourse->id,
        'context' => $context,
    ]);

    $event->add_record_snapshot('course_modules', $cm);
    $event->add_record_snapshot('course', $course);
    $event->add_record_snapshot('subcourse', $subcourse);

    $event->trigger();
}