MDL-57724 mod_lesson: New Web Service mod_lesson_finish_attempt
authorJuan Leyva <juanleyvadelgado@gmail.com>
Thu, 19 Jan 2017 10:10:00 +0000 (11:10 +0100)
committerJuan Leyva <juanleyvadelgado@gmail.com>
Wed, 22 Mar 2017 16:24:24 +0000 (17:24 +0100)
mod/lesson/classes/external.php
mod/lesson/db/services.php
mod/lesson/tests/external_test.php
mod/lesson/version.php

index d5a4f88..034a01d 100644 (file)
@@ -1537,4 +1537,117 @@ class mod_lesson_external extends external_api {
             )
         );
     }
+
+    /**
+     * Describes the parameters for finish_attempt.
+     *
+     * @return external_external_function_parameters
+     * @since Moodle 3.3
+     */
+    public static function finish_attempt_parameters() {
+        return new external_function_parameters (
+            array(
+                'lessonid' => new external_value(PARAM_INT, 'Lesson instance id.'),
+                'password' => new external_value(PARAM_RAW, 'Optional password (the lesson may be protected).', VALUE_DEFAULT, ''),
+                'outoftime' => new external_value(PARAM_BOOL, 'If the user run out of time.', VALUE_DEFAULT, false),
+                'review' => new external_value(PARAM_BOOL, 'If we want to review just after finishing (1 hour margin).',
+                    VALUE_DEFAULT, false),
+            )
+        );
+    }
+
+    /**
+     * Finishes the current attempt.
+     *
+     * @param int $lessonid lesson instance id
+     * @param str $password optional password (the lesson may be protected)
+     * @param bool $outoftime optional if the user run out of time
+     * @param bool $review if we want to review just after finishing (1 hour margin)
+     * @return array of warnings and information about the finished attempt
+     * @since Moodle 3.3
+     * @throws moodle_exception
+     */
+    public static function finish_attempt($lessonid, $password = '', $outoftime = false, $review = false) {
+
+        $params = array('lessonid' => $lessonid, 'password' => $password, 'outoftime' => $outoftime, 'review' => $review);
+        $params = self::validate_parameters(self::finish_attempt_parameters(), $params);
+
+        $warnings = array();
+
+        list($lesson, $course, $cm, $context) = self::validate_lesson($params['lessonid']);
+
+        // Update timer so the validation can check the time restrictions.
+        $timer = $lesson->update_timer();
+
+        // Return the validation to avoid exceptions in case the user is out of time.
+        $params['pageid'] = LESSON_EOL;
+        $validation = self::validate_attempt($lesson, $params, true);
+
+        if (array_key_exists('eolstudentoutoftime', $validation)) {
+            // Maybe we run out of time just now.
+            $params['outoftime'] = true;
+            unset($validation['eolstudentoutoftime']);
+        }
+        // Check if there are more errors.
+        if (!empty($validation)) {
+            reset($validation);
+            throw new moodle_exception(key($validation), 'lesson', '', current($validation));   // Throw first error.
+        }
+
+        $result = $lesson->process_eol_page($params['outoftime']);
+
+        // Return the data.
+         $validmessages = array(
+            'notenoughtimespent', 'numberofpagesviewed', 'youshouldview', 'numberofcorrectanswers',
+            'displayscorewithessays', 'displayscorewithoutessays', 'yourcurrentgradeisoutof', 'eolstudentoutoftimenoanswers',
+            'welldone', 'displayofgrade', 'reviewlesson', 'modattemptsnoteacher', 'progresscompleted');
+
+        $data = array();
+        foreach ($result as $el => $value) {
+            if ($value !== false) {
+                $message = '';
+                if (in_array($el, $validmessages)) { // Check if the data comes with an informative message.
+                    $a = (is_bool($value)) ? null : $value;
+                    $message = get_string($el, 'lesson', $a);
+                }
+                // Return the data.
+                $data[] = array(
+                    'name' => $el,
+                    'value' => (is_bool($value)) ? 1 : json_encode($value), // The data can be a php object.
+                    'message' => $message
+                );
+            }
+        }
+
+        $result = array(
+            'data'     => $data,
+            'messages' => self::format_lesson_messages($lesson),
+            'warnings' => $warnings,
+        );
+        return $result;
+    }
+
+    /**
+     * Describes the finish_attempt return value.
+     *
+     * @return external_single_structure
+     * @since Moodle 3.3
+     */
+    public static function finish_attempt_returns() {
+        return new external_single_structure(
+            array(
+                'data' => new external_multiple_structure(
+                    new external_single_structure(
+                        array(
+                            'name' => new external_value(PARAM_ALPHANUMEXT, 'Data name.'),
+                            'value' => new external_value(PARAM_RAW, 'Data value.'),
+                            'message' => new external_value(PARAM_RAW, 'Data message (translated string).'),
+                        )
+                    ), 'The EOL page information data.'
+                ),
+                'messages' => self::external_messages(),
+                'warnings' => new external_warnings(),
+            )
+        );
+    }
 }
index 09224df..4e1c874 100644 (file)
@@ -124,4 +124,12 @@ $functions = array(
         'capabilities'  => 'mod/lesson:view',
         'services'      => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
     ),
+    'mod_lesson_finish_attempt' => array(
+        'classname'     => 'mod_lesson_external',
+        'methodname'    => 'finish_attempt',
+        'description'   => 'Finishes the current attempt.',
+        'type'          => 'write',
+        'capabilities'  => 'mod/lesson:view',
+        'services'      => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
+    ),
 );
index 625aa13..7a98c60 100644 (file)
@@ -1042,4 +1042,93 @@ class mod_lesson_external_testcase extends externallib_advanced_testcase {
         $this->assertFalse($result['maxattemptsreached']);  // Still one attempt.
         $this->assertEquals(50, $result['progress']);
     }
+
+    /**
+     * Test finish attempt not doing anything.
+     */
+    public function test_finish_attempt_not_doing_anything() {
+
+        $this->setUser($this->student);
+        // First we need to launch the lesson so the timer is on.
+        mod_lesson_external::launch_attempt($this->lesson->id);
+
+        $result = mod_lesson_external::finish_attempt($this->lesson->id);
+        $result = external_api::clean_returnvalue(mod_lesson_external::finish_attempt_returns(), $result);
+
+        $this->assertCount(0, $result['warnings']);
+        $returneddata = [];
+        foreach ($result['data'] as $data) {
+            $returneddata[$data['name']] = $data['value'];
+        }
+        $this->assertEquals(1, $returneddata['gradelesson']);   // Graded lesson.
+        $this->assertEquals(1, $returneddata['welldone']);      // Finished correctly (even without grades).
+        $gradeinfo = json_decode($returneddata['gradeinfo']);
+        $expectedgradeinfo = (object) [
+            'nquestions' => 0,
+            'attempts' => 0,
+            'total' => 0,
+            'earned' => 0,
+            'grade' => 0,
+            'nmanual' => 0,
+            'manualpoints' => 0,
+        ];
+    }
+
+    /**
+     * Test finish attempt with correct answer.
+     */
+    public function test_finish_attempt_with_correct_answer() {
+        global $DB;
+
+        $this->setUser($this->student);
+        // First we need to launch the lesson so the timer is on.
+        mod_lesson_external::launch_attempt($this->lesson->id);
+
+        // Attempt a question, correct answer.
+        $DB->set_field('lesson', 'custom', 0, array('id' => $this->lesson->id));
+        $DB->set_field('lesson', 'progressbar', 1, array('id' => $this->lesson->id));
+
+        $answercorrect = 0;
+        $p2answers = $DB->get_records('lesson_answers', array('lessonid' => $this->lesson->id, 'pageid' => $this->page2->id), 'id');
+        foreach ($p2answers as $answer) {
+            if ($answer->jumpto != 0) {
+                $answercorrect = $answer->id;
+            }
+        }
+
+        $data = array(
+            array(
+                'name' => 'answerid',
+                'value' => $answercorrect,
+            ),
+            array(
+                'name' => '_qf__lesson_display_answer_form_truefalse',
+                'value' => 1,
+            )
+        );
+        $result = mod_lesson_external::process_page($this->lesson->id, $this->page2->id, $data);
+        $result = external_api::clean_returnvalue(mod_lesson_external::process_page_returns(), $result);
+
+        $result = mod_lesson_external::finish_attempt($this->lesson->id);
+        $result = external_api::clean_returnvalue(mod_lesson_external::finish_attempt_returns(), $result);
+
+        $this->assertCount(0, $result['warnings']);
+        $returneddata = [];
+        foreach ($result['data'] as $data) {
+            $returneddata[$data['name']] = $data['value'];
+        }
+        $this->assertEquals(1, $returneddata['gradelesson']);   // Graded lesson.
+        $this->assertEquals(1, $returneddata['numberofpagesviewed']);
+        $this->assertEquals(1, $returneddata['numberofcorrectanswers']);
+        $gradeinfo = json_decode($returneddata['gradeinfo']);
+        $expectedgradeinfo = (object) [
+            'nquestions' => 1,
+            'attempts' => 1,
+            'total' => 1,
+            'earned' => 1,
+            'grade' => 100,
+            'nmanual' => 0,
+            'manualpoints' => 0,
+        ];
+    }
 }
index d7b95f6..dc0ab41 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2016120511;     // The current module version (Date: YYYYMMDDXX)
+$plugin->version   = 2016120512;     // The current module version (Date: YYYYMMDDXX)
 $plugin->requires  = 2016112900;    // Requires this Moodle version
 $plugin->component = 'mod_lesson'; // Full name of the plugin (used for diagnostics)
 $plugin->cron      = 0;