MDL-30112: marking rubric filling instances for regrading

- If a used rubric being updated, teacher (moderator) is notified
- If the changes are significant, all instances automatically marked for regrade
- if the changes are minor (texts, options or sortorder), teacher can choose whether to mark for regrade or not
- The instances marked for regrade can be re-graded but students are not able to see the rubric
- when grading a message appears if the instance is marked for regrade and is not visible to students
This commit is contained in:
Marina Glancy 2011-11-03 16:08:36 +08:00
parent dd736a87ee
commit 0136124e21
9 changed files with 339 additions and 70 deletions

View File

@ -56,6 +56,9 @@ abstract class gradingform_controller {
/** @var array graderange array of valid grades for this area. Use set_grade_range and get_grade_range to access this */ /** @var array graderange array of valid grades for this area. Use set_grade_range and get_grade_range to access this */
private $graderange = null; private $graderange = null;
/** @var boolean|null cached result of function has_active_instances() */
protected $hasactiveinstances = null;
/** /**
* Do not instantinate this directly, use {@link grading_manager::get_controller()} * Do not instantinate this directly, use {@link grading_manager::get_controller()}
* *
@ -334,8 +337,8 @@ abstract class gradingform_controller {
} }
/** /**
* Returns the ACTIVE instance for this definition for the specified $raterid and $itemid * Returns the current instance (either with status ACTIVE or NEEDUPDATE) for this definition for the
* (if multiple raters are allowed, or only for $itemid otherwise). * specified $raterid and $itemid (if multiple raters are allowed, or only for $itemid otherwise).
* *
* @param int $raterid * @param int $raterid
* @param int $itemid * @param int $itemid
@ -344,19 +347,22 @@ abstract class gradingform_controller {
*/ */
public function get_current_instance($raterid, $itemid, $idonly = false) { public function get_current_instance($raterid, $itemid, $idonly = false) {
global $DB; global $DB;
$select = array( $params = array(
'formid' => $this->definition->id, 'formid' => $this->definition->id,
'itemid' => $itemid, 'itemid' => $itemid,
'status' => gradingform_instance::INSTANCE_STATUS_ACTIVE); 'status1' => gradingform_instance::INSTANCE_STATUS_ACTIVE,
'status2' => gradingform_instance::INSTANCE_STATUS_NEEDUPDATE);
$select = 'formid=:formid and itemid=:itemid and (status=:status1 or status=:status2)';
if (false /* TODO $manager->allow_multiple_raters() */) { if (false /* TODO $manager->allow_multiple_raters() */) {
$select['raterid'] = $raterid; $select .= ' and raterid=:raterid';
$params['raterid'] = $raterid;
} }
if ($idonly) { if ($idonly) {
if ($current = $DB->get_record('grading_instances', $select, 'id', IGNORE_MISSING)) { if ($current = $DB->get_record_select('grading_instances', $select, $params, 'id', IGNORE_MISSING)) {
return $current->id; return $current->id;
} }
} else { } else {
if ($current = $DB->get_record('grading_instances', $select, '*', IGNORE_MISSING)) { if ($current = $DB->get_record_select('grading_instances', $select, $params, '*', IGNORE_MISSING)) {
return $this->get_instance($current); return $this->get_instance($current);
} }
} }
@ -364,12 +370,13 @@ abstract class gradingform_controller {
} }
/** /**
* Returns list of active instances for the specified $itemid * Returns list of ACTIVE instances for the specified $itemid
* (intentionally does not return instances with status NEEDUPDATE)
* *
* @param int $itemid * @param int $itemid
* @return array of gradingform_instance objects * @return array of gradingform_instance objects
*/ */
public function get_current_instances($itemid) { public function get_active_instances($itemid) {
global $DB; global $DB;
$conditions = array('formid' => $this->definition->id, $conditions = array('formid' => $this->definition->id,
'itemid' => $itemid, 'itemid' => $itemid,
@ -382,6 +389,22 @@ abstract class gradingform_controller {
return $rv; return $rv;
} }
/**
* Returns true if there are already people who has been graded on this definition.
* In this case plugins may restrict changes of the grading definition
*
* @return boolean
*/
public function has_active_instances() {
global $DB;
if ($this->hasactiveinstances === null) {
$conditions = array('formid' => $this->definition->id,
'status' => gradingform_instance::INSTANCE_STATUS_ACTIVE);
$this->hasactiveinstances = $DB->record_exists('grading_instances', $conditions);
}
return $this->hasactiveinstances;
}
/** /**
* Returns the object of type gradingform_XXX_instance (where XXX is the plugin method name) * Returns the object of type gradingform_XXX_instance (where XXX is the plugin method name)
* *
@ -528,9 +551,10 @@ abstract class gradingform_controller {
* @param int $itemid * @param int $itemid
* @param array $grading_info result of function grade_get_grades if plugin want to use some of their info * @param array $grading_info result of function grade_get_grades if plugin want to use some of their info
* @param string $defaultcontent default string to be returned if no active grading is found or for some reason can not be shown to a user * @param string $defaultcontent default string to be returned if no active grading is found or for some reason can not be shown to a user
* @param boolean $cangrade whether current user has capability to grade in this context
* @return string * @return string
*/ */
public function render_grade($page, $itemid, $grading_info, $defaultcontent) { public function render_grade($page, $itemid, $grading_info, $defaultcontent, $cangrade) {
return $defaultcontent; return $defaultcontent;
} }
@ -563,6 +587,7 @@ abstract class gradingform_controller {
*/ */
abstract class gradingform_instance { abstract class gradingform_instance {
const INSTANCE_STATUS_ACTIVE = 1; const INSTANCE_STATUS_ACTIVE = 1;
const INSTANCE_STATUS_NEEDUPDATE = 2;
const INSTANCE_STATUS_INCOMPLETE = 0; const INSTANCE_STATUS_INCOMPLETE = 0;
const INSTANCE_STATUS_ARCHIVE = 3; const INSTANCE_STATUS_ARCHIVE = 3;
@ -624,6 +649,19 @@ abstract class gradingform_instance {
return $instanceid; return $instanceid;
} }
/**
* Returns the current (active or needupdate) instance for the same raterid and itemid as this
* instance. This function is useful to find the status of the currently modified instance
*
* @return gradingform_instance
*/
public function get_current_instance() {
if ($this->get_status() == self::INSTANCE_STATUS_ACTIVE || $this->get_status() == self::INSTANCE_STATUS_NEEDUPDATE) {
return $this;
}
return $this->get_controller()->get_current_instance($this->data->raterid, $this->data->itemid);
}
/** /**
* Returns the controller * Returns the controller
* *
@ -642,6 +680,15 @@ abstract class gradingform_instance {
return $this->data->id; return $this->data->id;
} }
/**
* Returns instance status
*
* @return int
*/
public function get_status() {
return $this->data->status;
}
/** /**
* Marks the instance as ACTIVE and current active instance (if exists) as ARCHIVE * Marks the instance as ACTIVE and current active instance (if exists) as ARCHIVE
*/ */
@ -655,14 +702,10 @@ abstract class gradingform_instance {
throw new coding_exception('You cannot mark active the grading instance without itemid'); throw new coding_exception('You cannot mark active the grading instance without itemid');
} }
$currentid = $this->get_controller()->get_current_instance($this->data->raterid, $this->data->itemid, true); $currentid = $this->get_controller()->get_current_instance($this->data->raterid, $this->data->itemid, true);
if ($currentid) { if ($currentid && $currentid != $this->get_id()) {
if ($currentid != $this->get_id()) {
$DB->update_record('grading_instances', array('id' => $currentid, 'status' => self::INSTANCE_STATUS_ARCHIVE)); $DB->update_record('grading_instances', array('id' => $currentid, 'status' => self::INSTANCE_STATUS_ARCHIVE));
$DB->update_record('grading_instances', array('id' => $this->get_id(), 'status' => self::INSTANCE_STATUS_ACTIVE));
} }
} else {
$DB->update_record('grading_instances', array('id' => $this->get_id(), 'status' => self::INSTANCE_STATUS_ACTIVE)); $DB->update_record('grading_instances', array('id' => $this->get_id(), 'status' => self::INSTANCE_STATUS_ACTIVE));
}
$this->data->status = self::INSTANCE_STATUS_ACTIVE; $this->data->status = self::INSTANCE_STATUS_ACTIVE;
} }

View File

@ -44,16 +44,16 @@ $PAGE->set_url(new moodle_url('/grade/grading/form/rubric/edit.php', array('area
$PAGE->set_title(get_string('definerubric', 'gradingform_rubric')); $PAGE->set_title(get_string('definerubric', 'gradingform_rubric'));
$PAGE->set_heading(get_string('definerubric', 'gradingform_rubric')); $PAGE->set_heading(get_string('definerubric', 'gradingform_rubric'));
$mform = new gradingform_rubric_editrubric(null, array('areaid' => $areaid, 'context' => $context)); $mform = new gradingform_rubric_editrubric(null, array('areaid' => $areaid, 'context' => $context, 'allowdraft' => !$controller->has_active_instances()));
$data = $controller->get_definition_for_editing(); $data = $controller->get_definition_for_editing();
$returnurl = optional_param('returnurl', $manager->get_management_url(), PARAM_LOCALURL); $returnurl = optional_param('returnurl', $manager->get_management_url(), PARAM_LOCALURL);
$data->returnurl = $returnurl; $data->returnurl = $returnurl;
$mform->set_data($data); $mform->set_data($data);
if ($mform->is_cancelled()) { if ($mform->is_cancelled()) {
redirect($returnurl); redirect($returnurl);
} else if ($mform->is_submitted() && $mform->is_validated()) { } else if ($mform->is_submitted() && $mform->is_validated() && !$mform->need_confirm_regrading($controller)) {
$data = $mform->get_data(); // everything ok, validated, re-grading confirmed if needed. Make changes to the rubric
$controller->update_definition($data); $controller->update_definition($mform->get_data());
redirect($returnurl); redirect($returnurl);
} }

View File

@ -69,7 +69,12 @@ class gradingform_rubric_editrubric extends moodleform {
$buttonarray = array(); $buttonarray = array();
$buttonarray[] = &$form->createElement('submit', 'saverubric', get_string('saverubric', 'gradingform_rubric')); $buttonarray[] = &$form->createElement('submit', 'saverubric', get_string('saverubric', 'gradingform_rubric'));
if ($this->_customdata['allowdraft']) {
$buttonarray[] = &$form->createElement('submit', 'saverubricdraft', get_string('saverubricdraft', 'gradingform_rubric')); $buttonarray[] = &$form->createElement('submit', 'saverubricdraft', get_string('saverubricdraft', 'gradingform_rubric'));
}
$editbutton = &$form->createElement('submit', 'editrubric', ' ');
$editbutton->freeze();
$buttonarray[] = &$editbutton;
$buttonarray[] = &$form->createElement('cancel'); $buttonarray[] = &$form->createElement('cancel');
$form->addGroup($buttonarray, 'buttonar', '', array(' '), false); $form->addGroup($buttonarray, 'buttonar', '', array(' '), false);
$form->closeHeaderBefore('buttonar'); $form->closeHeaderBefore('buttonar');
@ -93,6 +98,9 @@ class gradingform_rubric_editrubric extends moodleform {
if ($rubricel->non_js_button_pressed($data['rubric'])) { if ($rubricel->non_js_button_pressed($data['rubric'])) {
// if JS is disabled and button such as 'Add criterion' is pressed - prevent from submit // if JS is disabled and button such as 'Add criterion' is pressed - prevent from submit
$err['rubricdummy'] = 1; $err['rubricdummy'] = 1;
} else if (isset($data['editrubric'])) {
// continue editing
$err['rubricdummy'] = 1;
} else if (isset($data['saverubric']) && $data['saverubric']) { } else if (isset($data['saverubric']) && $data['saverubric']) {
// If user attempts to make rubric active - it needs to be validated // If user attempts to make rubric active - it needs to be validated
if ($rubricel->validate($data['rubric']) !== false) { if ($rubricel->validate($data['rubric']) !== false) {
@ -117,4 +125,70 @@ class gradingform_rubric_editrubric extends moodleform {
} }
return $data; return $data;
} }
/**
* Check if there are changes in the rubric and it is needed to ask user whether to
* mark the current grades for re-grading. User may confirm re-grading and continue,
* return to editing or cancel the changes
*
* @param gradingform_rubric_controller $controller
*/
function need_confirm_regrading($controller) {
$data = $this->get_data();
if (isset($data->rubric['regrade'])) {
// we have already displayed the confirmation on the previous step
return false;
}
if (!isset($data->saverubric) || !$data->saverubric) {
// we only need confirmation when button 'Save rubric' is pressed
return false;
}
if (!$controller->has_active_instances()) {
// nothing to re-grade, confirmation not needed
return false;
}
$changelevel = $controller->update_or_check_rubric($data);
if ($changelevel == 0) {
// no changes in the rubric, no confirmation needed
return false;
}
// freeze form elements and pass the values in hidden fields
// TODO description_editor does not freeze the normal way!
$form = $this->_form;
foreach (array('rubric', 'name'/*, 'description_editor'*/) as $fieldname) {
$el =& $form->getElement($fieldname);
$el->freeze();
$el->setPersistantFreeze(true);
if ($fieldname == 'rubric') {
$el->add_regrade_confirmation($changelevel);
}
}
// replace button text 'saverubric' and unfreeze 'Back to edit' button
$this->findButton('saverubric')->setValue(get_string('continue'));
$el =& $this->findButton('editrubric');
$el->setValue(get_string('backtoediting', 'gradingform_rubric'));
$el->unfreeze();
return true;
}
/**
* Returns a form element (submit button) with the name $elementname
*
* @param string $elementname
* @return HTML_QuickForm_element
*/
function &findButton($elementname) {
$form = $this->_form;
$buttonar =& $form->getElement('buttonar');
$elements =& $buttonar->getElements();
foreach ($elements as $el) {
if ($el->getName() == $elementname) {
return $el;
}
}
return null;
}
} }

View File

@ -69,3 +69,18 @@ $string['err_nodescription'] = 'Criterion description can not be empty';
$string['err_nodefinition'] = 'Level definition can not be empty'; $string['err_nodefinition'] = 'Level definition can not be empty';
$string['err_scoreformat'] = 'Number of points for each level must be a valid non-negative number'; $string['err_scoreformat'] = 'Number of points for each level must be a valid non-negative number';
$string['err_totalscore'] = 'Maximum number of points possible when graded by the rubric must be more than zero'; $string['err_totalscore'] = 'Maximum number of points possible when graded by the rubric must be more than zero';
$string['regrademessage1'] = 'You are about to save changes to the rubric that has already been used for grading. Please indicate whether your changes
are significant and students grades need to be reviewed.
If students already graded are marked for re-grading their
current grades remain in gradebook but the students will not see the rubric grading before teacher updates it.';
$string['regrademessage5'] = 'You are about to save significant changes to the rubric that has already been used for grading. Please note that all students already graded will be marked for re-grading.
The
current grades remain in gradebook but the students will not see the rubric grading before teacher updates it.';
$string['regradeoption0'] = 'Do not mark for regrade';
$string['regradeoption1'] = 'Mark for regrade';
$string['needregrademessage'] = 'Rubric definition was changed after this student had been graded. You must update the grade otherwise it will not be shown to student.';
$string['rubricnotcompleted'] = 'You have to select a feedback on each rubric criterion';
$string['backtoediting'] = 'Back to editing';

View File

@ -65,10 +65,36 @@ class gradingform_rubric_controller extends gradingform_controller {
* @param int|null $usermodified optional userid of the author of the definition, defaults to the current user * @param int|null $usermodified optional userid of the author of the definition, defaults to the current user
*/ */
public function update_definition(stdClass $newdefinition, $usermodified = null) { public function update_definition(stdClass $newdefinition, $usermodified = null) {
$this->update_or_check_rubric($newdefinition, $usermodified, true);
if (isset($newdefinition->rubric['regrade']) && $newdefinition->rubric['regrade']) {
$this->mark_for_regrade();
}
}
/**
* Either saves the rubric definition into the database or check if it has been changed.
* Returns the level of changes:
* 0 - no changes
* 1 - only texts or criteria sortorders are changed, students probably do not require re-grading
* 2 - added levels but maximum score on rubric is the same, students still may not require re-grading
* 3 - removed criteria or added levels or changed number of points, students require re-grading but may be re-graded automatically
* 4 - removed levels - students require re-grading and not all students may be re-graded automatically
* 5 - added criteria - all students require manual re-grading
*
* @param stdClass $newdefinition rubric definition data as coming from gradingform_rubric_editrubric::get_data()
* @param int|null $usermodified optional userid of the author of the definition, defaults to the current user
* @param boolean $doupdate if true actually updates DB, otherwise performs a check
*
*/
public function update_or_check_rubric(stdClass $newdefinition, $usermodified = null, $doupdate = false) {
global $DB; global $DB;
// firstly update the common definition data in the {grading_definition} table // firstly update the common definition data in the {grading_definition} table
if ($this->definition === false) { if ($this->definition === false) {
if (!$doupdate) {
// if we create the new definition there is no such thing as re-grading anyway
return 5;
}
// if definition does not exist yet, create a blank one // if definition does not exist yet, create a blank one
// (we need id to save files embedded in description) // (we need id to save files embedded in description)
parent::update_definition(new stdClass(), $usermodified); parent::update_definition(new stdClass(), $usermodified);
@ -81,13 +107,12 @@ class gradingform_rubric_controller extends gradingform_controller {
$editoroptions = self::description_form_field_options($this->get_context()); $editoroptions = self::description_form_field_options($this->get_context());
$newdefinition = file_postupdate_standard_editor($newdefinition, 'description', $editoroptions, $this->get_context(), $newdefinition = file_postupdate_standard_editor($newdefinition, 'description', $editoroptions, $this->get_context(),
'gradingform_rubric', 'definition_description', $this->definition->id); 'gradingform_rubric', 'definition_description', $this->definition->id);
parent::update_definition($newdefinition, $usermodified);
// reload the definition from the database // reload the definition from the database
$currentdefinition = $this->get_definition(true); $currentdefinition = $this->get_definition(true);
// update rubric data // update rubric data
$haschanges = false; $haschanges = array();
if (empty($newdefinition->rubric['criteria'])) { if (empty($newdefinition->rubric['criteria'])) {
$newcriteria = array(); $newcriteria = array();
} else { } else {
@ -102,6 +127,7 @@ class gradingform_rubric_controller extends gradingform_controller {
if (array_key_exists('levels', $criterion)) { if (array_key_exists('levels', $criterion)) {
$levelsdata = $criterion['levels']; $levelsdata = $criterion['levels'];
} }
$criterionmaxscore = null;
if (preg_match('/^NEWID\d+$/', $id)) { if (preg_match('/^NEWID\d+$/', $id)) {
// insert criterion into DB // insert criterion into DB
$data = array('formid' => $this->definition->id, 'descriptionformat' => FORMAT_MOODLE); // TODO format is not supported yet $data = array('formid' => $this->definition->id, 'descriptionformat' => FORMAT_MOODLE); // TODO format is not supported yet
@ -110,8 +136,10 @@ class gradingform_rubric_controller extends gradingform_controller {
$data[$key] = $criterion[$key]; $data[$key] = $criterion[$key];
} }
} }
if ($doupdate) {
$id = $DB->insert_record('gradingform_rubric_criteria', $data); $id = $DB->insert_record('gradingform_rubric_criteria', $data);
$haschanges = true; }
$haschanges[5] = true;
} else { } else {
// update criterion in DB // update criterion in DB
$data = array(); $data = array();
@ -123,14 +151,21 @@ class gradingform_rubric_controller extends gradingform_controller {
if (!empty($data)) { if (!empty($data)) {
// update only if something is changed // update only if something is changed
$data['id'] = $id; $data['id'] = $id;
if ($doupdate) {
$DB->update_record('gradingform_rubric_criteria', $data); $DB->update_record('gradingform_rubric_criteria', $data);
$haschanges = true;
} }
// remove deleted levels from DB $haschanges[1] = true;
foreach (array_keys($currentcriteria[$id]['levels']) as $levelid) { }
// remove deleted levels from DB and calculate the maximum score for this criteria
foreach ($currentcriteria[$id]['levels'] as $levelid => $currentlevel) {
if ($criterionmaxscore === null || $criterionmaxscore < $currentlevel['score']) {
$criterionmaxscore = $currentlevel['score'];
}
if (!array_key_exists($levelid, $levelsdata)) { if (!array_key_exists($levelid, $levelsdata)) {
if ($doupdate) {
$DB->delete_records('gradingform_rubric_levels', array('id' => $levelid)); $DB->delete_records('gradingform_rubric_levels', array('id' => $levelid));
$haschanges = true; }
$haschanges[4] = true;
} }
} }
} }
@ -150,8 +185,15 @@ class gradingform_rubric_controller extends gradingform_controller {
$data[$key] = $level[$key]; $data[$key] = $level[$key];
} }
} }
if ($doupdate) {
$levelid = $DB->insert_record('gradingform_rubric_levels', $data); $levelid = $DB->insert_record('gradingform_rubric_levels', $data);
$haschanges = true; }
if ($criterionmaxscore !== null && $criterionmaxscore >= $level['score']) {
// new level is added but the maximum score for this criteria did not change, re-grading may not be necessary
$haschanges[2] = true;
} else {
$haschanges[3] = true;
}
} else { } else {
// update level in DB // update level in DB
$data = array(); $data = array();
@ -163,8 +205,13 @@ class gradingform_rubric_controller extends gradingform_controller {
if (!empty($data)) { if (!empty($data)) {
// update only if something is changed // update only if something is changed
$data['id'] = $levelid; $data['id'] = $levelid;
if ($doupdate) {
$DB->update_record('gradingform_rubric_levels', $data); $DB->update_record('gradingform_rubric_levels', $data);
$haschanges = true; }
if (isset($data['score'])) {
$haschanges[3] = true;
}
$haschanges[1] = true;
} }
} }
} }
@ -172,13 +219,42 @@ class gradingform_rubric_controller extends gradingform_controller {
// remove deleted criteria from DB // remove deleted criteria from DB
foreach (array_keys($currentcriteria) as $id) { foreach (array_keys($currentcriteria) as $id) {
if (!array_key_exists($id, $newcriteria)) { if (!array_key_exists($id, $newcriteria)) {
if ($doupdate) {
$DB->delete_records('gradingform_rubric_criteria', array('id' => $id)); $DB->delete_records('gradingform_rubric_criteria', array('id' => $id));
$DB->delete_records('gradingform_rubric_levels', array('criterionid' => $id)); $DB->delete_records('gradingform_rubric_levels', array('criterionid' => $id));
$haschanges = true; }
$haschanges[3] = true;
} }
} }
foreach (array('status', 'description', 'descriptionformat', 'name', 'options') as $key) {
if (isset($newdefinition->$key) && $newdefinition->$key != $this->definition->$key) {
$haschanges[1] = true;
}
}
if ($usermodified && $usermodified != $this->definition->usermodified) {
$haschanges[1] = true;
}
if (!count($haschanges)) {
return 0;
}
if ($doupdate) {
parent::update_definition($newdefinition, $usermodified);
$this->load_definition(); $this->load_definition();
} }
// return the maximum level of changes
$changelevels = array_keys($haschanges);
sort($changelevels);
return array_pop($changelevels);
}
public function mark_for_regrade() {
global $DB;
if ($this->has_active_instances()) {
$conditions = array('formid' => $this->definition->id,
'status' => gradingform_instance::INSTANCE_STATUS_ACTIVE);
$DB->set_field('grading_instances', 'status', gradingform_instance::INSTANCE_STATUS_NEEDUPDATE, $conditions);
}
}
/** /**
* Loads the rubric form definition if it exists * Loads the rubric form definition if it exists
@ -321,8 +397,6 @@ class gradingform_rubric_controller extends gradingform_controller {
return $new; return $new;
} }
// TODO the following functions may be moved to parent:
/** /**
* @return array options for the form description field * @return array options for the form description field
*/ */
@ -416,11 +490,11 @@ class gradingform_rubric_controller extends gradingform_controller {
* @param int $itemid * @param int $itemid
* @param array $grading_info result of function grade_get_grades * @param array $grading_info result of function grade_get_grades
* @param string $defaultcontent default string to be returned if no active grading is found * @param string $defaultcontent default string to be returned if no active grading is found
* @param boolean $cangrade whether current user has capability to grade in this context
* @return string * @return string
*/ */
public function render_grade($page, $itemid, $grading_info, $defaultcontent) { public function render_grade($page, $itemid, $grading_info, $defaultcontent, $cangrade) {
$instances = $this->get_current_instances($itemid); return $this->get_renderer($page)->display_instances($this->get_active_instances($itemid), $defaultcontent, $cangrade);
return $this->get_renderer($page)->display_instances($this->get_current_instances($itemid), $defaultcontent);
} }
//// full-text search support ///////////////////////////////////////////// //// full-text search support /////////////////////////////////////////////
@ -508,7 +582,6 @@ class gradingform_rubric_instance extends gradingform_instance {
* @return boolean true if the form data is validated and contains no errors * @return boolean true if the form data is validated and contains no errors
*/ */
public function validate_grading_element($elementvalue) { public function validate_grading_element($elementvalue) {
// TODO: if there is nothing selected in rubric, we don't enter this function at all :(
$criteria = $this->get_controller()->get_definition()->rubric_criteria; $criteria = $this->get_controller()->get_definition()->rubric_criteria;
if (!isset($elementvalue['criteria']) || !is_array($elementvalue['criteria']) || sizeof($elementvalue['criteria']) < sizeof($criteria)) { if (!isset($elementvalue['criteria']) || !is_array($elementvalue['criteria']) || sizeof($elementvalue['criteria']) < sizeof($criteria)) {
return false; return false;
@ -554,12 +627,15 @@ class gradingform_rubric_instance extends gradingform_instance {
foreach ($data['criteria'] as $criterionid => $record) { foreach ($data['criteria'] as $criterionid => $record) {
if (!array_key_exists($criterionid, $currentgrade['criteria'])) { if (!array_key_exists($criterionid, $currentgrade['criteria'])) {
$newrecord = array('forminstanceid' => $this->get_id(), 'criterionid' => $criterionid, $newrecord = array('forminstanceid' => $this->get_id(), 'criterionid' => $criterionid,
'levelid' => $record['levelid'], 'remark' => $record['remark'], 'remarkformat' => FORMAT_MOODLE); 'levelid' => $record['levelid'], 'remarkformat' => FORMAT_MOODLE);
if (isset($record['remark'])) {
$newrecord['remark'] = $record['remark'];
}
$DB->insert_record('gradingform_rubric_fillings', $newrecord); $DB->insert_record('gradingform_rubric_fillings', $newrecord);
} else { } else {
$newrecord = array('id' => $currentgrade['criteria'][$criterionid]['id']); $newrecord = array('id' => $currentgrade['criteria'][$criterionid]['id']);
foreach (array('levelid', 'remark'/*, 'remarkformat' TODO */) as $key) { foreach (array('levelid', 'remark'/*, 'remarkformat' TODO */) as $key) {
if ($currentgrade['criteria'][$criterionid][$key] != $record[$key]) { if (isset($record[$key]) && $currentgrade['criteria'][$criterionid][$key] != $record[$key]) {
$newrecord[$key] = $record[$key]; $newrecord[$key] = $record[$key];
} }
} }
@ -616,15 +692,6 @@ class gradingform_rubric_instance extends gradingform_instance {
return round(($curscore-$minscore)/($maxscore-$minscore)*($maxgrade-$mingrade), 0) + $mingrade; // TODO mapping return round(($curscore-$minscore)/($maxscore-$minscore)*($maxgrade-$mingrade), 0) + $mingrade; // TODO mapping
} }
/**
* Returns the error message displayed in case of validation failed
*
* @return string
*/
public function default_validation_error_message() {
return 'The rubric is incomplete'; //TODO string
}
/** /**
* Returns html for form element of type 'grading'. * Returns html for form element of type 'grading'.
* *
@ -648,10 +715,18 @@ class gradingform_rubric_instance extends gradingform_instance {
$criteria = $this->get_controller()->get_definition()->rubric_criteria; $criteria = $this->get_controller()->get_definition()->rubric_criteria;
$options = $this->get_controller()->get_options(); $options = $this->get_controller()->get_options();
$value = $gradingformelement->getValue(); $value = $gradingformelement->getValue();
$html = '';
if ($value === null) { if ($value === null) {
$value = $this->get_rubric_filling(); $value = $this->get_rubric_filling();
} else if (!$this->validate_grading_element($value)) {
$html .= html_writer::tag('div', get_string('rubricnotcompleted', 'gradingform_rubric'), array('class' => 'gradingform_rubric-error'));
} }
return $this->get_controller()->get_renderer($page)->display_rubric($criteria, $options, $mode, $gradingformelement->getName(), $value); $currentinstance = $this->get_current_instance();
if ($currentinstance && $currentinstance->get_status() == gradingform_instance::INSTANCE_STATUS_NEEDUPDATE) {
$html .= html_writer::tag('div', get_string('needregrademessage', 'gradingform_rubric'), array('class' => 'gradingform_rubric-regrade'));
}
$html .= $this->get_controller()->get_renderer($page)->display_rubric($criteria, $options, $mode, $gradingformelement->getName(), $value);
return $html;
} }
} }

View File

@ -296,16 +296,16 @@ class gradingform_rubric_renderer extends plugin_renderer_base {
} }
break; break;
default: default:
if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FROZEN && $value) {
$html .= html_writer::empty_tag('input', $attrs + array('type' => 'hidden', 'value' => $value));
}
// Display option as checkbox // Display option as checkbox
$attrs['type'] = 'checkbox'; $attrs['type'] = 'checkbox';
$attrs['value'] = 1; $attrs['value'] = 1;
if ($value) { if ($value) {
$attrs['checked'] = 'checked'; $attrs['checked'] = 'checked';
} }
if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FROZEN) { if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FROZEN || $mode == gradingform_rubric_controller::DISPLAY_PREVIEW) {
$attrs['disabled'] = 'disabled';
}
if ($mode == gradingform_rubric_controller::DISPLAY_PREVIEW) {
$attrs['disabled'] = 'disabled'; $attrs['disabled'] = 'disabled';
unset($attrs['name']); unset($attrs['name']);
} }
@ -391,14 +391,16 @@ class gradingform_rubric_renderer extends plugin_renderer_base {
* *
* @param array $instances array of objects of type gradingform_rubric_instance * @param array $instances array of objects of type gradingform_rubric_instance
* @param string $defaultcontent default string that would be displayed without advanced grading * @param string $defaultcontent default string that would be displayed without advanced grading
* @param boolean $cangrade whether current user has capability to grade in this context
* @return string * @return string
*/ */
public function display_instances($instances, $defaultcontent) { public function display_instances($instances, $defaultcontent, $cangrade) {
$rv = '';
if (sizeof($instances)) { if (sizeof($instances)) {
$rv = html_writer::start_tag('div', array('class' => 'advancedgrade')); $rv .= html_writer::start_tag('div', array('class' => 'advancedgrade'));
$idx = 0; $idx = 0;
foreach ($instances as $instance) { foreach ($instances as $instance) {
$rv .= $this->display_instance($instance, $idx++); $rv .= $this->display_instance($instance, $idx++, $cangrade);
} }
$rv .= html_writer::end_tag('div'); $rv .= html_writer::end_tag('div');
} }
@ -410,12 +412,34 @@ class gradingform_rubric_renderer extends plugin_renderer_base {
* *
* @param gradingform_rubric_instance $instance * @param gradingform_rubric_instance $instance
* @param int idx unique number of instance on page * @param int idx unique number of instance on page
* @param boolean $cangrade whether current user has capability to grade in this context
*/ */
public function display_instance(gradingform_rubric_instance $instance, $idx) { public function display_instance(gradingform_rubric_instance $instance, $idx, $cangrade) {
$criteria = $instance->get_controller()->get_definition()->rubric_criteria; $criteria = $instance->get_controller()->get_definition()->rubric_criteria;
$options = $instance->get_controller()->get_options(); $options = $instance->get_controller()->get_options();
$values = $instance->get_rubric_filling(); $values = $instance->get_rubric_filling();
// TODO mode should be DISPLAY_REVIEW if this user is a teacher if ($cangrade) {
return $this->display_rubric($criteria, $options, gradingform_rubric_controller::DISPLAY_VIEW, 'rubric'.$idx, $values); $mode = gradingform_rubric_controller::DISPLAY_REVIEW;
} else {
$mode = gradingform_rubric_controller::DISPLAY_VIEW;
}
return $this->display_rubric($criteria, $options, $mode, 'rubric'.$idx, $values);
}
public function display_regrade_confirmation($elementname, $changelevel, $value) {
$html = html_writer::start_tag('div', array('class' => 'gradingform_rubric-regrade'));
if ($changelevel<=2) {
$html .= get_string('regrademessage1', 'gradingform_rubric');
$selectoptions = array(
0 => get_string('regradeoption0', 'gradingform_rubric'),
1 => get_string('regradeoption1', 'gradingform_rubric')
);
$html .= html_writer::select($selectoptions, $elementname.'[regrade]', $value, false);
} else {
$html .= get_string('regrademessage5', 'gradingform_rubric');
$html .= html_writer::empty_tag('input', array('name' => $elementname.'[regrade]', 'value' => 1, 'type' => 'hidden'));
}
$html .= html_writer::end_tag('div');
return $html;
} }
} }

View File

@ -44,6 +44,23 @@ class MoodleQuickForm_rubriceditor extends HTML_QuickForm_input {
return 'default'; return 'default';
} }
protected $regradeconfirmation = false;
/**
* Specifies that confirmation about re-grading needs to be added to this rubric editor.
* $changelevel is saved in $this->regradeconfirmation and retrieved in toHtml()
*
* @see gradingform_rubric_controller::update_or_check_rubric()
* @param int $changelevel
*/
function add_regrade_confirmation($changelevel) {
$this->regradeconfirmation = $changelevel;
}
/**
* Returns html string to display this element
*
* @return string
*/
function toHtml() { function toHtml() {
global $PAGE; global $PAGE;
$html = $this->_getTabs(); $html = $this->_getTabs();
@ -69,6 +86,12 @@ class MoodleQuickForm_rubriceditor extends HTML_QuickForm_input {
$mode = gradingform_rubric_controller::DISPLAY_PREVIEW; $mode = gradingform_rubric_controller::DISPLAY_PREVIEW;
} }
} }
if ($this->regradeconfirmation) {
if (!isset($data['regrade'])) {
$data['regrade'] = 1;
}
$html .= $renderer->display_regrade_confirmation($this->getName(), $this->regradeconfirmation, $data['regrade']);
}
if ($this->validationerrors) { if ($this->validationerrors) {
$html .= $renderer->notification($this->validationerrors, 'error'); $html .= $renderer->notification($this->validationerrors, 'error');
} }
@ -114,6 +137,14 @@ class MoodleQuickForm_rubriceditor extends HTML_QuickForm_input {
} }
} }
} }
if (is_array($value)) {
// for other array keys of $value no special treatmeant neeeded, copy them to return value as is
foreach (array_keys($value) as $key) {
if ($key != 'options' && $key != 'criteria') {
$return[$key] = $value[$key];
}
}
}
// iterate through criteria // iterate through criteria
$lastaction = null; $lastaction = null;

View File

@ -108,3 +108,10 @@
.gradingform_rubric .criterion .description.error, .gradingform_rubric .criterion .description.error,
.gradingform_rubric .criterion .levels .level .definition.error, .gradingform_rubric .criterion .levels .level .definition.error,
.gradingform_rubric .criterion .levels .level .score.error {background:#FFDDDD;} .gradingform_rubric .criterion .levels .level .score.error {background:#FFDDDD;}
/**
*
*/
.gradingform_rubric-regrade {padding:10px;background:#FFDDDD;border:1px solid #F00;margin-bottom:10px;}
.gradingform_rubric-error {color:red;font-weight:bold;}

View File

@ -335,7 +335,7 @@ class assignment_base {
$grade_str = '<div class="grade">'. get_string("grade").': '.$grade->str_long_grade. '</div>'; $grade_str = '<div class="grade">'. get_string("grade").': '.$grade->str_long_grade. '</div>';
if (!empty($submission) && $controller = get_grading_manager($this->context, 'mod_assignment', 'submission')->get_active_controller()) { if (!empty($submission) && $controller = get_grading_manager($this->context, 'mod_assignment', 'submission')->get_active_controller()) {
$controller->set_grade_range(make_grades_menu($this->assignment->grade)); $controller->set_grade_range(make_grades_menu($this->assignment->grade));
echo $controller->render_grade($PAGE, $submission->id, $item, $grade_str); echo $controller->render_grade($PAGE, $submission->id, $item, $grade_str, has_capability('mod/assignment:grade', $this->context));
} else { } else {
echo $grade_str; echo $grade_str;
} }