/** * Either saves the guide 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 guide is the same, students still may not require re-grading * 3 - removed criteria 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 guide definition data as coming from gradingform_guide_editguide::get_data() * @param int|null $usermodified optional userid of the author of the definition, defaults to the current user * @param bool $doupdate if true actually updates DB, otherwise performs a check * @return int */ public function update_or_check_guide(stdClass $newdefinition, $usermodified = null, $doupdate = false) { global $DB; // Firstly update the common definition data in the {grading_definition} table. 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 // (we need id to save files embedded in description). parent::update_definition(new stdClass(), $usermodified); parent::load_definition(); } if (!isset($newdefinition->guide['options'])) { $newdefinition->guide['options'] = self::get_default_options(); } $newdefinition->options = json_encode($newdefinition->guide['options']); $editoroptions = self::description_form_field_options($this->get_context()); $newdefinition = file_postupdate_standard_editor($newdefinition, 'description', $editoroptions, $this->get_context(), 'grading', 'description', $this->definition->id); // Reload the definition from the database. $currentdefinition = $this->get_definition(true); // Update guide data. $haschanges = array(); if (empty($newdefinition->guide['criteria'])) { $newcriteria = array(); } else { $newcriteria = $newdefinition->guide['criteria']; // New ones to be saved. } $currentcriteria = $currentdefinition->guide_criteria; $criteriafields = array('sortorder', 'description', 'descriptionformat', 'descriptionmarkers', 'descriptionmarkersformat', 'shortname', 'maxscore'); foreach ($newcriteria as $id => $criterion) { if (preg_match('/^NEWID\\d+$/', $id)) { // Insert criterion into DB. $data = array('definitionid' => $this->definition->id, 'descriptionformat' => FORMAT_MOODLE, 'descriptionmarkersformat' => FORMAT_MOODLE); // TODO format is not supported yet. foreach ($criteriafields as $key) { if (array_key_exists($key, $criterion)) { $data[$key] = $criterion[$key]; } } if ($doupdate) { $id = $DB->insert_record('gradingform_guide_criteria', $data); } $haschanges[5] = true; } else { // Update criterion in DB. $data = array(); foreach ($criteriafields as $key) { if (array_key_exists($key, $criterion) && $criterion[$key] != $currentcriteria[$id][$key]) { $data[$key] = $criterion[$key]; } } if (!empty($data)) { // Update only if something is changed. $data['id'] = $id; if ($doupdate) { $DB->update_record('gradingform_guide_criteria', $data); } $haschanges[1] = true; } } } // Remove deleted criteria from DB. foreach (array_keys($currentcriteria) as $id) { if (!array_key_exists($id, $newcriteria)) { if ($doupdate) { $DB->delete_records('gradingform_guide_criteria', array('id' => $id)); } $haschanges[3] = true; } } // Now handle comments. if (empty($newdefinition->guide['comments'])) { $newcomment = array(); } else { $newcomment = $newdefinition->guide['comments']; // New ones to be saved. } $currentcomments = $currentdefinition->guide_comment; $commentfields = array('sortorder', 'description'); foreach ($newcomment as $id => $comment) { if (preg_match('/^NEWID\\d+$/', $id)) { // Insert criterion into DB. $data = array('definitionid' => $this->definition->id, 'descriptionformat' => FORMAT_MOODLE); foreach ($commentfields as $key) { if (array_key_exists($key, $comment)) { $data[$key] = $comment[$key]; } } if ($doupdate) { $id = $DB->insert_record('gradingform_guide_comments', $data); } } else { // Update criterion in DB. $data = array(); foreach ($commentfields as $key) { if (array_key_exists($key, $comment) && $comment[$key] != $currentcomments[$id][$key]) { $data[$key] = $comment[$key]; } } if (!empty($data)) { // Update only if something is changed. $data['id'] = $id; if ($doupdate) { $DB->update_record('gradingform_guide_comments', $data); } } } } // Remove deleted criteria from DB. foreach (array_keys($currentcomments) as $id) { if (!array_key_exists($id, $newcomment)) { if ($doupdate) { $DB->delete_records('gradingform_guide_comments', array('id' => $id)); } } } // End comments handle. 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(); } // Return the maximum level of changes. $changelevels = array_keys($haschanges); sort($changelevels); return array_pop($changelevels); }
/** * 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; // firstly update the common definition data in the {grading_definition} table 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 // (we need id to save files embedded in description) parent::update_definition(new stdClass(), $usermodified); parent::load_definition(); } if (!isset($newdefinition->rubric['options'])) { $newdefinition->rubric['options'] = self::get_default_options(); } $newdefinition->options = json_encode($newdefinition->rubric['options']); $editoroptions = self::description_form_field_options($this->get_context()); $newdefinition = file_postupdate_standard_editor($newdefinition, 'description', $editoroptions, $this->get_context(), 'grading', 'description', $this->definition->id); // reload the definition from the database $currentdefinition = $this->get_definition(true); // update rubric data $haschanges = array(); if (empty($newdefinition->rubric['criteria'])) { $newcriteria = array(); } else { $newcriteria = $newdefinition->rubric['criteria']; // new ones to be saved } $currentcriteria = $currentdefinition->rubric_criteria; $criteriafields = array('sortorder', 'description', 'descriptionformat'); $levelfields = array('score', 'definition', 'definitionformat'); foreach ($newcriteria as $id => $criterion) { // get list of submitted levels $levelsdata = array(); if (array_key_exists('levels', $criterion)) { $levelsdata = $criterion['levels']; } $criterionmaxscore = null; if (preg_match('/^NEWID\\d+$/', $id)) { // insert criterion into DB $data = array('definitionid' => $this->definition->id, 'descriptionformat' => FORMAT_MOODLE); // TODO format is not supported yet foreach ($criteriafields as $key) { if (array_key_exists($key, $criterion)) { $data[$key] = $criterion[$key]; } } if ($doupdate) { $id = $DB->insert_record('gradingform_rubric_criteria', $data); } $haschanges[5] = true; } else { // update criterion in DB $data = array(); foreach ($criteriafields as $key) { if (array_key_exists($key, $criterion) && $criterion[$key] != $currentcriteria[$id][$key]) { $data[$key] = $criterion[$key]; } } if (!empty($data)) { // update only if something is changed $data['id'] = $id; if ($doupdate) { $DB->update_record('gradingform_rubric_criteria', $data); } $haschanges[1] = true; } // 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 ($doupdate) { $DB->delete_records('gradingform_rubric_levels', array('id' => $levelid)); } $haschanges[4] = true; } } } foreach ($levelsdata as $levelid => $level) { if (isset($level['score'])) { $level['score'] = (double) $level['score']; if ($level['score'] < 0) { // TODO why we can't allow negative score for rubric? $level['score'] = 0; } } if (preg_match('/^NEWID\\d+$/', $levelid)) { // insert level into DB $data = array('criterionid' => $id, 'definitionformat' => FORMAT_MOODLE); // TODO format is not supported yet foreach ($levelfields as $key) { if (array_key_exists($key, $level)) { $data[$key] = $level[$key]; } } if ($doupdate) { $levelid = $DB->insert_record('gradingform_rubric_levels', $data); } 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 { // update level in DB $data = array(); foreach ($levelfields as $key) { if (array_key_exists($key, $level) && $level[$key] != $currentcriteria[$id]['levels'][$levelid][$key]) { $data[$key] = $level[$key]; } } if (!empty($data)) { // update only if something is changed $data['id'] = $levelid; if ($doupdate) { $DB->update_record('gradingform_rubric_levels', $data); } if (isset($data['score'])) { $haschanges[3] = true; } $haschanges[1] = true; } } } } // remove deleted criteria from DB foreach (array_keys($currentcriteria) as $id) { if (!array_key_exists($id, $newcriteria)) { if ($doupdate) { $DB->delete_records('gradingform_rubric_criteria', array('id' => $id)); $DB->delete_records('gradingform_rubric_levels', array('criterionid' => $id)); } $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(); } // return the maximum level of changes $changelevels = array_keys($haschanges); sort($changelevels); return array_pop($changelevels); }
/** * Either saves the enriched 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 enriched 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 or changed enriched criteria or changed enriched level values - all students require manual re-grading * * @param stdClass $newdefinition enriched rubric definition data as coming from gradingform_erubric_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_erubric(stdClass $newdefinition, $usermodified = null, $doupdate = false) { global $DB; // Firstly update the common definition data in the {grading_definition} table. 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 // (we need id to save files embedded in description). parent::update_definition(new stdClass(), $usermodified); parent::load_definition(); } if (!isset($newdefinition->erubric['options'])) { $newdefinition->erubric['options'] = self::get_default_options(); } $newdefinition->options = json_encode($newdefinition->erubric['options']); $editoroptions = self::description_form_field_options($this->get_context()); $newdefinition = file_postupdate_standard_editor($newdefinition, 'description', $editoroptions, $this->get_context(), 'grading', 'description', $this->definition->id); // Reload the definition from the database. $currentdefinition = $this->get_definition(true); // Update enriched rubric data. $haschanges = array(); if (empty($newdefinition->erubric['criteria'])) { $newcriteria = array(); } else { $newcriteria = $newdefinition->erubric['criteria']; // New ones to be saved... } $currentcriteria = $currentdefinition->erubric_criteria; // Create tables to use for evaluation of form data. $enrichedcriteriafields = array('sortorder', 'description', 'criteriontype', 'collaborationtype', 'operator', 'referencetype'); $enrichedlevelfields = array('score', 'definition', 'definitionformat', 'enrichedvalue'); foreach ($newcriteria as $id => $criterion) { // Get the list of submitted levels (if they exist). $levelsdata = array(); if (array_key_exists('levels', $criterion)) { $levelsdata = $criterion['levels']; } $criterionmaxscore = null; if (preg_match('/^NEWID\\d+$/', $id)) { // Insert new criterion into DB. $data = array('definitionid' => $this->definition->id, 'descriptionformat' => FORMAT_MOODLE); foreach ($enrichedcriteriafields as $key) { if (array_key_exists($key, $criterion)) { $data[$key] = $criterion[$key] && (string) $criterion[$key] != '' ? $criterion[$key] : null; } } // Handle course modules array (if exist), to store in database. if (array_key_exists('coursemodules', $criterion) && $criterion['coursemodules'] && is_array($criterion['coursemodules'])) { // Check if user wants to publish the given form definition as a new template in the forms bank, // or if he uses a template to create a new form. $formshared = optional_param('shareform', '', PARAM_INT); $pick = optional_param('pick', '', PARAM_INT); if ($formshared || $pick) { // Don't re-encode coursemodules table. $dblbrackets = array('[[', ']]'); $snglbrackets = array('[', ']'); $data['coursemodules'] = str_replace($dblbrackets, $snglbrackets, json_encode($criterion['coursemodules'])); } else { $data['coursemodules'] = json_encode($criterion['coursemodules']); } } else { $data['coursemodules'] = null; } if ($doupdate) { $id = $DB->insert_record('gradingform_erubric_criteria', $data); } $haschanges[5] = true; } else { // Update criterion in DB. $data = array(); // Set missing array elements to empty strings to avoid warnings. if (!array_key_exists('coursemodules', $criterion)) { $criterion['coursemodules'] = ''; } foreach ($enrichedcriteriafields as $key) { if (array_key_exists($key, $criterion) && (string) $criterion[$key] != (string) $currentcriteria[$id][$key]) { $data[$key] = $criterion[$key] && (string) $criterion[$key] != '' ? $criterion[$key] : null; } } // Handle course modules array. if (is_array($criterion['coursemodules'])) { // Check if coursemodules array already exist in current definition. if (array_key_exists('coursemodules', $currentcriteria[$id]) && is_array($currentcriteria[$id]['coursemodules'])) { // Something quick to accurate compare the modules array from the form, with the modules array to be updated. $dblbrackets = array('[[', ']]'); $snglbrackets = array('[', ']'); $tempmodulescompstr1 = str_replace($dblbrackets, $snglbrackets, json_encode($currentcriteria[$id]['coursemodules'])); $tempmodulescompstr2 = json_encode($criterion['coursemodules']); } else { $tempmodulescompstr1 = 1; // Dummy var $tempmodulescompstr2 = 2; // Dummy var } // If there is a change, update current data. if ($tempmodulescompstr1 != $tempmodulescompstr2) { $data['coursemodules'] = json_encode($criterion['coursemodules']); } } else { // In case we need to reset already stored course modules to null... if (array_key_exists('coursemodules', $currentcriteria[$id])) { $data['coursemodules'] = null; } } if (!empty($data)) { // Update only if something is changed... $data['id'] = $id; if ($doupdate) { $DB->update_record('gradingform_erubric_criteria', $data); } // Check if there is a change in any of the enriched fields. if (count($data) > 1 || count($data) == 2 && !isset($data['description'])) { $haschanges[5] = true; // Else, only the criterion description has changed. } else { $haschanges[1] = true; } } // 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 ($doupdate) { $DB->delete_records('gradingform_erubric_levels', array('id' => $levelid)); } $haschanges[4] = true; } } } foreach ($levelsdata as $levelid => $level) { if (isset($level['score'])) { $level['score'] = (double) $level['score']; if ($level['score'] < 0) { $level['score'] = 0; } } if (array_key_exists('enrichedvalue', $level) && !is_null($level['enrichedvalue']) && strlen(trim($level['enrichedvalue'])) > 0) { $level['enrichedvalue'] = (double) $level['enrichedvalue']; } else { $level['enrichedvalue'] = null; } if (preg_match('/^NEWID\\d+$/', $levelid)) { // Insert level into DB. $data = array('criterionid' => $id, 'definitionformat' => FORMAT_MOODLE); foreach ($enrichedlevelfields as $key) { if (array_key_exists($key, $level) && strlen(trim($level[$key])) > 0) { $data[$key] = $level[$key]; } } if ($doupdate) { $levelid = $DB->insert_record('gradingform_erubric_levels', $data); } 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; } // If there is a new enriched level value added, students must be re-graded. if (count($data) > 1) { $haschanges[5] = true; } } else { // Update level in DB. $data = array(); foreach ($enrichedlevelfields as $key) { if (array_key_exists($key, $level) && (string) $level[$key] != (string) $currentcriteria[$id]['levels'][$levelid][$key]) { $data[$key] = $level[$key]; } } if (!empty($data)) { // Update only if something has changed. $data['id'] = $levelid; if ($doupdate) { $DB->update_record('gradingform_erubric_levels', $data); } if (isset($data['score'])) { $haschanges[3] = true; } // Check if there is a change in any of the enriched fields. if (count($data) > 1 || count($data) == 1 && !is_null($data['enrichedvalue'])) { $haschanges[5] = true; // Else, only the criterion description has changed. } else { $haschanges[1] = true; } } } } } // Rremove deleted criteria from DB. foreach (array_keys($currentcriteria) as $id) { if (!array_key_exists($id, $newcriteria)) { if ($doupdate) { $DB->delete_records('gradingform_erubric_criteria', array('id' => $id)); $DB->delete_records('gradingform_erubric_levels', array('criterionid' => $id)); } $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(); } // Return the maximum level of changes. $changelevels = array_keys($haschanges); sort($changelevels); return array_pop($changelevels); }