]> git.mjollnir.org Git - moodle.git/commitdiff
Edit grading form refactoring
authorDavid Mudrak <david.mudrak@gmail.com>
Mon, 4 Jan 2010 17:43:02 +0000 (17:43 +0000)
committerDavid Mudrak <david.mudrak@gmail.com>
Mon, 4 Jan 2010 17:43:02 +0000 (17:43 +0000)
The previous desing was too "base-class oriented". I have realized that
strategies have to deal with loading and saving dimension definitions in
their own way. Getting rid of all db<->form field mappings as it would
work for very simple strategies only.

mod/workshop/editgradingform.php
mod/workshop/grading/accumulative/gradingform.php
mod/workshop/grading/accumulative/simpletest/teststrategy.php
mod/workshop/grading/accumulative/strategy.php
mod/workshop/grading/gradingform.php
mod/workshop/grading/strategy.php
mod/workshop/submission.php

index 8f3914d7e285c30878ad82c2f63229fa86ee8e14..c7710dbd1f93ad5257108d61090b37432c0164f5 100644 (file)
@@ -64,21 +64,21 @@ if (file_exists($strategylib)) {
 $classname = 'workshop_' . $workshop->strategy . '_strategy';
 $strategy = new $classname($workshop);
 
-// load the dimensions from the database
-$dimensions = $strategy->load_dimensions();
+// load the assessment form definition from the database
+// this must be called before get_edit_strategy_form() where we have to know
+// the number of repeating fieldsets
+$formdata = $strategy->load_grading_form();
 
 // load the form to edit the grading strategy dimensions
-$mform = $strategy->get_edit_strategy_form($selfurl, count($dimensions));
+$mform = $strategy->get_edit_strategy_form($selfurl);
 
 // initialize form data
-$formdata = new stdClass;
-$formdata->dimensions = $dimensions;
 $mform->set_data($formdata);
 
 if ($mform->is_cancelled()) {
     redirect($returnurl);
 } elseif ($data = $mform->get_data()) {
-    $strategy->save_dimensions($data);
+    $strategy->save_grading_form($data);
     if (isset($data->saveandclose)) {
         redirect($returnurl);
     } else {
index 269e9dabbcaebca53464906b1b1ffa001b55fa39..8011374c3c503130ee35e47c52eb0b200c5bb273 100644 (file)
@@ -68,7 +68,7 @@ class workshop_edit_accumulative_strategy_form extends workshop_edit_strategy_fo
 
         $numofdimensionstoadd   = 2;
         $numofinitialdimensions = 3;
-        $numofdisplaydimensions = max($this->nocurrentdims + $numofdimensionstoadd, $numofinitialdimensions);
+        $numofdisplaydimensions = max($this->strategy->get_number_of_dimensions() + $numofdimensionstoadd, $numofinitialdimensions);
         $this->repeat_elements($repeated, $numofdisplaydimensions,  $repeatedoptions, 'numofdimensions', 'adddimensions', $numofdimensionstoadd, get_string('addmoredimensionsaccumulative', 'workshop', $numofdimensionstoadd));
     }
 
index 0ffcf7f5d6640b80c06bd1b1453af3dfce6fdb3b..180f7041151c01c005138b97328bb056868ceafe 100644 (file)
@@ -35,9 +35,14 @@ require_once($CFG->dirroot . '/mod/workshop/grading/accumulative/strategy.php');
  */
 class testable_workshop_accumulative_strategy extends workshop_accumulative_strategy {
 
-    public function cook_form_data($raw) {
-        return parent::cook_form_data($raw);
+    public function _cook_dimension_records(array $raw) {
+        return parent::_cook_dimension_records($raw);
     }
+
+    public function _cook_form_data(stdClass $raw) {
+        return parent::_cook_form_data($raw);
+    }
+
 }
 
 
@@ -49,8 +54,11 @@ class workshop_accumulative_strategy_test extends UnitTestCase {
     /** instance of the strategy logic class being tested */
     protected $strategy;
 
-    /** this emulates data returned by get_data() of a submitted strategy edit form */
-    protected $rawdata;
+    /** this emulates dimensions data returned by get_data() of a submitted strategy edit form */
+    protected $rawform;
+
+    /** this emulates dimensions data stored in database to be loaded into strategy edit form */
+    protected $rawdb;
 
     /** setup testing environment */
     public function setUp() {
@@ -63,24 +71,69 @@ class workshop_accumulative_strategy_test extends UnitTestCase {
 
         // emulate a form with 5 dimensions. The first three are already in DB, the forth is new and the
         // fifth is left empty
-        $this->rawdata = new stdClass;
-        $this->rawdata->workshopid          = 42;
-        $this->rawdata->strategy            = 'accumulative';
-        $this->rawdata->numofdimensions     = 5;
-        $this->rawdata->dimensionid         = array(0 => 3,       1=> 2,         2 => 1,       3 => 0,       4 => 0);
-        $this->rawdata->description         = array(0 => 'First', 1 => 'Second', 2 => 'Third', 3 => 'Forth', 4 => '');
-        $this->rawdata->descriptionformat   = array(0 => 1,       1 => 1,        2 => 1,       3 => 1,       4 => 1);
-        $this->rawdata->grade               = array(0 => 10,      1 => 5,        2 => 5,       3 => 2,       4 => 10);
-        $this->rawdata->weight              = array(0 => 1,       1 => 1,        2 => 2,       3 => 2,       4 => 1);
+        $this->rawform = new stdClass;
+        $this->rawform->workshopid          = 42;
+        $this->rawform->strategy            = 'accumulative';
+        $this->rawform->numofdimensions     = 5;
+        $this->rawform->dimensionid         = array(0 => 3,       1=> 2,         2 => 1,       3 => 0,       4 => 0);
+        $this->rawform->description         = array(0 => 'First', 1 => 'Second', 2 => 'Third', 3 => 'Forth', 4 => '');
+        $this->rawform->descriptionformat   = array(0 => 1,       1 => 1,        2 => 1,       3 => 1,       4 => 1);
+        $this->rawform->grade               = array(0 => 10,      1 => 5,        2 => 5,       3 => 2,       4 => 10);
+        $this->rawform->weight              = array(0 => 1,       1 => 1,        2 => 2,       3 => 2,       4 => 1);
+
+        // emulate two assessment dimensions being stored in database
+        $this->rawdb = array();
+        $this->rawdb[3] = new stdClass;
+        $this->rawdb[3]->id                 = 3;
+        $this->rawdb[3]->workshopid         = 42;
+        $this->rawdb[3]->sort               = 1;
+        $this->rawdb[3]->description        = 'First';
+        $this->rawdb[3]->descriptionformat  = 1;
+        $this->rawdb[3]->grade              = 20;
+        $this->rawdb[3]->weight             = 16;
+
+        $this->rawdb[7] = new stdClass;
+        $this->rawdb[7]->id                 = 7;
+        $this->rawdb[7]->workshopid         = 42;
+        $this->rawdb[7]->sort               = 2;
+        $this->rawdb[7]->description        = 'Second';
+        $this->rawdb[7]->descriptionformat  = 1;
+        $this->rawdb[7]->grade              = 10;
+        $this->rawdb[7]->weight             = 1;
+
     }
 
+
     public function tearDown() {
         $this->strategy = null;
+        $this->rawform  = null;
+        $this->rawdb    = null;
     }
 
+    
+    public function test_cook_dimension_records() {
+        
+        $cooked = $this->strategy->_cook_dimension_records($this->rawdb);
+        $this->assertIsA($cooked, 'stdClass');
+        $cooked = (array)$cooked;
+
+        $this->assertEqual($cooked['dimensionid[0]'], 3);
+        $this->assertEqual($cooked['description[0]'], 'First');
+        $this->assertEqual($cooked['descriptionformat[0]'], 1);
+        $this->assertEqual($cooked['grade[0]'], 20);
+        $this->assertEqual($cooked['weight[0]'], 16);
+
+        $this->assertEqual($cooked['dimensionid[1]'], 7);
+        $this->assertEqual($cooked['description[1]'], 'Second');
+        $this->assertEqual($cooked['descriptionformat[1]'], 1);
+        $this->assertEqual($cooked['grade[1]'], 10);
+        $this->assertEqual($cooked['weight[1]'], 1);
+    }
+
+    
     public function test_cook_form_data() {
 
-        $cooked = $this->strategy->cook_form_data($this->rawdata);
+        $cooked = $this->strategy->_cook_form_data($this->rawform);
         $this->assertIsA($cooked, 'Array');
         $this->assertEqual($cooked[2], (object)array(
                             'id'                => 1,
index d08660986cfc6dc5d783398459e6628ca7048307..15d56db7b41ecb59e885c7d417d1898ae8873cba 100644 (file)
@@ -32,21 +32,111 @@ require_once(dirname(dirname(__FILE__)) . '/strategy.php'); // parent class
 /**
  * Accumulative grading strategy logic.
  */
-class workshop_accumulative_strategy extends workshop_strategy {
+class workshop_accumulative_strategy extends workshop_base_strategy {
+
+    public function load_grading_form() {
+        global $DB;
+
+        $dims = $DB->get_records('workshop_forms_' . $this->name, array('workshopid' => $this->workshop->id), 'sort');
+        $this->nodimensions = count($dims);
+        return $this->_cook_dimension_records($dims);
+    }
+
+
+    /**
+     * Transpones the dimension data from DB so the assessment form editor can be populated by set_data
+     *
+     * Called internally from load_grading_form(). Could be private but keeping protected
+     * for unit testing purposes.
+     * 
+     * @param array $raw Array of raw dimension records as fetched by get_record()
+     * @return array Array of fields data to be used by the mform set_data
+     */
+    protected function _cook_dimension_records(array $raw) {
+
+        $formdata = array();
+        $key = 0;
+        foreach ($raw as $dimension) {
+            $formdata['dimensionid[' . $key . ']']       = $dimension->id;
+            $formdata['description[' . $key . ']']       = $dimension->description;
+            $formdata['descriptionformat[' . $key . ']'] = $dimension->descriptionformat;
+            $formdata['grade[' . $key . ']']             = $dimension->grade;
+            $formdata['weight[' . $key . ']']            = $dimension->weight;
+            $key++;
+        }
+        return (object)$formdata;
+    }
+
 
     /**
-     * Mapping of the db fields to the form fields for every dimension of assessment
+     * Save the assessment dimensions into database
      *
-     * @return array Array ['field_db_name' => 'field_form_name']
+     * Saves data into the main strategy form table. If the record->id is null or zero,
+     * new record is created. If the record->id is not empty, the existing record is updated. Records with
+     * empty 'description' field are removed from database.
+     * The passed data object are the raw data returned by the get_data().
+     *
+     * @uses $DB
+     * @param object $data Raw data returned by the dimension editor form
+     * @access public
+     * @return void
      */
-    public function map_dimension_fieldnames() {
-        return array(
-                'id'                => 'dimensionid',
-                'description'       => 'description',
-                'descriptionformat' => 'descriptionformat',
-                'grade'             => 'grade',
-                'weight'            => 'weight',
-            );
+    public function save_grading_form(stdClass $data) {
+        global $DB;
+
+        if (!isset($data->strategyname) || ($data->strategyname != $this->name)) {
+            // the workshop strategy has changed since the form was opened for editing
+            throw new moodle_exception('strategyhaschanged', 'workshop');
+        }
+
+        $data = $this->_cook_form_data($data);
+        $todelete = array();
+        foreach ($data as $record) {
+            if (empty($record->description)) {
+                if (!empty($record->id)) {
+                    // existing record with empty description - to be deleted
+                    $todelete[] = $record->id;
+                }
+                continue;
+            }
+            if (empty($record->id)) {
+                // new field
+                $record->id = $DB->insert_record('workshop_forms_' . $this->name, $record);
+            } else {
+                // exiting field
+                $DB->update_record('workshop_forms_' . $this->name, $record);
+            }
+        }
+        $DB->delete_records_list('workshop_forms_' . $this->name, 'id', $todelete);
+    }
+
+
+    /**
+     * Prepares data returned by mform so they can be saved into database
+     *
+     * It automatically adds some columns into every record. The sorting is 
+     * done by the order of the returned array and starts with 1.
+     * Called internally from save_grading_form() only. Could be private but
+     * keeping protected for unit testing purposes.
+     * 
+     * @param object $raw Raw data returned by mform
+     * @return array Array of objects to be inserted/updated in DB
+     */
+    protected function _cook_form_data(stdClass $raw) {
+
+        $cook = array();
+
+        for ($k = 0; $k < $raw->numofdimensions; $k++) {
+            $cook[$k]                    = new stdClass();
+            $cook[$k]->id                = isset($raw->dimensionid[$k]) ? $raw->dimensionid[$k] : null;
+            $cook[$k]->workshopid        = $this->workshop->id;
+            $cook[$k]->sort              = $k + 1;
+            $cook[$k]->description       = isset($raw->description[$k]) ? $raw->description[$k] : null;
+            $cook[$k]->descriptionformat = FORMAT_HTML;
+            $cook[$k]->grade             = isset($raw->grade[$k]) ? $raw->grade[$k] : null;
+            $cook[$k]->weight            = isset($raw->weight[$k]) ? $raw->weight[$k] : null;
+        }
+        return $cook;
     }
 
 
index 3e28b892f4fe0475b2ebfb2b86dee6a164f0691b..36dc6613fe85f568b0e0f3664763b9b12487d306 100644 (file)
@@ -43,9 +43,6 @@ class workshop_edit_strategy_form extends moodleform {
     /** strategy logic instance that this class is editor of */ 
     protected $strategy;
 
-    /** number of current dimensions loaded from DB */
-    protected $nocurrentdims;
-
     /**
      * Add the fields that are common for all grading strategies.
      *
@@ -61,8 +58,7 @@ class workshop_edit_strategy_form extends moodleform {
         global $CFG;
 
         $mform = $this->_form;
-        $this->strategy         = $this->_customdata['strategy'];
-        $this->nocurrentdims    = $this->_customdata['nocurrentdims']; 
+        $this->strategy = $this->_customdata['strategy'];
 
         $mform->addElement('hidden', 'strategyname', $this->strategy->name);
 
@@ -91,33 +87,4 @@ class workshop_edit_strategy_form extends moodleform {
         // By default, do nothing.
     }
 
-
-    /**
-     * Set the form data before it is displayed
-     *
-     * Strategy plugins should provide the list of fields to be mapped from 
-     * DB record to the form fields in their map_dimension_fieldnames() method
-     * 
-     * @param object $formdata Should contain the array $formdata->dimensions
-     * @access public
-     * @return void
-     */
-    public function set_data($formdata) {
-
-        if (is_array($formdata->dimensions) && !empty($formdata->dimensions)) {
-            // $formdata->dimensions must be array of dimension records loaded from database
-            $key = 0;
-            $default_values = array();
-            foreach ($formdata->dimensions as $dimension) {
-                foreach ($this->strategy->map_dimension_fieldnames() as $fielddbname => $fieldformname) {
-                    $default_values[$fieldformname . '[' . $key . ']'] = $dimension->$fielddbname;
-                }
-                $key++;
-            }
-            $formdata = (object)((array)$formdata + $default_values);
-        }
-        parent::set_data($formdata);
-    }
-
-
 }
index 35d45435f842838a48236698e94d20f2aef4bc08..f769ee573424849d1a5bdc5bcc48799c247c0bf9 100644 (file)
@@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die();
 /**
  * Strategy interface defines all methods that strategy subplugins has to implemens
  */
-interface workshop_strategy_interface {
+interface workshop_strategy {
 
     /**
      * Factory method returning an instance of an assessment form editor class
@@ -38,47 +38,59 @@ interface workshop_strategy_interface {
      * dimensions that will be passed by set_data() must be already known here becase the
      * definition() of the form has to know the number and it is called before set_data().
      *
-     * @param string $actionurl URL of the action handler script
-     * @param int $nocurrentdims Number of current dimensions to be set later by set_data()
+     * @param string $actionurl URL of the action handler script, defaults to auto detect
      * @access public
      * @return object The instance of the assessment form editor class
      */
-    public function get_edit_strategy_form($actionurl, $nocurrentdims=0);
+    public function get_edit_strategy_form($actionurl=null);
 
 
     /**
-     * Load the assessment dimensions from database
+     * Load the assessment dimensions and other grading form elements
      * 
      * Assessment dimension (also know as assessment element) represents one aspect or criterion 
      * to be evaluated. Each dimension consists of a set of form fields. Strategy-specific information
      * are saved in workshop_forms_{strategyname} tables.
+     * The returned object is passed to the mform set_data() method.
      *
      * @access public
-     * @return array Array of database records
+     * @return object Object representing the form fields values
      */
-    public function load_dimensions();
+    public function load_grading_form();
 
 
     /**
-     * Save the assessment dimensions into database
+     * Save the assessment dimensions and other grading form elements
      *
      * Assessment dimension (also know as assessment element) represents one aspect or criterion 
      * to be evaluated. Each dimension consists of a set of form fields. Strategy-specific information
      * are saved in workshop_forms_{strategyname} tables.
      *
      * @access public
+     * @param object $data Raw data as returned by the form editor
      * @return void
      */
-    public function save_dimensions($data);
+    public function save_grading_form(stdClass $data);
+
+
+    /**
+     * Return the number of assessment dimensions defined in the instance of the strategy
+     * 
+     * @return int Zero or positive integer
+     */
+    public function get_number_of_dimensions();
+
 }
 
+
+
 /**
  * Base class for grading strategy logic
  *
  * This base class implements the default behaviour that should be suitable for the most
  * of simple grading strategies.
  */
-class workshop_strategy implements workshop_strategy_interface {
+abstract class workshop_base_strategy implements workshop_strategy {
 
     /** the name of the strategy */
     public $name;
@@ -86,6 +98,9 @@ class workshop_strategy implements workshop_strategy_interface {
     /** the parent workshop instance */
     protected $workshop;
 
+    /** number of dimensions defined in database */
+    protected $nodimensions;
+
     /**
      * Constructor 
      * 
@@ -95,8 +110,9 @@ class workshop_strategy implements workshop_strategy_interface {
      */
     public function __construct($workshop) {
 
-        $this->name     = $workshop->strategy;
-        $this->workshop = $workshop;
+        $this->name         = $workshop->strategy;
+        $this->workshop     = $workshop;
+        $this->nodimensions = null;
     }
 
 
@@ -105,8 +121,10 @@ class workshop_strategy implements workshop_strategy_interface {
      *
      * By default, the class is defined in grading/{strategy}/gradingform.php and is named
      * workshop_edit_{strategy}_strategy_form
+     *
+     * @param $actionurl URL of form handler, defaults to auto detect the current url
      */
-    public function get_edit_strategy_form($actionurl, $nocurrentdims=0) {
+    public function get_edit_strategy_form($actionurl=null) {
         global $CFG;    // needed because the included files use it
     
         $strategyform = dirname(__FILE__) . '/' . $this->name . '/gradingform.php';
@@ -120,8 +138,6 @@ class workshop_strategy implements workshop_strategy_interface {
         $customdata = new stdClass;
         $customdata = array(
                         'strategy'      => $this,
-                        'nocurrentdims' => $nocurrentdims,
-
                         );
         $attributes = array('class' => 'editstrategyform');
 
@@ -131,92 +147,13 @@ class workshop_strategy implements workshop_strategy_interface {
 
 
     /**
-     * Load the assessment dimensions from database
+     * By default, the number of loaded dimensions is set by load_grading_form() 
      * 
-     * This base method just fetches all relevant records from the main strategy form table.
-     *
-     * @uses $DB
      * @access public
      * @return void
      */
-    public function load_dimensions() {
-        global $DB;
-
-        return $DB->get_records('workshop_forms_' . $this->name, array('workshopid' => $this->workshop->id), 'sort');
-    }
-
-
-    /**
-     * Save the assessment dimensions into database
-     *
-     * This base method saves data into the main strategy form table. If the record->id is null or zero,
-     * new record is created. If the record->id is not empty, the existing record is updated. Records with
-     * empty 'description' field are not saved.
-     * The passed data object are the raw data returned by the get_data(). They must be cooked here.
-     *
-     * @uses $DB
-     * @param object $data Raw data returned by the dimension editor form
-     * @access public
-     * @return void
-     */
-    public function save_dimensions($data) {
-        global $DB;
-        
-        if (!isset($data->strategyname) || ($data->strategyname != $this->name)) {
-            // the workshop strategy has changed since the form was opened for editing
-            throw new moodle_exception('strategyhaschanged', 'workshop');
-        }
-
-        $data = $this->cook_form_data($data);
-
-        foreach ($data as $record) {
-            if (empty($record->description)) {
-                continue;
-            }
-            if (empty($record->id)) {
-                // new field
-                $record->id = $DB->insert_record('workshop_forms_' . $this->name, $record);
-            } else {
-                // exiting field
-                $DB->update_record('workshop_forms_' . $this->name, $record);
-            }
-        }
-    }
-
-
-    /**
-     * The default implementation transposes the returned structure
-     *
-     * It automatically adds some columns into every record.
-     * The sorting is done by the order of the returned array and starts with 1.
-     * 
-     * @param object $raw 
-     * @return void
-     */
-    protected function cook_form_data($raw) {
-
-        $cook = array();
-        foreach (array_flip($this->map_dimension_fieldnames()) as $formfield => $dbfield) {
-            for ($k = 0; $k < $raw->numofdimensions; $k++) {
-                $cook[$k]->{$dbfield}   = isset($raw->{$formfield}[$k]) ? $raw->{$formfield}[$k] : null;
-                $cook[$k]->descriptionformat    = FORMAT_HTML;
-                $cook[$k]->sort                 = $k + 1;
-                $cook[$k]->workshopid           = $this->workshop->id;
-            }
-        }
-        return $cook;
-    }
-
-
-    /**
-     * Return the mapping of the db fields to the form fields for every assessment dimension
-     *
-     * This must be public because it is also used by the dimensions editor class.
-     * 
-     * @return array Array ['field_db_name' => 'field_form_name']
-     */
-    public function map_dimension_fieldnames() {
-        return array();
+    public function get_number_of_dimensions() {
+        return $this->nodimensions;
     }
 
 
index f30e6f5d73c95968899b5fd384190fa1aa2ec302..a1c06b40aec092c4755b0b4f97252c5b940e9376 100644 (file)
@@ -85,7 +85,7 @@ $mform = new workshop_submission_form(null, array('current' => $submission, 'cm'
                                                  'dataoptions' => $dataoptions, 'attachmentoptions'=>$attachmentoptions));
 
 if ($mform->is_cancelled()){
-    die();
+    die(); // todo
     if ($id){
         redirect("view.php?id=$cm->id");
     } else {