MDL-3942 - various forum scoring types - patch by Anthony Borrow; merged from MOODLE_19_STABLE

This commit is contained in:
skodak 2008-02-05 11:22:13 +00:00
parent df1c2c71da
commit 13bbe067f0
5 changed files with 305 additions and 31 deletions

View File

@ -5,6 +5,13 @@ $string['addanewdiscussion'] = 'Add a new discussion topic';
$string['addanewquestion'] = 'Add a new question'; $string['addanewquestion'] = 'Add a new question';
$string['addanewtopic'] = 'Add a new topic'; $string['addanewtopic'] = 'Add a new topic';
$string['advancedsearch'] = 'Advanced search'; $string['advancedsearch'] = 'Advanced search';
$string['aggregateavg'] = 'Average of ratings';
$string['aggregatecount'] = 'Count of ratings';
$string['aggregatemax'] = 'Maximum rating';
$string['aggregatemin'] = 'Minimum rating';
$string['aggregatenone'] = 'No ratings';
$string['aggregatesum'] = 'Sum of ratings';
$string['aggregatetype'] = 'Aggregate type';
$string['allforums'] = 'All forums'; $string['allforums'] = 'All forums';
$string['allowchoice'] = 'Allow everyone to choose'; $string['allowchoice'] = 'Allow everyone to choose';
$string['allowdiscussions'] = 'Can a $a post to this forum?'; $string['allowdiscussions'] = 'Can a $a post to this forum?';

View File

@ -0,0 +1,17 @@
<h1>Forum Aggregation Types</h1>
<p>Forum aggregation defines how all the ratings given to posts in a forum are combined to form the final grade (for each post and for the whole forum activity).</p>
<p>Choose from the following aggregation methods:</p>
<ul>
<li>Average (default)
<p>The mean of all the ratings given to posts in that forum. This is especially useful with peer grading when there are a lot of ratings being made.</p>
<li>Count
<p>The number of rated posts becomes the final grade. This is useful when the number of posts is important. Note that the total can not exceed the maximum grade allowed for the forum.</p>
<li>Max
<p>The highest rating is returned as the final grade. This method is useful for emphasising the best work from participants, allowing them to post one high-quality post as well as a number of more casual responses to others.</p>
<li>Min
<p>The smallest rating is returned as the final grade. This method promotes a culture of high quality for all posts.</p>
<li>Sum
<p>All the ratings for a particular user are added together. Note that the total is not allowed to exceed the maximum grade for the forum.</p>
</ul>

View File

@ -19,6 +19,13 @@ define('FORUM_TRACKING_ON', 2);
define('FORUM_UNSET_POST_RATING', -999); define('FORUM_UNSET_POST_RATING', -999);
define ('FORUM_AGGREGATE_NONE', 0); //no ratings
define ('FORUM_AGGREGATE_AVG', 1);
define ('FORUM_AGGREGATE_COUNT', 2);
define ('FORUM_AGGREGATE_MAX', 3);
define ('FORUM_AGGREGATE_MIN', 4);
define ('FORUM_AGGREGATE_SUM', 5);
// this file may be included from some functions, we must define these as global explicitly // this file may be included from some functions, we must define these as global explicitly
global $FORUM_LAYOUT_MODES, $FORUM_TYPES, $FORUM_TYPES_ALL, $FORUM_OPEN_MODES; global $FORUM_LAYOUT_MODES, $FORUM_TYPES, $FORUM_TYPES_ALL, $FORUM_OPEN_MODES;
@ -101,7 +108,7 @@ function forum_add_instance($forum) {
} }
/** /**
* Given an object containing all the necessary data, * Given an object containing all the necessary data,
* (defined by the form in mod.html) this function * (defined by the form in mod.html) this function
* will update an existing instance with new data. * will update an existing instance with new data.
@ -119,6 +126,15 @@ function forum_update_instance($forum) {
$forum->assesstimefinish = 0; $forum->assesstimefinish = 0;
} }
$oldforum = get_record('forum', 'id', $forum->id);
// MDL-3942 - if the aggregation type or scale (i.e. max grade) changes then recalculate the grades for the entire forum
// if scale changes - do we need to recheck the ratings, if ratings higher than scale how do we want to respond?
// for count and sum aggregation types the grade we check to make sure they do not exceed the scale (i.e. max score) when calculating the grade
if (($oldforum->assessed<>$forum->assessed) or ($oldforum->scale<>$forum->scale)) {
forum_update_grades($forum); // recalculate grades for the forum
}
if ($forum->type == 'single') { // Update related discussion and post. if ($forum->type == 'single') { // Update related discussion and post.
if (! $discussion = get_record('forum_discussions', 'forum', $forum->id)) { if (! $discussion = get_record('forum_discussions', 'forum', $forum->id)) {
if ($discussions = get_records('forum_discussions', 'forum', $forum->id, 'timemodified ASC')) { if ($discussions = get_records('forum_discussions', 'forum', $forum->id, 'timemodified ASC')) {
@ -447,7 +463,7 @@ function forum_cron() {
if (!isset($userto->tracking[$forum->id])) { if (!isset($userto->tracking[$forum->id])) {
$userto->tracking[$forum->id] = !$CFG->forum_usermarksread $userto->tracking[$forum->id] = !$CFG->forum_usermarksread
&& forum_tp_can_track_forums($forum, $userto) && forum_tp_can_track_forums($forum, $userto)
&& forum_tp_is_tracked($forum, $userto->id); && forum_tp_is_tracked($forum, $userto->id);
} }
// Mark post as read if forum_usermarksread is set off // Mark post as read if forum_usermarksread is set off
if ($userto->tracking[$forum->id]) { if ($userto->tracking[$forum->id]) {
@ -701,7 +717,7 @@ function forum_cron() {
if (!isset($userto->tracking[$forum->id])) { if (!isset($userto->tracking[$forum->id])) {
$userto->tracking[$forum->id] = !$CFG->forum_usermarksread $userto->tracking[$forum->id] = !$CFG->forum_usermarksread
&& forum_tp_can_track_forums($forum, $userto) && forum_tp_can_track_forums($forum, $userto)
&& forum_tp_is_tracked($forum, $userto->id); && forum_tp_is_tracked($forum, $userto->id);
} }
// Create an array of postid's for this user to mark as read. // Create an array of postid's for this user to mark as read.
@ -908,7 +924,7 @@ function forum_make_mail_html($course, $forum, $discussion, $post, $userfrom, $u
/** /**
* *
* @param object $course * @param object $course
* @param object $user * @param object $user
* @param object $mod TODO this is not used in this function, refactor * @param object $mod TODO this is not used in this function, refactor
@ -930,7 +946,7 @@ function forum_user_outline($course, $user, $mod, $forum) {
/** /**
* *
*/ */
function forum_user_complete($course, $user, $mod, $forum) { function forum_user_complete($course, $user, $mod, $forum) {
global $CFG; global $CFG;
@ -1163,15 +1179,64 @@ function forum_get_user_grades($forum, $userid=0) {
$user = $userid ? "AND u.id = $userid" : ""; $user = $userid ? "AND u.id = $userid" : "";
$sql = "SELECT u.id, u.id AS userid, avg(fr.rating) AS rawgrade $aggtype = $forum->assessed;
FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp, switch ($aggtype) {
{$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd case FORUM_AGGREGATE_COUNT :
WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id $sql = "SELECT u.id, u.id AS userid, COUNT(fr.rating) AS rawgrade
AND fr.userid != u.id AND fd.forum = $forum->id FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp,
$user {$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd
GROUP BY u.id"; WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id
AND fr.userid != u.id AND fd.forum = $forum->id
$user
GROUP BY u.id";
break;
case FORUM_AGGREGATE_MAX :
$sql = "SELECT u.id, u.id AS userid, MAX(fr.rating) AS rawgrade
FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp,
{$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd
WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id
AND fr.userid != u.id AND fd.forum = $forum->id
$user
GROUP BY u.id";
break;
case FORUM_AGGREGATE_MIN :
$sql = "SELECT u.id, u.id AS userid, MIN(fr.rating) AS rawgrade
FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp,
{$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd
WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id
AND fr.userid != u.id AND fd.forum = $forum->id
$user
GROUP BY u.id";
break;
case FORUM_AGGREGATE_SUM :
$sql = "SELECT u.id, u.id AS userid, SUM(fr.rating) AS rawgrade
FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp,
{$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd
WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id
AND fr.userid != u.id AND fd.forum = $forum->id
$user
GROUP BY u.id";
break;
default : //avg
$sql = "SELECT u.id, u.id AS userid, AVG(fr.rating) AS rawgrade
FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp,
{$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd
WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id
AND fr.userid != u.id AND fd.forum = $forum->id
$user
GROUP BY u.id";
break;
}
return get_records_sql($sql); $results = get_records_sql($sql);
// it could throw off the grading if count and sum returned a rawgrade higher than scale
// so to prevent it we review the results and ensure that rawgrade does not exceed the scale, if it does we set rawgrade = scale (i.e. full credit)
foreach ($results as $result) {
if ($result->rawgrade >$forum->scale) {
$result->rawgrade = $forum->scale;
}
}
return $results;
} }
/** /**
@ -1453,9 +1518,9 @@ function forum_get_readable_forums($userid, $courseid=0) {
ORDER BY f.name ASC"; ORDER BY f.name ASC";
if ($forums = get_records_sql($selectforums)) { if ($forums = get_records_sql($selectforums)) {
$groups = array(); $groups = array();
if ($group = groups_get_all_groups($course->id, $userid)) { if ($group = groups_get_all_groups($course->id, $userid)) {
foreach($group as $grp) { foreach($group as $grp) {
if (isset($grp->id)) { if (isset($grp->id)) {
$groups[] = $grp->id; $groups[] = $grp->id;
@ -1569,7 +1634,7 @@ function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=5
if (!$forums[$i]->accessallgroups) { if (!$forums[$i]->accessallgroups) {
if (!empty($forums[$i]->accessgroup)) { if (!empty($forums[$i]->accessgroup)) {
$groups = rtrim(implode(",", $forums[$i]->accessgroup),","); $groups = rtrim(implode(",", $forums[$i]->accessgroup),",");
$selectdiscussion .= " AND (d.groupid in ($groups)"; $selectdiscussion .= " AND (d.groupid in ($groups)";
$selectdiscussion .= ' OR d.groupid = -1)'; // -1 means open for all groups. $selectdiscussion .= ' OR d.groupid = -1)'; // -1 means open for all groups.
} else { } else {
// User isn't in any group. Only search discussions that are // User isn't in any group. Only search discussions that are
@ -2225,6 +2290,7 @@ function forum_print_post(&$post, $courseid, $ownpost=false, $reply=false, $link
$discussion = get_record('forum_discussions', 'id', $post->discussion); $discussion = get_record('forum_discussions', 'id', $post->discussion);
$post->forum = $discussion->forum; $post->forum = $discussion->forum;
} }
if (!$cm = get_coursemodule_from_instance('forum', $post->forum)) { if (!$cm = get_coursemodule_from_instance('forum', $post->forum)) {
error('Course Module ID was incorrect'); error('Course Module ID was incorrect');
} }
@ -2414,6 +2480,10 @@ function forum_print_post(&$post, $courseid, $ownpost=false, $reply=false, $link
$post->forumtype = get_field('forum', 'type', 'id', $post->forum); $post->forumtype = get_field('forum', 'type', 'id', $post->forum);
} }
if (!isset($post->aggtype)) {
$post->aggtype = get_field('forum', 'assessed', 'id', $post->forum);
}
$age = time() - $post->created; $age = time() - $post->created;
// Hack for allow to edit news posts those are not displayed yet until they are displayed // Hack for allow to edit news posts those are not displayed yet until they are displayed
if (!$post->parent if (!$post->parent
@ -2468,7 +2538,7 @@ function forum_print_post(&$post, $courseid, $ownpost=false, $reply=false, $link
$canviewallratings = has_capability('mod/forum:viewanyrating', $post->modcontext); $canviewallratings = has_capability('mod/forum:viewanyrating', $post->modcontext);
if ($canviewallratings and !$mypost) { if ($canviewallratings and !$mypost) {
forum_print_ratings_mean($post->id, $ratings->scale, $canviewallratings); forum_print_ratings_mean($post->id, $ratings->scale, $post->aggtype, $canviewallratings);
if (!empty($ratings->allow)) { if (!empty($ratings->allow)) {
echo '&nbsp;'; echo '&nbsp;';
forum_print_rating_menu($post->id, $USER->id, $ratings->scale); forum_print_rating_menu($post->id, $USER->id, $ratings->scale);
@ -2476,7 +2546,7 @@ function forum_print_post(&$post, $courseid, $ownpost=false, $reply=false, $link
} }
} else if ($mypost) { } else if ($mypost) {
forum_print_ratings_mean($post->id, $ratings->scale, true); forum_print_ratings_mean($post->id, $ratings->scale, $post->aggtype, true);
} else if (!empty($ratings->allow) ) { } else if (!empty($ratings->allow) ) {
forum_print_rating_menu($post->id, $USER->id, $ratings->scale); forum_print_rating_menu($post->id, $USER->id, $ratings->scale);
@ -2697,15 +2767,37 @@ function forum_shorten_post($message) {
/** /**
* Print the multiple ratings on a post given to the current user by others. * Print the multiple ratings on a post given to the current user by others.
* Forumid prevents the double lookup of the forumid in discussion to determine the aggregate type
* Scale is an array of ratings * Scale is an array of ratings
*/ */
function forum_print_ratings_mean($postid, $scale, $link=true) { function forum_print_ratings_mean($postid, $scale, $aggregatetype, $link=true) {
static $strrate; $strratings = '';
$mean = forum_get_ratings_mean($postid, $scale); switch ($aggregatetype) {
case FORUM_AGGREGATE_AVG :
$agg = forum_get_ratings_mean($postid, $scale);
$strratings = get_string("aggregateavg", "forum");
break;
case FORUM_AGGREGATE_COUNT :
$agg = forum_get_ratings_count($postid, $scale);
$strratings = get_string("aggregatecount", "forum");
break;
case FORUM_AGGREGATE_MAX :
$agg = forum_get_ratings_max($postid, $scale);
$strratings = get_string("aggregatemax", "forum");
break;
case FORUM_AGGREGATE_MIN :
$agg = forum_get_ratings_min($postid, $scale);
$strratings = get_string("aggregatemin", "forum");
break;
case FORUM_AGGREGATE_SUM :
$agg = forum_get_ratings_sum($postid, $scale);
$strratings = get_string("aggregatesum", "forum");
break;
}
if ($mean !== "") { if ($agg !== "") {
if (empty($strratings)) { if (empty($strratings)) {
$strratings = get_string("ratings", "forum"); $strratings = get_string("ratings", "forum");
@ -2713,9 +2805,9 @@ function forum_print_ratings_mean($postid, $scale, $link=true) {
echo "$strratings: "; echo "$strratings: ";
if ($link) { if ($link) {
link_to_popup_window ("/mod/forum/report.php?id=$postid", "ratings", $mean, 400, 600); link_to_popup_window ("/mod/forum/report.php?id=$postid", "ratings", $agg, 400, 600);
} else { } else {
echo "$mean "; echo "$agg ";
} }
} }
} }
@ -2725,6 +2817,7 @@ function forum_print_ratings_mean($postid, $scale, $link=true) {
* Return the mean rating of a post given to the current user by others. * Return the mean rating of a post given to the current user by others.
* Scale is an array of possible ratings in the scale * Scale is an array of possible ratings in the scale
* Ratings is an optional simple array of actual ratings (just integers) * Ratings is an optional simple array of actual ratings (just integers)
* Forumid is the forum id field needed - passing it avoids a double query of lookup up the discusion and then the forum id to get the aggregate type
*/ */
function forum_get_ratings_mean($postid, $scale, $ratings=NULL) { function forum_get_ratings_mean($postid, $scale, $ratings=NULL) {
@ -2739,7 +2832,7 @@ function forum_get_ratings_mean($postid, $scale, $ratings=NULL) {
$count = count($ratings); $count = count($ratings);
if ($count == 0) { if ($count == 0 ) {
return ""; return "";
} else if ($count == 1) { } else if ($count == 1) {
@ -2760,6 +2853,143 @@ function forum_get_ratings_mean($postid, $scale, $ratings=NULL) {
} }
} }
/**
* Return the count of the ratings of a post given to the current user by others.
* Scale is an array of possible ratings in the scale - the end of the scale is the highest or max grade
* Ratings is an optional simple array of actual ratings (just integers)
*/
function forum_get_ratings_count($postid, $scale, $ratings=NULL) {
if (!$ratings) {
$ratings = array();
if ($rates = get_records("forum_ratings", "post", $postid)) {
foreach ($rates as $rate) {
$ratings[] = $rate->rating;
}
}
}
$count = count($ratings);
$scalecount = count($scale)-1; //this should give us the last element of the scale aka the max grade with $scale[$scalecount]
if ($count > $scale[$scalecount]) { //if the count exceeds the forum scale (i.e. max grade then set the score to the max grade
$count = $scale[$scalecount];
}
return $scale[$count];
}
/**
* Return the max rating of a post given to the current user by others.
* Scale is an array of possible ratings in the scale
* Ratings is an optional simple array of actual ratings (just integers)
*/
function forum_get_ratings_max($postid, $scale, $ratings=NULL) {
if (!$ratings) {
$ratings = array();
if ($rates = get_records("forum_ratings", "post", $postid)) {
foreach ($rates as $rate) {
$ratings[] = $rate->rating;
}
}
}
$count = count($ratings);
$max = max($ratings);
if ($count == 0 ) {
return "";
} else if ($count == 1) { //this works for max
return $scale[$ratings[0]];
} else {
if (isset($scale[$max])) {
return $scale[$max]." ($count)";
} else {
return "$max ($count)"; // Should never happen, hopefully
}
}
}
/**
* Return the min rating of a post given to the current user by others.
* Scale is an array of possible ratings in the scale
* Ratings is an optional simple array of actual ratings (just integers)
*/
function forum_get_ratings_min($postid, $scale, $ratings=NULL) {
if (!$ratings) {
$ratings = array();
if ($rates = get_records("forum_ratings", "post", $postid)) {
foreach ($rates as $rate) {
$ratings[] = $rate->rating;
}
}
}
$count = count($ratings);
$min = min($ratings);
if ($count == 0 ) {
return "";
} else if ($count == 1) {
return $scale[$ratings[0]]; //this works for min
} else {
if (isset($scale[$min])) {
return $scale[$min]." ($count)";
} else {
return "$min ($count)"; // Should never happen, hopefully
}
}
}
/**
* Return the sum or total of ratings of a post given to the current user by others.
* Scale is an array of possible ratings in the scale
* Ratings is an optional simple array of actual ratings (just integers)
*/
function forum_get_ratings_sum($postid, $scale, $ratings=NULL) {
if (!$ratings) {
$ratings = array();
if ($rates = get_records("forum_ratings", "post", $postid)) {
foreach ($rates as $rate) {
$ratings[] = $rate->rating;
}
}
}
$count = count($ratings);
$scalecount = count($scale)-1; //this should give us the last element of the scale aka the max grade with $scale[$scalecount]
if ($count == 0 ) {
return "";
} else if ($count == 1) { //this works for max.
return $scale[$ratings[0]];
} else {
$total = 0;
foreach ($ratings as $rating) {
$total += $rating;
}
if ($total > $scale[$scalecount]) { //if the total exceeds the max grade then set it to the max grade
$total = $scale[$scalecount];
}
if (isset($scale[$total])) {
return $scale[$total]." ($count)";
} else {
return "$total ($count)"; // Should never happen, hopefully
}
}
}
/** /**
* Return a summary of post ratings given to the current user by others. * Return a summary of post ratings given to the current user by others.
* Scale is an array of possible ratings in the scale * Scale is an array of possible ratings in the scale
@ -5656,6 +5886,21 @@ function forum_convert_to_roles($forum, $forummodid, $teacherroles=array(),
return true; return true;
} }
/**
* Returns array of forum aggregate types
*/
function forum_get_aggregate_types() {
$forum_aggregate_types = array (
FORUM_AGGREGATE_NONE => get_string('aggregatenone', 'forum'),
FORUM_AGGREGATE_AVG => get_string('aggregateavg', 'forum'),
FORUM_AGGREGATE_COUNT => get_string('aggregatecount', 'forum'),
FORUM_AGGREGATE_MAX => get_string('aggregatemax', 'forum'),
FORUM_AGGREGATE_MIN => get_string('aggregatemin', 'forum'),
FORUM_AGGREGATE_SUM => get_string('aggregatesum', 'forum'));
return $forum_aggregate_types;
}
?> ?>

View File

@ -77,20 +77,23 @@ class mod_forum_mod_form extends moodleform_mod {
//------------------------------------------------------------------------------- //-------------------------------------------------------------------------------
$mform->addElement('header', '', get_string('grade')); $mform->addElement('header', '', get_string('grade'));
$mform->addElement('checkbox', 'assessed', get_string('allowratings', 'forum') , get_string('ratingsuse', 'forum'));
$mform->addElement('select', 'assessed', get_string('aggregatetype', 'forum') , forum_get_aggregate_types());
$mform->setDefault('assessed', 0);
$mform->setHelpButton('assessed', array('assessaggregate', get_string('aggregatetype', 'forum'), 'forum'));
$mform->addElement('modgrade', 'scale', get_string('grade'), false); $mform->addElement('modgrade', 'scale', get_string('grade'), false);
$mform->disabledIf('scale', 'assessed'); $mform->disabledIf('scale', 'assessed', 'eq', 0);
$mform->addElement('checkbox', 'ratingtime', get_string('ratingtime', 'forum')); $mform->addElement('checkbox', 'ratingtime', get_string('ratingtime', 'forum'));
$mform->disabledIf('ratingtime', 'assessed'); $mform->disabledIf('ratingtime', 'assessed', 'eq', 0);
$mform->addElement('date_time_selector', 'assesstimestart', get_string('from')); $mform->addElement('date_time_selector', 'assesstimestart', get_string('from'));
$mform->disabledIf('assesstimestart', 'assessed'); $mform->disabledIf('assesstimestart', 'assessed', 'eq', 0);
$mform->disabledIf('assesstimestart', 'ratingtime'); $mform->disabledIf('assesstimestart', 'ratingtime');
$mform->addElement('date_time_selector', 'assesstimefinish', get_string('to')); $mform->addElement('date_time_selector', 'assesstimefinish', get_string('to'));
$mform->disabledIf('assesstimefinish', 'assessed'); $mform->disabledIf('assesstimefinish', 'assessed', 'eq', 0);
$mform->disabledIf('assesstimefinish', 'ratingtime'); $mform->disabledIf('assesstimefinish', 'ratingtime');

View File

@ -18,7 +18,9 @@
if (!$cm = get_coursemodule_from_instance('forum', $forum->id)) { if (!$cm = get_coursemodule_from_instance('forum', $forum->id)) {
error("Course Module ID was incorrect"); error("Course Module ID was incorrect");
} } else {
$forum->cmidnumber = $cm->id; //MDL-12961
}
require_login($course, false, $cm); require_login($course, false, $cm);