Merge pull request #13 from marinaglancy/wip-formvalidation-rubric

Initial prototype of integrating advanced grading methods into current forms

There is a new form element called 'grading', which receives grading_controller as an argument and uses it's functions to produce html and validate the value.

The biggest change in grading form is that I included call to new function validate_and_preprocess_feedback, which validates and also fills the xgrade field with value, calculated by controller

Another issue: when the teacher grades the student on offline assignment for the first time, there may be no entry in submission table (and therefore no id). So I create a submission if there is advanced grading otherwise we won't be able to save via AJAX the process of filling rubric, because normal grading will create an entry only on submit.

And as an example I created text field in rubric, that is saved as grade. There is validation that value can not be more than 100.
This commit is contained in:
David Mudrak 2011-10-12 01:06:27 -07:00
commit a5c4c99b24
7 changed files with 287 additions and 7 deletions

View File

@ -264,6 +264,30 @@ abstract class gradingform_controller {
}
}
/**
* Saves non-js data and returns the gradebook grade
*/
abstract public function save_and_get_grade($itemid, $formdata);
/**
* Returns html for form element
*/
abstract public function to_html($gradingformelement);
/**
*
*/
public function default_validation_error_message() {
return '';
}
/**
*
*/
public function validate_grading_element($elementvalue, $itemid) {
return true;
}
////////////////////////////////////////////////////////////////////////////

View File

@ -226,6 +226,41 @@ class gradingform_rubric_controller extends gradingform_controller {
return $properties;
}
/**
* Saves non-js data and returns the gradebook grade
*/
public function save_and_get_grade($itemid, $formdata) {
// TODO: this function is a patch at the moment!
if (is_array($formdata) && array_key_exists('grade', $formdata)) {
return $formdata['grade'];
}
return -1;
}
/**
* Returns html for form element
*/
public function to_html($gradingformelement) {
// TODO: this function is a patch at the moment!
//global $PAGE, $USER;
//$gradingrenderer = $this->prepare_renderer($PAGE);
$output = '';
$elementname = $gradingformelement->getName();
$elementvalue = $gradingformelement->getValue();
$submissionid = $gradingformelement->get_grading_attribute('submissionid');
$output .= "assessing submission $submissionid<br />";
$output .= html_writer::empty_tag('input', array('type' => 'text', 'name' => $elementname.'[grade]', 'size' => '20', 'value' => $elementvalue['grade']));
//$output .= "assessing user $userid on assignment $assignmentid<br>";
//TODO find $submissionid from $userid & $assignmentid (may not exist yet, actually)
/*$submissionid = null;
$gradingwidget = $this->make_grading_widget($USER->id, $submissionid);
if ($gradingwidget instanceof renderable) {
return $output. $gradingrenderer->render($gradingwidget);
}*/
return $output;
}
// TODO the following functions may be moved to parent:
/**
@ -273,4 +308,26 @@ class gradingform_rubric_controller extends gradingform_controller {
// TODO change filearea for embedded files in grading_definition.description
return $data;
}
public function is_form_available($foruserid = null) {
return true;
// TODO this is temporary for testing!
}
/**
*
*/
public function default_validation_error_message() {
return 'Validation failed';
}
/**
*
*/
public function validate_grading_element($elementvalue, $itemid) {
if ($elementvalue['grade'] > 100 || $elementvalue['grade'] < 0) {
return false;
}
return true;
}
}

View File

@ -374,6 +374,19 @@ class grading_manager {
return new $classname($this->context, $this->component, $this->area, $this->areacache->id);
}
/**
* Returns the controller for the active method if it is available
*/
public function get_active_controller() {
if ($gradingmethod = $this->get_active_method()) {
$controller = $this->get_controller($gradingmethod);
if ($controller->is_form_available()) {
return $controller;
}
}
return null;
}
////////////////////////////////////////////////////////////////////////////
/**

View File

@ -26,6 +26,7 @@
defined('MOODLE_INTERNAL') || die();
$string['exc_gradingformelement'] = 'Unable to instantiate grading form element';
$string['formnotavailable'] = 'Advanced grading method was selected to use but the grading form is not available yet. You may need to define it first via a link in the Settings block.';
$string['gradinginarea'] = 'Grading ({$a})';
$string['gradingmethod'] = 'Grading method';

118
lib/form/grading.php Normal file
View File

@ -0,0 +1,118 @@
<?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/>.
/**
* Element-container for advanced grading custom input
*
* @copyright 2011 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
global $CFG;
require_once("HTML/QuickForm/element.php");
require_once($CFG->dirroot.'/grade/grading/form/lib.php');
if (class_exists('HTML_QuickForm')) {
HTML_QuickForm::registerRule('gradingvalidated', 'callback', '_validate', 'MoodleQuickForm_grading');
}
/**
* HTML class for a grading element
*
* @author Marina Glancy
* @access public
*/
class MoodleQuickForm_grading extends HTML_QuickForm_input{
/**
* html for help button, if empty then no help
*
* @var string
*/
var $_helpbutton='';
private $gradingattributes;
function MoodleQuickForm_grading($elementName=null, $elementLabel=null, $attributes=null) {
parent::HTML_QuickForm_input($elementName, $elementLabel, $attributes);
$this->gradingattributes = $attributes;
}
function toHtml(){
return $this->get_controller()->to_html($this);
}
function get_grading_attribute($name) {
return $this->gradingattributes[$name];
}
function get_controller() {
return $this->get_grading_attribute('controller');
}
/**
* set html for help button
*
* @access public
* @param array $help array of arguments to make a help button
* @param string $function function name to call to get html
*/
function setHelpButton($helpbuttonargs, $function='helpbutton'){
debugging('component setHelpButton() is not used any more, please use $mform->setHelpButton() instead');
}
/**
* get html for help button
*
* @access public
* @return string html for help button
*/
function getHelpButton(){
return $this->_helpbutton;
}
/**
* @return string
*/
function getElementTemplateType(){
return 'default';
}
/**
* Adds necessary rules to the element
*/
function onQuickFormEvent($event, $arg, &$caller) {
if ($event == 'createElement') {
$attributes = $arg[2];
if (!is_array($attributes) || !array_key_exists('controller', $attributes) || !($attributes['controller'] instanceof gradingform_controller)) {
throw new moodle_exception('exc_gradingformelement', 'grading');
}
}
$name = $this->getName();
if ($name && $caller->elementExists($name)) {
$caller->addRule($name, $this->get_controller()->default_validation_error_message(), 'gradingvalidated', $this->gradingattributes);
}
return parent::onQuickFormEvent($event, $arg, $caller);
}
/**
* Function registered as rule for this element and is called when this element is being validated
*/
static function _validate($elementValue, $attributes = null) {
return $attributes['controller']->validate_grading_element($elementValue, $attributes['submissionid']);
}
}

View File

@ -2487,6 +2487,7 @@ MoodleQuickForm::registerElementType('file', "$CFG->libdir/form/file.php", 'Mood
MoodleQuickForm::registerElementType('filemanager', "$CFG->libdir/form/filemanager.php", 'MoodleQuickForm_filemanager');
MoodleQuickForm::registerElementType('filepicker', "$CFG->libdir/form/filepicker.php", 'MoodleQuickForm_filepicker');
MoodleQuickForm::registerElementType('format', "$CFG->libdir/form/format.php", 'MoodleQuickForm_format');
MoodleQuickForm::registerElementType('grading', "$CFG->libdir/form/grading.php", 'MoodleQuickForm_grading');
MoodleQuickForm::registerElementType('group', "$CFG->libdir/form/group.php", 'MoodleQuickForm_group');
MoodleQuickForm::registerElementType('header', "$CFG->libdir/form/header.php", 'MoodleQuickForm_header');
MoodleQuickForm::registerElementType('hidden', "$CFG->libdir/form/hidden.php", 'MoodleQuickForm_hidden');

View File

@ -630,7 +630,10 @@ class assignment_base {
switch ($mode) {
case 'grade': // We are in a main window grading
if ($submission = $this->process_feedback()) {
if (!$this->validate_and_preprocess_feedback()) {
// validation failed
$this->display_submission();
} else if ($submission = $this->process_feedback()) {
$this->display_submissions(get_string('changessaved'));
} else {
$this->display_submissions();
@ -744,7 +747,11 @@ class assignment_base {
case 'saveandnext':
///We are in pop up. save the current one and go to the next one.
//first we save the current changes
if ($submission = $this->process_feedback()) {
if (!$this->validate_and_preprocess_feedback()) {
// validation failed
$this->display_submission();
break;
} else if ($submission = $this->process_feedback()) {
//print_heading(get_string('changessaved'));
//$extra_javascript = $this->update_main_listing($submission);
}
@ -1039,7 +1046,7 @@ class assignment_base {
} elseif ($assignment->assignmenttype == 'uploadsingle') {
$mformdata->fileui_options = array('subdirs'=>0, 'maxbytes'=>$CFG->userquota, 'maxfiles'=>1, 'accepted_types'=>'*', 'return_types'=>FILE_INTERNAL);
}
$gradingman = get_grading_manager($this->context, 'mod_assignment', 'submission');
/*$gradingman = get_grading_manager($this->context, 'mod_assignment', 'submission');
if ($gradingmethod = $gradingman->get_active_method()) {
$controller = $gradingman->get_controller($gradingmethod);
if ($controller->is_form_available()) {
@ -1067,6 +1074,13 @@ class assignment_base {
} else {
notice(get_string('formnotavailable', 'core_grading'), new moodle_url('/course/view.php', array('id' => $assignment->course)));
}
}*/
if ($controller = get_grading_manager($this->context, 'mod_assignment', 'submission')->get_active_controller()) {
if (!isset($submission->id)) {
// TODO this is a patch if submission id does not exist yet
$mformdata->submission = $this->get_submission($user->id, true);
}
$mformdata->advancedgradingcontroller = $controller;
}
$submitform = new mod_assignment_grading_form( null, $mformdata );
@ -1074,7 +1088,9 @@ class assignment_base {
if (!$display) {
$ret_data = new stdClass();
$ret_data->mform = $submitform;
if (isset($mformdata->fileui_options)) {
$ret_data->fileui_options = $mformdata->fileui_options;
}
return $ret_data;
}
@ -1574,6 +1590,33 @@ class assignment_base {
echo $OUTPUT->footer();
}
/**
* Validates the submitted form and returns false if validation did not pass.
* If validation passes, preprocess advanced grading (if applicable) and returns true.
*/
function validate_and_preprocess_feedback() {
if (!$feedback = data_submitted()) {
return true; // No incoming data, nothing to validate
}
$userid = required_param('userid', PARAM_INT);
$offset = required_param('offset', PARAM_INT);
$submissiondata = $this->display_submission($offset, $userid, false);
$mform = $submissiondata->mform;
if ($mform->is_submitted()) {
if (!$mform->is_validated()) {
return false;
}
// preprocess advanced grading here
if ($controller = $mform->use_advanced_grading()) {
$data = $mform->get_data();
// TODO find better way to find submission id
$submission = $this->get_submission($userid);
$_POST['xgrade'] = $controller->save_and_get_grade($submission->id, $data->advancedgrading);
}
}
return true;
}
/**
* Process teacher feedback submission
*
@ -2252,6 +2295,10 @@ class mod_assignment_grading_form extends moodleform {
global $OUTPUT;
$mform =& $this->_form;
if (isset($this->_customdata->advancedgradingcontroller)) {
$this->use_advanced_grading($this->_customdata->advancedgradingcontroller);
}
$formattr = $mform->getAttributes();
$formattr['id'] = 'submitform';
$mform->setAttributes($formattr);
@ -2297,6 +2344,19 @@ class mod_assignment_grading_form extends moodleform {
}
private $_advancegradingcontroller;
/**
* Gets or sets the controller for advanced grading
*
* @param <type> $controller
*/
public function use_advanced_grading($controller = false) {
if ($controller !== false) {
$this->_advancegradingcontroller = $controller;
}
return $this->_advancegradingcontroller;
}
function add_grades_section() {
global $CFG;
$mform =& $this->_form;
@ -2307,9 +2367,10 @@ class mod_assignment_grading_form extends moodleform {
$mform->addElement('header', 'Grades', get_string('grades', 'grades'));
if (!empty($this->_customdata->advancedgradingenabled)) {
$mform->addElement('static', 'advancedgradingwidget', get_string('grade').':', $this->_customdata->advancedgradingwidget);
if ($controller = $this->use_advanced_grading()) {
// TODO what if submission id does not exist yet!
$mform->addElement('grading', 'advancedgrading', get_string('grade').':',
array('controller' => $controller, 'submissionid' => $this->_customdata->submission->id));
} else {
// use simple direct grading
$grademenu = make_grades_menu($this->_customdata->assignment->grade);
@ -2468,6 +2529,11 @@ class mod_assignment_grading_form extends moodleform {
}
$data = file_postupdate_standard_editor($data, 'submissioncomment', $editoroptions, $this->_customdata->context, $editoroptions['component'], $editoroptions['filearea'], $itemid);
}
if ($this->use_advanced_grading() && !isset($data->advancedgrading)) {
$data->advancedgrading = null;
}
return $data;
}
}