jenkins-bot has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/374035 )

Change subject: Add ability for proposal positions to be shuffled on load
......................................................................


Add ability for proposal positions to be shuffled on load

Currently the quiz extension only shuffles the order of questions using Js.
This change adds shuffling feature for proposal for each questions by using
shuffle function of Php.It is achieved by using shuffleanswer parameter for
quiz.Tests are also added for the same.

Bug: T170799
Change-Id: I7bdeade1fb5359e0db2051a48a2e51831f673ba7
---
M Question.php
M Quiz.class.php
M tests/phpunit/QuestionTest.php
3 files changed, 142 insertions(+), 12 deletions(-)

Approvals:
  jenkins-bot: Verified
  Mvolz: Looks good to me, approved



diff --git a/Question.php b/Question.php
index 93c1e25..d9c1a2e 100644
--- a/Question.php
+++ b/Question.php
@@ -4,17 +4,19 @@
        /**
         * Constructor
         *
-        * @param bool $beingCorrected
-        * @param bool $caseSensitive
-        * @param int $questionId the Identifier of the question used to 
generate input names.
-        * @param Parser &$parser the wikitext parser.
+        * @param Boolean $beingCorrected : Identifier for quiz being corrected.
+        * @param Boolean $caseSensitive : Identifier for case sensitive inputs.
+        * @param Integer $questionId : the Identifier of the question used to 
generate input names.
+        * @param Integer $shufAns : Identifier if answers are supposed to be 
shuffled.
+        * @param Parser &$parser : Parser the wikitext parser.
         */
-       public function __construct( $beingCorrected, $caseSensitive, 
$questionId, &$parser ) {
+       public function __construct( $beingCorrected, $caseSensitive, 
$questionId, $shufAns, &$parser ) {
                global $wgRequest;
                $this->mRequest = &$wgRequest;
                $this->mQuestionId = $questionId;
                $this->mBeingCorrected = $beingCorrected;
                $this->mCaseSensitive = $caseSensitive;
+               $this->shuffleAnswers = $shufAns;
                $this->mParser = $parser;
                $this->mState = ( $beingCorrected ) ? 'NA' : '';
                $this->mType = 'multipleChoice';
@@ -125,6 +127,44 @@
        }
 
        /**
+        * Check order obtained from request
+        *
+        * @param string $order : The order obtained from request
+        * @param Integer $proposalIndex : Contains the index of last Proposal
+        *
+        * @return int
+        */
+       function checkRequestOrder( $order, $proposalIndex ) {
+               $tempOrder = explode( ' ', $order );
+
+               // Check the number of order matches number of proposals
+               if ( count( $tempOrder ) !== $proposalIndex + 1 ) {
+                       return 1;
+               }
+
+               // Check if each value is numeric and is within 0 to 
proposalIndex rannge
+               foreach ( $tempOrder as $orderVal ) {
+                       if ( !is_numeric( $orderVal ) ) {
+                               return 1;
+                       }
+                       if ( $orderVal < 0 || $orderVal > $proposalIndex ) {
+                               return 1;
+                       }
+               }
+
+               // Check for repeated values
+               $orderChecker = array_fill( 0, $proposalIndex + 1, 0 );
+               foreach ( $tempOrder as $orderVal ) {
+                       if ( $orderChecker[ $orderVal ] !== 1 ) {
+                               $orderChecker[ $orderVal ] = 1;
+                       } else {
+                               return 1;
+                       }
+               }
+               return 0;
+       }
+
+       /**
         * Transmit a single choice object to the basic type parser.
         *
         * @param string $input A question object in quiz syntax.
@@ -162,12 +202,15 @@
                // Parameters used in some special cases.
                $expectOn = 0;
                $attemptChecker = 0;
+               $lines = [];
+               $proposalIndex = -1;
                $checkedCount = 0;
                foreach ( $raws as $proposalId => $raw ) {
                        $text = null;
                        $colSpan = '';
                        $signesOutput = '';
                        if ( preg_match( $this->mProposalPattern, $raw, 
$matches ) ) {
+                               $proposalIndex++;
                                $rawClass = 'proposal';
                                // Insulate the proposal signes.
                                $text = array_pop( $matches );
@@ -275,18 +318,73 @@
                                $colSpan = ' colspan="13"';
                        }
                        if ( $text ) {
-                               $output .= '<tr class="' . $rawClass . '">' . 
"\n";
-                               $output .= $signesOutput;
-                               $output .= '<td' . $colSpan . '>';
-                               $output .= $this->mParser->recursiveTagParse( 
$text );
-                               $output .= '</td>';
-                               $output .= '</tr>' . "\n";
+                               $lineOutput = '';
+                               $lineOutput = '<tr class="' . $rawClass . '">' 
. "\n";
+                               $lineOutput .= $signesOutput;
+                               $lineOutput .= '<td' . $colSpan . '>';
+                               $lineOutput .= 
$this->mParser->recursiveTagParse( $text );
+                               $lineOutput .= '</td>';
+                               $lineOutput .= '</tr>' . "\n";
+                               if ( $rawClass === 'correction selected' || 
$rawClass === 'correction unselected' ) {
+                                       if ( $proposalIndex === -1 ) {
+                                               // Add to output directly
+                                               $output .= $lineOutput;
+                                       } else {
+                                               // Add feedback to previous 
proposal
+                                               $lines[ $proposalIndex ] .= 
$lineOutput;
+                                       }
+                               } else {
+                                       // Add lineOutput for proposal
+                                       $lines[ $proposalIndex ] = $lineOutput;
+                               }
                        }
                }
                // A single choice object with no correct proposal is a syntax 
error.
                if ( isset( $typeId ) && $typeId == 'sn' && $expectOn == 0 ) {
                        $this->setState( 'error' );
                }
+               // Finding order of shuffled proposals
+               $order = '';
+               if ( $this->shuffleAnswers ) {
+                       $order = '';
+                       if ( $this->mBeingCorrected ) {
+                               $orderInvalid = 0;
+                               $order = $this->mRequest->getVal( 
$this->mQuestionId . '|order' );
+                               // Check order values
+                               $orderInvalid = $this->checkRequestOrder( 
$order, $proposalIndex );
+
+                               // If order is invalid then order is reset
+                               if ( $orderInvalid ) {
+                                       $order = '';
+                                       for ( $i = 0; $i <= $proposalIndex; 
$i++ ) {
+                                               $order .= ' '.$i;
+                                       }
+                               }
+                       } else {
+                               for ( $i = 0; $i <= $proposalIndex; ) {
+                                       $j = rand( 0, $proposalIndex );
+                                       if ( strpos( $order, ''.$j ) == false ) 
{
+                                               $order .= ' '.$j;
+                                               $i++;
+                                       }
+                               }
+                       }
+               } else {
+                       for ( $i = 0; $i <= $proposalIndex; $i++ ) {
+                               $order .= ' '.$i;
+                       }
+               }
+               $order = ltrim( $order );
+               $tempOrder = explode( ' ', $order );
+               for ( $i = 0; $i <= $proposalIndex; ++$i ) {
+                       $output .= $lines[ $tempOrder[ $i ] ];
+               }
+               $orderName = $this->mQuestionId . '|order';
+               $orderValue = $order;
+               $attribs['hidden'] = 'hidden';
+               $attribs['checked'] = 'checked';
+               $orderHtml = Xml::input( $orderName, $orderSize = null, 
$orderValue, $attribs );
+               $output = $this->shuffleAnswers ? $orderHtml . $output : 
$output;
                return $output;
        }
 
diff --git a/Quiz.class.php b/Quiz.class.php
index 10e2785..6457b81 100644
--- a/Quiz.class.php
+++ b/Quiz.class.php
@@ -35,6 +35,8 @@
                $this->mIgnoringCoef = false;
                $this->mDisplaySimple = ( array_key_exists( 'display', $argv ) 
&&
                        $argv['display'] == 'simple' );
+               $this->shuffleAnswers = ( array_key_exists( 'shuffleanswers', 
$argv ) &&
+                       $argv['shuffleanswers'] == 'true' );
 
                if ( $this->mBeingCorrected ) {
                        $lAddedPoints = str_replace( ',', '.',
@@ -271,6 +273,7 @@
                        $this->mBeingCorrected,
                        $this->mCaseSensitive,
                        $this->mQuestionId,
+                       $this->shuffleAnswers,
                        $this->mParser
                );
                Hooks::run( 'QuizQuestionCreated', [ $this, &$question ] );
diff --git a/tests/phpunit/QuestionTest.php b/tests/phpunit/QuestionTest.php
index fc6583d..a2e7b4d 100644
--- a/tests/phpunit/QuestionTest.php
+++ b/tests/phpunit/QuestionTest.php
@@ -35,7 +35,9 @@
        }
 
        private function getQuestion( $beingCorrected, $caseSensitive, 
$questionId ) {
-               return new Question( $beingCorrected, $caseSensitive, 
$questionId, $this->parser );
+               // Randomly generate shuffle parameter value
+               $shuffle = rand( 0, 100 ) % 2 == 0 ? 0 : 1;
+               return new Question( $beingCorrected, $caseSensitive, 
$questionId, $shuffle, $this->parser );
        }
 
        private function getRequest() {
@@ -306,4 +308,31 @@
                $this->assertEquals( $output, $expected );
        }
 
+       public function provideCheckRequestOrder() {
+               return [
+                       // Test for correct order
+                       [ '1 0 2 3', 3, 0 ],
+                       // Test for order having repeated values
+                       [ '3 3 2 2', 3, 1 ],
+                       // Test for order having more values than the number of 
proposals
+                       [ '0 1 4 3 5', 3, 1 ],
+                       // Test for order having less values than the number of 
proposals
+                       [ '0 1 3', 3, 1 ],
+                       // Test for order having value more than proposalIndex
+                       [ '0 1 2 4', 3, 1 ],
+                       // Test for order having value less than proposalIndex
+                       [ '0 -1 1 2', 3, 1 ]
+               ];
+       }
+
+       /**
+        * @dataProvider provideCheckRequestOrder
+        * @covers Question::checkRequestOrder
+        */
+       public function testCheckRequestOrder( $order, $proposalIndex, 
$expected ) {
+               $this->question = $this->getQuestion( $beingCorrected = 1, 1, 2 
);
+               $output = $this->question->checkRequestOrder( $order, 
$proposalIndex );
+               $this->assertEquals( $output, $expected );
+       }
+
 }

-- 
To view, visit https://gerrit.wikimedia.org/r/374035
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I7bdeade1fb5359e0db2051a48a2e51831f673ba7
Gerrit-PatchSet: 10
Gerrit-Project: mediawiki/extensions/Quiz
Gerrit-Branch: master
Gerrit-Owner: Harjotsingh <[email protected]>
Gerrit-Reviewer: Harjotsingh <[email protected]>
Gerrit-Reviewer: Mvolz <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to