diff --git a/question/type/regexp/db/mysql.php b/question/type/regexp/db/mysql.php deleted file mode 100644 index bc2af45f7a4..00000000000 --- a/question/type/regexp/db/mysql.php +++ /dev/null @@ -1,24 +0,0 @@ -dirroot/question/type/regexp/db/mysql.sql"); - return $result; - } - - // Question type was installed before. Upgrades must be applied - - if ($oldversion < 2007012800) { - $result = modify_database("$CFG->dirroot/question/type/regexp/db/mysql02.sql"); - return $result; - - } - - return true; -} - -?> diff --git a/question/type/regexp/db/mysql.sql b/question/type/regexp/db/mysql.sql deleted file mode 100644 index 504e9d53832..00000000000 --- a/question/type/regexp/db/mysql.sql +++ /dev/null @@ -1,15 +0,0 @@ - - --- --- Table structure for table `prefix_question_regexp` --- - -CREATE TABLE prefix_question_regexp ( - id int(10) unsigned NOT NULL auto_increment, - question int(10) unsigned NOT NULL default '0', - answers varchar(255) NOT NULL default '', - usehint tinyint(2) NULL default '0', PRIMARY KEY (id), - KEY question (question) -) TYPE=MyISAM COMMENT='Options for regexp questions'; - --- -------------------------------------------------------- diff --git a/question/type/regexp/db/mysql02.sql b/question/type/regexp/db/mysql02.sql deleted file mode 100644 index 2cfdc6d321f..00000000000 --- a/question/type/regexp/db/mysql02.sql +++ /dev/null @@ -1,8 +0,0 @@ - - --- --- Add the usehint field to table `prefix_question_regexp` --- - -ALTER TABLE `prefix_question_regexp` ADD `usehint` TINYINT( 2 ) NULL DEFAULT '0'; --- -------------------------------------------------------- diff --git a/question/type/regexp/db/postgres7.php b/question/type/regexp/db/postgres7.php deleted file mode 100644 index 14a01dd30aa..00000000000 --- a/question/type/regexp/db/postgres7.php +++ /dev/null @@ -1,23 +0,0 @@ -dirroot/question/type/regexp/db/postgres7.sql"); - return $result; - } - - // Question type was installed before. Upgrades must be applied - if ($oldversion < 2007012800) { - $result = modify_database("$CFG->dirroot/question/type/regexp/db/postgres7_02.sql"); - return $result; - - } - return true; -} - -?> diff --git a/question/type/regexp/db/postgres7.sql b/question/type/regexp/db/postgres7.sql deleted file mode 100644 index a0564f9047c..00000000000 --- a/question/type/regexp/db/postgres7.sql +++ /dev/null @@ -1,14 +0,0 @@ - -# -------------------------------------------------------- - -# -# Table structure for table prefix_question_regexp -# - -CREATE TABLE prefix_question_regexp ( - id SERIAL PRIMARY KEY, - question integer NOT NULL default '0', - answers varchar(255) NOT NULL default '', - usehint integer NULL default '0' -); -CREATE INDEX prefix_question_regexp_question_idx ON prefix_question_regexp (question); diff --git a/question/type/regexp/display.html b/question/type/regexp/display.html deleted file mode 100644 index ec59c321be4..00000000000 --- a/question/type/regexp/display.html +++ /dev/null @@ -1,65 +0,0 @@ -
- -
- - - - - -
-
- -
- -
- size="80"/>  - options->usehint && ($cmoptions->optionflags == QUESTION_ADAPTIVE) && !$readonly ) { - $p = get_string("hintpenalty", "qtype_regexp", $question->penalty); - $q = 'resp'.$question->id.'_'; - $q2 = $q.'submit'; - $hint = get_string("hint", "qtype_regexp"); - echo(''); - } - ?> - -
- -
- -
- -
- print_question_submit_buttons($question, $state, $cmoptions, $options); ?> - -
- -
- - - -function showdiv(id,thisbutton) { -if (document.getElementById(id).style.display == 'none') { - document.getElementById(id).style.display = 'block' - thisbutton.value = '".$hide."' - } else { - document.getElementById(id).style.display = 'none' - thisbutton.value = '".$show."' - } - return; -} -function submittheform (myid) { - a = document.forms[0].action; - if (a.indexOf('preview.php')!=-1) { - document.forms[0].name = 'responseform'; - } - a = document.getElementsByName(myid)[0].value; - document.getElementsByName(myid)[0].value = a + '\t'; - this.document.forms.responseform.submit(); -} -"); - ?> \ No newline at end of file diff --git a/question/type/regexp/edit_regexp_form.php b/question/type/regexp/edit_regexp_form.php deleted file mode 100644 index 2ea6504801f..00000000000 --- a/question/type/regexp/edit_regexp_form.php +++ /dev/null @@ -1,102 +0,0 @@ -qtype(); - - $mform->removeElement('generalfeedback'); //JR - $menu = array(get_string('no', 'moodle'), get_string('yes', 'moodle')); - $mform->addElement('select', 'usehint', get_string('usehint', 'qtype_regexp'), $menu); - $mform->setHelpButton('usehint', array('regexphint', get_string('usehint', 'qtype_regexp'), 'qtype_regexp')); - $mform->addElement('static', 'answersinstruct', get_string('correctanswers', 'quiz'), get_string('filloutoneanswer', $langfile)); - $mform->closeHeaderBefore('answersinstruct'); - $creategrades = get_grade_options(); - $gradeoptions = $creategrades->gradeoptions; - $repeated = array(); - $repeated[] =& $mform->createElement('header', 'answerhdr', get_string('answerno', $langfile, '{no}')); - $repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'),array('size'=>100)); //JR - $repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions); - $repeated[] =& $mform->createElement('textarea', 'feedback', get_string('feedback', 'quiz'),array('cols'=>60, 'rows'=>1)); - - if (isset($this->question->options)){ - $countanswers = count($this->question->options->answers); - } else { - $countanswers = 0; - } - $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))? - QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD); - $repeatedoptions = array(); - $mform->setType('answer', PARAM_NOTAGS); - $repeatedoptions['fraction']['default'] = 0; - $this->repeat_elements($repeated, $repeatsatstart, $repeatedoptions, 'noanswers', 'addanswers', QUESTION_NUMANS_ADD, get_string('addmoreanswerblanks', 'qtype_shortanswer')); - - } - - function set_data($question) { - if (isset($question->options)){ - $answers = $question->options->answers; - if (count($answers)) { - $key = 0; - foreach ($answers as $answer){ - $default_values['answer['.$key.']'] = $answer->answer; - $default_values['fraction['.$key.']'] = $answer->fraction; - $default_values['feedback['.$key.']'] = $answer->feedback; - $key++; - } - } - $default_values['image'] = "None"; - $default_values['usehint'] = $question->options->usehint; - $question = (object)((array)$question + $default_values); - } else { - $key = 0; - $default_values['fraction['.$key.']'] = 1; - $question = (object)((array)$question + $default_values); - } - parent::set_data($question); - } - function validation($data){ - $langfile = 'qtype_'.$this->qtype(); - $errors = array(); - $answers = $data['answer']; - $answercount = 0; - foreach ($answers as $answer){ - $trimmedanswer = trim($answer); - if (!empty($trimmedanswer)){ - $parenserror = check_my_parens($trimmedanswer); - if ($parenserror) { - $errors['answer['.$answercount.']'] = $parenserror; - } - $answercount++; - } - } - if ($answercount==0){ - $errors['answer[0]'] = get_string('notenoughanswers', $langfile); - } - if ($data['fraction'][0] != 1) { - $errors['fraction[0]'] = get_string('fractionsnomax', $langfile, 1); - } - return $errors; - } - function qtype() { - return 'regexp'; - } -} -?> \ No newline at end of file diff --git a/question/type/regexp/expandregexp.php b/question/type/regexp/expandregexp.php deleted file mode 100644 index 18f904cb6ef..00000000000 --- a/question/type/regexp/expandregexp.php +++ /dev/null @@ -1,489 +0,0 @@ - escaped ?, treat as literal char (replace with ¬ char temporarily) -// this ¬ char chosen because non-alphanumeric & rarely used... - if ($c0 == '\\') { - $myregexp = substr($myregexp, 0,$c1 -1 ) .'¬' .substr($myregexp ,$c1 + 1); - continue; - } -// if )? -> meta ? action upon parens (), replace with ¤ char temporarily -// this ¤ char chosen because non-alphanumeric & rarely used... - if ($c0 == ')') { - $myregexp = substr( $myregexp, 0, $c1 -1 ) .'¤' .substr($myregexp, $c1 + 1); - continue; - } -// if ? metacharacter acts upon an escaped char, put it in $c2 - if ($myregexp[$c1 - 2] == '\\') { - $c0 = '\\'.$c0; - } - $c2 = '('.$c0.'|)'; - $myregexp = str_replace($c0.'?', $c2, $myregexp); - } - // replaces possible temporary ¬ char with escaped question mark - if (strpos( $myregexp, '¬') != -1) { - $myregexp = str_replace('¬', '\?', $myregexp); - } -// replaces possible temporary ¤ char with escaped question mark - if (strpos( $myregexp, '¤') != -1) { - $myregexp = str_replace('¤', ')?', $myregexp); - } - -// process ? metacharacter acting upon a set of parentheses \(.*?\)\? - $myregexp = str_replace(')?', '|)', $myregexp); - -// replace escaped characters with their escape code - while ($c = strpos($myregexp, '\\')) { - $s1 = substr($myregexp, $c, 2); - $s2 = $myregexp[$c + 1]; - $s2 = rawurlencode($s2); - -// alaphanumeric chars can't be escaped; escape codes useful here are: -// . = %2e ; + = %2b ; * = %2a -// add any others as needed & modify below accordingly - switch ($s2) { - case '.' : $s2 = '%2e'; break; - case '+' : $s2 = '%2b'; break; - case '*' : $s2 = '%2a'; break; - } - $myregexp = str_replace($s1, $s2, $myregexp); - } - -// remove starting and trailing metacharacters; not used for generation but useful for testing regexp - if (strpos($myregexp, '^')) { - $myregexp = substr($myregexp, 1); - } - if (strpos($myregexp, '$') == strlen($myregexp) -1) { - $myregexp = substr( $myregexp, 0, strlen($myregexp) -1); - } - -// process metacharacters not accepted in sentence generation - $illegalchars = array ('+','*','.','{','}'); - $illegalchar = false; - foreach ($illegalchars as $i) { - if (strpos($myregexp, $i)) { - $illegalchar = true; - } - } - if ($illegalchar == true) { - echo ("

SORRY! Cannot generate sentences from a regExp containing one of these metacharacters: ".implode(' ', $illegalchars)."
If you need to use one of them in your regExp as a LITERAL character, you must \'escape\' it.
EXAMPLE: The [bc]at sat on the ma[pt]\\.

Wrong regExp = $myregexp"); - return ('$myregexp'); - } - - $mynewregexp = find_nested_ors($myregexp); // check $myregexp for nested parentheses - if ($mynewregexp != null) { - $myregexp = $mynewregexp; - } - - $result = find_ors($myregexp); // expand parenthesis contents - if ( is_array($result) ) { - $results = implode('\n', $result); - } - return $result; // returns array of alternate strings -} - -function check_my_parens($myregexp) { - $openparen = 0; - $closeparen = 0; - $opensqbrack = 0; - $closesqbrack = 0; - $iserror = false; - $message = ''; - for ($i = 0; $i'; - } - if ($openparen != $closeparen) { - $message .= get_string ('regexperrorparen', 'qtype_regexp').' - '.get_string ('regexperroropen', 'qtype_regexp', $openparen)." # ".get_string ('regexperrorclose', 'qtype_regexp', $closeparen).'
'; - } - if ($opensqbrack != $closesqbrack) { - $message .= get_string ('regexperrorsqbrack', 'qtype_regexp').' - '.get_string ('regexperroropen', 'qtype_regexp', $opensqbrack)." # ".get_string ('regexperrorclose', 'qtype_regexp', $closesqbrack); - } - if ($iserror) { - return $message; - } - return; -} - -// find individual $nestedors expressions in $myregexp -function is_nested_ors ($mystring) {//return false; - $orsstart = 0; $orsend = 0; $isnested = false; $parens = 0; $result = ''; - for ($i = 0; $i < strlen($mystring); $i++) { - switch ($mystring[$i]) { - case '(': - $parens++; - if ($parens == 1) { - $orsstart = $i; - } - if ($parens == 2) { - $isnested = true; - } - break; - case ')': - $parens--; - if ($parens == 0) { - if ($isnested == true) { - $orsend = $i + 1; - $i = strlen($mystring); - break; - } //end if - } //end case - } //end switch - } // end for - if ($isnested == true) { - $result = substr( $mystring, $orsstart, $orsend - $orsstart); - return $result; - } - return false; -} - -// find nested parentheses -function is_parents ($myregexp) { - $finalresult = null; - $pattern = '/[^(|)]*\\(([^(|)]*\\|[^(|)]*)+\\)[^(|)]*/'; - if (preg_match_all($pattern, $myregexp, $matches, PREG_OFFSET_CAPTURE)) { - $matches = $matches[0]; - for ($i=0; $i OR OR etc. -// into a LIST of possible alternatives, -// e.g. -> , , - $expandedors = array(); - $expandedors[] = ''; - $slen = sizeof($expandedors); - $expandedors[$slen-1] = ''; - if ($plainparts[0] == 0) { // if chunk begins with $plainparts - $expandedors[$slen-1] = $plainparts[1]; - array_splice($plainparts, 0,2); - } - while ((sizeof($plainparts) !=0) || (sizeof($plainors) !=0)) { // go through sentence $plainparts - $l = sizeof($expandedors); - for ($k=0; $k<$l; $k++) { - for ($m=0; $m < sizeof($plainors[1]); $m++) { - $expandedors[] = ''; - $slen = sizeof($expandedors) -1; - $expandedors[$slen] = $expandedors[0].$plainors[1][$m]; - if (sizeof($plainparts)) { - if ($plainparts[1]) { - $expandedors[$slen] .=$plainparts[1]; - } - } - $expandedors[$slen] = rawurldecode($expandedors[$slen]); - } - array_splice($expandedors, 0, 1);// remove current "model" sentence from Sentences - } - array_splice($plainors,0,2); // remove current $plainors - array_splice($plainparts,0,2); // remove current $plainparts - - } -// eliminate all extra µ signs which have been placed to replace consecutive parentheses by )µ( - $n = count ($expandedors); - for ($i = 0; $i < $n; $i++) { - if (is_int(strpos($expandedors[$i], 'µ') ) ) { //corrects strpos for 1st char of a string found! - $expandedors[$i] = str_replace('µ', '', $expandedors[$i]); - } - } - return ($expandedors); -} - -function index_plain_parts($mystring, $plainparts) { - $indexedplainparts = array(); - if (is_array($plainparts) ) { - foreach($plainparts as $parts) { - if ($parts) { - $index = strpos($mystring, $parts) ; - $indexedplainparts[] = $index; - $indexedplainparts[] = $parts; - } - } - } - return ($indexedplainparts); -} - -function index_ors($mystring, $plainors) { - $indexedplainors = array(); - foreach ($plainors as $ors) { - foreach ($ors as $or) { - $indexedplainors[] = $or[1]; - $o = substr($or[0], 1, strlen($or[0]) -2); - $o = explode('|', $o); - $indexedplainors[] = $o; - } - } - return ($indexedplainors); -} - -// functions adapted from Hot Potatoes -function check_beginning( $guess, $answer, $ignorecase){ - if (substr($answer,0,8) == ''){ // this answer is in fact the regexp itself, do not process it - return; - } - $guess = utf8_decode($guess); - $answer = utf8_decode($answer); - $outstring = ''; - if ($ignorecase) { - $guessoriginal = $guess; - $guess = strtoupper($guess); - $answer = strtoupper($answer); - } - for ($i=0; ( $i $longest) { - $longest = strlen($g); - $closesta = $g; - if ($ishint) { - $a = utf8_decode($a); // for accents etc. - $c = substr($a, $longest, 1); - $closesta .= substr($a,$longest, 1); - if (substr($a,$longest, 1) == ' ') { // if hint letter is a space, add next one - $closesta .= substr($a,$longest + 1, 1); - } - if ($a == $closesta) { - $closest[1] = true; - } - - } - } - } - } - $closest[0] = utf8_encode($closesta); - return $closest; -} -// end functions adapted from Hot Potatoes -?> \ No newline at end of file diff --git a/question/type/regexp/icon.gif b/question/type/regexp/icon.gif deleted file mode 100644 index dc9daaf3853..00000000000 Binary files a/question/type/regexp/icon.gif and /dev/null differ diff --git a/question/type/regexp/lang/en_utf8/help/regexp/regexp.html b/question/type/regexp/lang/en_utf8/help/regexp/regexp.html deleted file mode 100644 index 6a72479f754..00000000000 --- a/question/type/regexp/lang/en_utf8/help/regexp/regexp.html +++ /dev/null @@ -1,102 +0,0 @@ -

RegExp Short Answer Questions

-

Like the Short Answer question, the RegExp Short Answer question expects the respondent to answer an "open" question with a word or a short phrase. However, the RegExp system system gives you access to a more powerful system for - analysing the student's answers. For a complete introduction to Regular Expressions, - see these sites regular-expressions -tutorial or rezeau.org.

- -

Correct answer matching a regular expression pattern

-

It is not possible to give complete examples of the vast possibilities offered - by this system, and the following are just some possibilities.

-

Example 1. Suppose your question is "What are the colors - of the French flag?". In the Answer 1 box you would type the "best" answer, e.g. "it's blue, white and red". For more details, see First correct answer below.

-

In the Answer 2 box you would type this regular - expression: "it’s blue, white(,| and) - red/i" (quotes should not be typed, of course) . This will match any of those 4 responses:

- -

Please note that by default a regular expression match is case sensitive; to - make the match case insensitive you must add the /i parameter - right at the end of your expression.

-

Example 2. Question: "What is blue, or red, or yellow?". - Answer: "(|it's )a colou?r". This answer will match:

- -

Notes.- The beginning of this regular expression "(|it's )" will - match either nothing or "it's " (i.e. "it's" followed by - a space). The ? (question-mark) means: preceding character zero or one time; - it is used here to match British English as well as US spelling.

-

Example 3. Question: "Name an animal whose name is made - of 3 letters and the middle letter is the vowel a". Anwer: - "[bcr]at". This will match: bat, cat and rat.

-

Detecting missing required words or character strings

-

Regular expressions alone cannot detect absent character strings, so you have to - add a little code in your Answer to take care of this. Any Teacher Answer which - begins with a double hyphen will analyse the student’s response to find out - whether the following string is present or absent. If present, the analysis - continues to the next question; if absent, the analysis stops and the relevant - Feedback message is displayed.

-

Example 4.

- -

Here, the . (dot) stands for “any character” and the * (asterisk) - means “preceding special character repeated any number of times”. - The Answer2 regular expression above means: check whether the character string - "blue", preceded with anything and followed by anything is absent - from the student's answer. Please note that the use of the asterisk is different - in the normal Short Answer question type and in the RegExp question type.

-

Example 5. Question: "Name an animal whose name is made of - 3 letters and the middle letter is the vowel a". Teacher Answer: "--[b|c|r]". Feedback: "Your answer should start with one of these letters: b, c or r"

-

Escaping special characters

-

If you need to use (in your Answers) characters which are part of the regular expressions set - of special characters (or meta characters), you need to "escape" them (i.e. precede - them with a backslash). E.g. if you want to accept the answer "My computer - cost 1000$", you must write the regular expression as "My computer cost - 1000\$". But please see First correct answer below. The special characters which must be escaped are the following .^$*()[]+?|{}

-

Entering Answers in Edit Question mode (teacher)

-

[changes starting from February 2007 version of regexp question type]

-

The Editing regular expression question interface screen has been slightly modified.

-

Display Hint Button

-

It is now possible for the teacher to tick a Display Hint Button box in order to allow the student to ask for a hint. This hint will add the next correct letter to the student's input response field and - if the penalty scheme has been enabled in the Quiz settings - will cost the student one penalty factor. When the student takes the quiz, mousing over the Hint button will display how much it will cost to "buy" a letter. By default Display Hint button value is set at No. Questions created prior to Feb. 2007 will have this No value. If you want to display the Hint button you'll have to edit those questions and select Yes and save. Please note that the Hint button will only be available to quizzes which have their Adaptive mode set to Yes, as it does not make sense to enable the Hint button for non-adaptive tests.

-

Each time a student clicks the Hint button to buy a letter, an invisible paragraph character ( ¶ ) is added to his response. When the teacher views the quiz results, that paragraph character is visible so the teacher knows that the Hint button was clicked for that response.

-

First correct answer

-

For a number of reasons, the way of entering the very first Answer (Answer 1) in your list of answers has been changed. For Answer 1 you must now enter an answer text which a) is the "best" possible answer; b) is not a regular expression or - more exactly - will not be interpreted as a regular expression but "as is" and c) has a Grade value of 100%. This means that you may have to edit regexp questions created prior to Feb. 2007 if they do not obey these rules. You will notice that when you create a new regexp question the Grade value for Answer 1 is already automatically set at 100%. You must not change this. If, however, you did, a warning message would be issued when you save your question, allowing you the opportunity to go back and set things right. In Moodle 1.8+ you won't be able to save your question if you have accidentally changed the 100% grade for Answer 1 for another value (this should not happen).

-

Note.- There are two ways to enter an answer containing meta characters, according to whether this is Answer 1 or any of the subsequent Answers. Exemple question: how much did your computer cost?

-

Answer 1: It cost $1,000.

-

Answer 2: It cost ( me)?\$1,000\.

-

In Anwer 1 you just type the expected answer "as is". The text in Answer 2 will be interpreted as a regular expression, and thus you need to escape the two meta characters (the $ sign and the end-of-sentence full stop). Note that here I have added the optional pronoun "me".

-

Other answers

-

Any answers with a Grade higher than 0% must be entered as valid regular expressions which can yield acceptable alternative answers (regardless of the Grade being less than 100%).

-

This means that some regular expressions, which are perfectly valid and would correctly analyse the student's (correct) answer are not recommended. The only case where they would work is a) if your question's Display Hint Button is set at No and b) your quiz Adaptative Mode is set at No. This means that you must not enter as an answer with a grade higher than 0% a regular expression beginning with a double hyphen "--", used for detecting missing character strings.

-

Previewing questions in popup window (teacher only)

-

[changes starting from February 2007 version of regexp question type]

-

When the teacher previews a question in the popup preview question window it is now possible to display all of the acceptable alternative answers. Those alternative answers are automatically generated from the regular expressions you have entered when creating the question which carry a grade higher than 0%. The very first acceptable answer is printed as is at the top of the list. This is followed by all the other alternative acceptable answers, consisting of a) the Grade attributed; b) a reminder of the regular expression you entered and c) a list of all alternative answers.

-

The only check done by the programme for the validity of your regular expressions consists in checking that opened parentheses and square brackets are correctly closed. If your parentheses and square brackets are not correctly closed, a warning message will be displayed showing the number of each. You should immediately edit the faulty question.

-

However this does not totally protect you from other errors. If you made an error in one of your regular expressions, either of the following can happen: a) a PHP error notice or warning will be displayed, something like [Fatal error: Maximum execution time of 30 seconds exceeded etc. ] or b) no error will be displayed but you will see by yourself that the alternative answers generated from your faulty regexp are in fact not acceptable. You should of course immediately edit the problem question, trying to find and correct the faulty regular expression. Hopefully a more robust and user-friendly checking mechanism will be available soon.

-

It is highly recommended to use the preview popup window systematically immediately after having created or edited a regexp type question, and to click on the Show alternate answers button in order to check the validity of one's regular expressions.

-

Inserting regexp sub-questions in Cloze type questions

-

The syntax for inserting regexp sub-questions in Cloze type questions is as shown in the following examples.

- -

Please note that, as explained above, the very first answer must be Graded 100% (in Cloze type question syntax, all correct is either = or 100%) and it must not be a regular expression. If you forget to give a 100% grade to the very first answer, a warning message will be displayed upon saving the question: "Answer 1 must be correct and its grade must be 100%. Do you want to go back and correct this?". However, if you see this message, you must not click the Yes button, because it would not take you back to your Cloze type editing window. You must click No and immediately edit the faulty Cloze question and correct the problem.

-

Please note that the syntax of the sub-questions inside a Cloze-type question must be followed exactly and that you must never ever copy and paste any question text from e.g. a word-processor into the Cloze-type question editing window. Quite often Cloze-type questions yield errors because extraneous blank spaces, new lines, or any odd formatting character has made its way into the question text.

-

Unfortunately the Hint button will not be available to the student for a regexp question inserted into a Cloze-type question. Maybe this will be available later on, when I'm retired and have more time on my hands.

-
-

Updated by JR 28-Mar-2007

diff --git a/question/type/regexp/lang/en_utf8/help/regexp/regexphint.html b/question/type/regexp/lang/en_utf8/help/regexp/regexphint.html deleted file mode 100644 index a8825618ef7..00000000000 --- a/question/type/regexp/lang/en_utf8/help/regexp/regexphint.html +++ /dev/null @@ -1,3 +0,0 @@ -

Hint button

- -

Selecting Display Hint Button -> Yes will display a Hint button in order to allow the student to ask for a letter. This letter will be added to the student's actual response, possibly after extraneous wrong letters have been removed. If the penalty scheme has been enabled in the Quiz settings this hint will cost the student one penalty factor. When the student takes the quiz, moving the cursor over the Hint button will display a tooltip saying how much it will cost to "buy" a letter. By default Display Hint button value is set at No. Questions created prior to Feb. 2007 will have this value set at No. If you want to display the Hint button you'll have to edit those questions and select Yes and save. Please note that the Hint button will only be available to quizzes which have their Adaptive mode set to Yes, as it does not make sense to enable the Hint button for non-adaptive tests.

diff --git a/question/type/regexp/lang/en_utf8/qtype_regexp.php b/question/type/regexp/lang/en_utf8/qtype_regexp.php deleted file mode 100644 index 367005dc181..00000000000 --- a/question/type/regexp/lang/en_utf8/qtype_regexp.php +++ /dev/null @@ -1,20 +0,0 @@ -Answer 1 must be a correct answer (grade = 100%%) and it must not be a regular expression.'; -$string['fractionsnomax'] = 'Answer $a must be correct and its grade must be 100%%.'; -$string['gradingdetailspenalty'] = 'Your submission(s) attracted a total penalty of $a. '; -$string['hidealternate'] = 'Hide alternate answers'; -$string['hint'] = 'Hint'; -$string['hintpenalty'] = 'Hint cost for this question: $a penatly points'; -$string['notenoughanswers'] = 'This type of question requires at least one answer'; -$string['regexp'] = 'Regular expression short answer'; -$string['regexperror'] = 'Error in regular expression:$a'; -$string['regexperrorclose'] = 'close: $a'; -$string['regexperroropen'] = 'open: $a'; -$string['regexperrorparen'] = 'Parentheses'; -$string['regexperrorsqbrack'] = 'Square brackets'; -$string['showalternate'] = 'Show alternate answers'; -$string['usehint'] = 'Display Hint button'; -?> \ No newline at end of file diff --git a/question/type/regexp/lang/fr_utf8/help/regexp/regexp.html b/question/type/regexp/lang/fr_utf8/help/regexp/regexp.html deleted file mode 100644 index 1bfec01632d..00000000000 --- a/question/type/regexp/lang/fr_utf8/help/regexp/regexp.html +++ /dev/null @@ -1,67 +0,0 @@ -

Questions à réponse courte avec expressions régulières

-

Tout comme la question à réponse courte, la question à réponse courte de type "expression régulière" attend de la part de l'élève une réponse à une question "ouverte", consistant en un ou plusieurs mots ou une courte phrase. Cependant, l'utilisation des expressions régulières vous donne accès à un puissant système d'analyse de la réponse des élèves. Pour une introduction au système des expressions régulières, voir ces sites : regular-expressions -tutorial et rezeau.org.

-

Réponse correcte correspondant à une expression régulière

-

Il est impossible de fournir des exemples complets des vastes possibilités offertes par ce système, mais voici quelques possibilités.

-

Exemple 1. Supposons que votre question soit "Donnes les couleurs du drapeau français". Dans le champ Solution 1 vous pourriez saisir cette expression régulière : "(il est |)bleu, blanc(,| et) rouge/i" (il ne faut pas taper les guillements, bien sûr ). Cette expression acceptera n'importe laquelle des réponses suivantes:

- -

Notez que par défaut une expression régulière est sensible à la casse (minuscules/majuscules). Pour rendre l'expression insensible à la casse, vous ajouterez le paramètre /i tout à la fin de l'expression.

-

Example 2. (en anglais) Question : "What is blue, or red, or yellow?". - Réponse : "(|it's )a colou?r". Cette expression acceptera n'importe laquelle des réponses suivantes :

- -

Note.- Le début de cette expression régulière "(|it's )" acceptera comme réponse soit rien soit "it's " (attention : it's suivi d'une espace). Le point d'interrogation signifie : le caractère qui précède, employé zéro ou une fois ; nous l'utilisons ici pour accepter aussi bien l'orthographe anglaise (colour) qu'américaine (color).

-

Exemple 3. (en anglais encore) Question: "Donnez en anglais le nom d'un animal qui fait 3 lettres et dont la lettreu du milieu ets la voyelle a". Réponse : - "[bcr]at". Ce qui acceptera les 3 réponses : bat, cat et rat.

-

Détection des mots ou chaînes de caractères manquants

-

Les expressions régulières seules ne permettent pas de détecter des mots ou groupes de lettres absents de la réponse d'un élève, il faudra donc ajouter un petit code pour y arriver. Toute réponse prévue par le professeur qui commencera par un double tiret analysera la réponse de l'élève pour voir si la chaîne de caractères qui suit est présente ou absente. Si cette chaîne est présente, l'analyse continuera jusqu'à la réponse prévue suivante ; si la chaîne est absente, l'analyse s'arrêtera et le mesage de feedback prévu s'affichera.

-

Exemple 4. Question sur les couleurs du drapeau français.

- -

Ici, le point signifie "n'importe quel caractère, et l'astérisque signifie "le caractère qui précède, répété un nombre quelconque de fois". La réponse prévue n° 2 ci-dessus signifie : vérifier si la chaîne de caractères "bleu", précédée de n'importe quoi et suivie de n'importe quoi est absente de la réponse de l'élève. Notez que l'usage de l'astérisque est légèrement différent dans les questions de type Réponse Courte et dans les questions de type Réponse Courte avec expressions régulières.

-

Exemple 5. Question: "Donnez en anglais le nom d'un animal qui fait 3 lettres et dont la lettreu du milieu ets la voyelle a". Réponse prévue : "--[b|c|r]". Feedback: "Votre réponse devrait commencer par l'une de ces 3 lettres : b, c ou r".

-

Échapper les caractères spéciaux

-

Si vous avez besoin dans vos réponses prévues de caractères qui font partie de la liste des caractères spéciaux utilisés dans la syntaxe des expressions régulières, vous devrez les "échapper", c-a-d les faire précéder d'une barre oblique inverse. Par exemple si vous voulez accepter la réponse "Cet ordinateur vaut 1 000$", vous écrirez ainsi l'expression régulière: "Cet ordinateur vaut 1 000\$". Voici la liste complète des caractères spéciaux qui doivent être échappés : ^ $ * ( ) [ ] + ? | { }

-

Edition des Réponses en mode professeur

-

[modifications à dater de février 2007]

-

L'écran d'édition des questions de type regexp a été légèrement modifié.

-

Affichage du bouton Indice

-

Il est désormais possible d'afficher un bouton Indice pour permettre à l'étudiant d'acheter une lettre. Le bouton Indice ne sera disponible pour l'étudiant que si dans le Test le paramètre Mode adaptatif a été réglé sur Oui. Si le paramètre Appliquer les pénalités a été activé dans les réglages du test, une pénalité correspondant au Facteur de pénalité sera appliquée à chaque "achat" de lettre par l'étudiant. Lorsque l'étudiant fait le test et qu'un bouton Indice est présent, en passant la souris sur ce bouton il verra s'afficher la pénalité que lui coûtera l'achat d'une lettre.

-

Chaque fois que l'étudiant clique sur le bouton Indice pour acheter une lettre, un caractère paragraphe invisible ( ¶ ) est ajouté au texte de sa réponse. Plus tard, lorsque le professeur visualise les résultats du test pour chaque étudiant, il peut voir ce caractère paragraphe à la fin de chaque réponse pour laquelle l'étudiant a demandé de l'aide.

-

Solution 1

-

Pour un certain nombre de raisons, la façon d'entrer les solutions dans Solution 1 a changé depuis la version de février 2007. Pour la Solution 1 vous devez maintenant entrer un texte qui a) est la "meilleure" réponse attendue possible; b) n'est pas une expression régulière ou - plus exactement - ne sera pas interprété comme telle mais comme du texte normal et c) à laquelle vous affectez une Note de 100%. Ceci veut dire que si vous avez créé des questions de type regexp avant Février 2007 vous devrez les modifier une par une pour vérifier qu'elles répondent bien à ces nouvelles règles. Vous remarquerez que lors de la création d'une nouvelle question de type regexp, la Note attribuée à la Solution 1 est automatiquement mise sur 100%. Vous ne devez pas modifier ceci. Si par hasard vous le faisiez, un message d'erreur vous le signalerait lors de l'enregistrement de votre question, et vous donnerait la possibilité de faire marche arrière pour corriger l'erreur. Message: "La Solution 1 doit être correcte et avoir un score de 100%. Voulez-vous retourner modifier ceci ?"

-

Note.- Il y a maintenant deux manières d'entrer une solution contenant des méta caractères (^ $ * ( ) [ ] + ? | { }), selon qu'il s'agit de la Solution 1 ou de l'une des solutions suivantes. Exemple: combien coûte cet ordinateur américain?

-

Solution 1: Il vaut 1 000$.

-

Solution 2: Il (m')?a couté 1 000\$\.

-

Dans Solution 1 vous tapez la réponse attendue telle quelle. Dans la Solution 2 en revanche vous devez "échapper" les méta-caractères $ et point final.

-

Autres Solutions (Solution 2, 3, etc.)

-

Toute solution avec une Note supérieure à 0% doit être entrée sous forme d'une expression régulière qui peut générer des réponses alternatives acceptables (que la note soit de 100% ou inférieure). Ceci veut dire que vous ne pouvez pas entrer une solution ayant une Note supérieure à 0% et commençant par un double tiret --, syntaxe utilisée pour détecter l'absence de mots, comme expliqué plus haut.

-

Prévisualisation des questions dans une fenêtre popup (pour le professeur seulement)

-

[modifications à dater de février 2007]

-

Lorsque le professeur prévisualise une question dans la fenêtre popup il peut désormais également voir la liste complète des solutions alternatives acceptables. Ces solutions alternatives sont automatiquement générées à partir des expressions régulières que vous avez entrées en créant la question et auxquelles vous avez attribué une note supérieure à 0%. La toute première solution (Solution 1) est affichée en tête de liste. Elle est suivie des autres solutions acceptables, sous la forme suivante: a) la note attribuée; b) un rappel de votre expression régulière et c) la liste des solutions générées par cette expression régulière.

-

Actuellement le seul mécanisme de vérification de la validité des expressions régulières entrées par le professeur consiste à vérifier que les parenthèses ouvertes sont bien toutes refermées (ainsi que les crochets carrés, si vous les utilisez). Si le nombre de parenthèses (ou de crochets carrés) ouvertes ne correspond pas au nombre de parenthèses fermées, un message d'erreur vous en avertira, qui précisera l'expression régulière où l'erreur a été détectée ainsi que la nature de l'erreur (mais pas l'emplacement exact). Vous devrez immédiatement corriger l'expression régulière fautive dans la question en cours de prévisualisation.

-

Toute autre erreur dans une expression régulière déclenchera l'un ou l'autre des comportements suivants lors de l'ouverture de la fenêtre popup de prévisualisation : a) une erreur de PHP du genre [Fatal error: Maximum execution time of 30 seconds exceeded etc. ] ou bien b) pas de message d'erreur mais, en cliquant sur le bouton Afficher les réponses alternatives, vous pourrez constater que certaines des solutions générées par l'expression régulière fautive sont en fait inacceptables. Il vous faudra alors immédiatement revenir en mode édition pour essayer de repérer l'expression régulière fautive et la corriger. J'espère pouvoir bientôt proposer un système de détection des erreurs plus convivial...

-

Insertion de sous-questions de type regexp dans une question de type Close

-

Voici 2 exemples de syntaxe à utiliser pour ce type d'insertion:

- -

Veuiller noter que, comme expliqué ci-dessus, la toute première solution doit avoir une note de 100% et ne doit pas être une expression régulière. Dans la syntaxe du type de question Cloze, une note de 100% s'écrit soit = soit %100%. Comme indiqué plus haut, si vous oubliez par hasard d'attribuer la note de 100% à la toute première solution de votre liste, au moment d'enregistrer votre question Cloze un message d'erreur vous en avertira. Toutefois, si vous voyez le message "La Solution 1 doit être correcte et avoir un score de 100%. Voulez-vous retourner modifier ceci ?" vous ne devez pas cliquer sur le bouton Oui, mais sur le bouton Non, puis retourner dans votre question Cloze et faire la correction nécessaire.

-

Veuillez noter que la syntaxe des sous-questions insérées dans une question de type Cloze doit être suivie à la lettre et qu'il ne faut jamais copier-coller du texte depuis votre traitement de texte vers le texte d'une question de type Cloze sous peine d'en empêcher le bon fonctionnement.

-

Malheureusement le bouton Indice n'est pas disponible pour une sous-question de type regexp insérée dans une question de type Cloze. Je ferai peut-être ça à la retraite... Un peu de patience!

- diff --git a/question/type/regexp/lang/fr_utf8/help/regexp/regexphint.html b/question/type/regexp/lang/fr_utf8/help/regexp/regexphint.html deleted file mode 100644 index 4834e90630f..00000000000 --- a/question/type/regexp/lang/fr_utf8/help/regexp/regexphint.html +++ /dev/null @@ -1,4 +0,0 @@ -

Le bouton Indice

- -

Si Afficher le bouton Indice est sélectionné (Oui), un bouton Indice sera affiché au moment du Test pour permettre à l'étudiant d'acheter une lettre. Cette lettre sera ajoutée à la réponse de l'étudiant, après que d'éventuelles lettres superfétatoires et incorrectes aient été effacées. Si le paramètre Appliquer les pénalités a été réglé à Oui dans les paramètres généraux du Test, l'achat de cette lettre coûtera à l'étudiant la valeur indiquée dans la question à Facteur de pénalité. Lorsque l'étudiant fera le test, au survol du bouton Indice par la souris s'affichera une info-bulle indiquant le montant de la pénalité appliquée (coût de la lettre achetée). Par défaut l'affichage du bouton Indice est mis à Non. Pour les questions de type REGEXP créées avant la version REGEXP de février 2007, l'affichage du bouton Indice est également mis à Non par défaut. Si vous voulez le faire afficher il vous faudra ré-éditer vos questions.

-

Enfin, notez que le bouton Indice s'affichera seulement dans les tests où le paramètre Mode adaptatif a été mis sur Oui.

diff --git a/question/type/regexp/lang/fr_utf8/qtype_regexp.php b/question/type/regexp/lang/fr_utf8/qtype_regexp.php deleted file mode 100644 index 5c60d1bf41e..00000000000 --- a/question/type/regexp/lang/fr_utf8/qtype_regexp.php +++ /dev/null @@ -1,20 +0,0 @@ -Solution 1 doit être correcte (Note = 100%%) et ne doit pas être une expression régulière.'; -$string['fractionsnomax'] = 'La Solution 1 doit être correcte et avoir un score de 100%%. Voulez-vous retourner modifier ceci ?'; -$string['gradingdetailspenalty'] = 'Vous avez actuellement un total
de $a points de pénalité. '; -$string['hidealternate'] = 'Cacher les réponses alternatives'; -$string['hint'] = 'Indice'; -$string['hintpenalty'] = 'Coût de l\'indice pour cette question : $a points'; -$string['notenoughanswers'] = 'Ce type de question nécessite au moins une réponse!'; -$string['regexp'] = 'Réponse courte de type expression régulière'; -$string['regexperror'] = 'Erreur dans votre expression régulière : $a'; -$string['regexperrorclose'] = 'fermant(e)s: $a'; -$string['regexperroropen'] = 'ouvrant(e)s: $a'; -$string['regexperrorparen'] = 'Parenthèses'; -$string['regexperrorsqbrack'] = 'Crochets carrés'; -$string['showalternate'] = 'Afficher les réponses alternatives'; -$string['usehint'] = 'Afficher le bouton Indice'; -?> \ No newline at end of file diff --git a/question/type/regexp/questiontype.php b/question/type/regexp/questiontype.php deleted file mode 100644 index ca9eed53802..00000000000 --- a/question/type/regexp/questiontype.php +++ /dev/null @@ -1,649 +0,0 @@ -options = get_record('question_regexp', 'question', $question->id)) { - notify('Error: Missing question options!'); - return false; - } - - if (!$question->options->answers = get_records('question_answers', 'question', - $question->id, 'id ASC')) { - notify('Error: Missing question answers!'); - return false; - } - return true; - } - - function save_question_options($question) { - if (!$oldanswers = get_records("question_answers", "question", $question->id, "id ASC")) { - $oldanswers = array(); - } - - $answers = array(); - $maxfraction = -1; - $i = 0; - - // Insert all the new answers - foreach ($question->answer as $key => $dataanswer) { - $i++; - $result = ''; - $answer = ''; - if ($dataanswer != "") { - if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it - $answer = $oldanswer; - $answer->answer = trim($dataanswer); - $answer->fraction = $question->fraction[$key]; - $answer->feedback = $question->feedback[$key]; - if (!update_record("question_answers", $answer)) { - $result->error = "Could not update quiz answer! (id=$answer->id)"; - return $result; - } - } else { // This is a completely new answer - unset($answer); - $answer->answer = trim($dataanswer); - $answer->question = $question->id; - $answer->fraction = $question->fraction[$key]; - $answer->feedback = $question->feedback[$key]; - if (!$answer->id = insert_record("question_answers", $answer)) { - $result->error = "Could not insert quiz answer!"; - return $result; - } - } - $answers[] = $answer->id; - if ( ($i == 1) && ($question->fraction[$key] == 1) ) { - $maxfraction = 1; - } - } - } - if (!$question->usehint) { // for instance the regexp has been authored as part of a CLOZE question, no usehint value has been provided - $question->usehint = 0; - } - - if ($options = get_record("question_regexp", "question", $question->id)) { - $options->answers = implode(",",$answers); - $options->usehint = $question->usehint; - if (!update_record("question_regexp", $options)) { - $result->error = "Could not update quiz regexp options! (id=$options->id)"; - return $result; - } - } else { - unset($options); - $options->question = $question->id; - $options->answers = implode(",",$answers); - $options->usehint = $question->usehint; - if (!insert_record("question_regexp", $options)) { - $result->error = "Could not insert quiz regexp options!"; - return $result; - } - } - - // delete old answer records - if (!empty($oldanswers)) { - foreach($oldanswers as $oa) { - delete_records('question_answers', 'id', $oa->id); - } - } - - /// Perform sanity checks on fractional grades - if ($maxfraction != 1) { - $maxfraction = $maxfraction * 100; - $result->noticeyesno = get_string("fractionsnomax", "qtype_regexp", $maxfraction); - return $result; - } else { - return true; - } - } - - /** - * Deletes question from the question-type specific tables - * - * @return boolean Success/Failure - * @param object $question The question being deleted - */ - function delete_question($questionid) { - delete_records("question_regexp", "question", $questionid); - return true; - } - function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) { - global $CFG; - global $firstcorrectanswer; - global $closestcomplete; - $firstcorrectanswer =''; - $formatoptions = ''; - // get *first* correct answer for this question - // rewrite this function if more answers are needed - $correctanswers = $this->get_correct_responses($question, $state); - $readonly = empty($options->readonly) ? '' : 'readonly="readonly"'; - $formatoptions->noclean = true; - $formatoptions->para = false; - $nameprefix = $question->name_prefix; - $isadaptive = ($cmoptions->optionflags & QUESTION_ADAPTIVE); - $ispreview = ($state->attempt == 0); // workaround to detect if question is displayed to teacher in preview popup window - /// Print question text and media - - $questiontext = format_text($question->questiontext, - $question->questiontextformat, - $formatoptions, $cmoptions->course); - $image = get_question_image($question, $cmoptions->course); - /// Print input controls - if (isset($state->responses[''])) { - $r = $this->remove_blanks($state->responses['']); // $r = original full student response - if ($closest = $this->find_closest(&$question, &$state, &$teststate, $isadaptive, $ispreview) ) { - $response = $closest; - } else { - $response = $r; - } - $value = ' value="'.s($response, true).'" '; - } else { - $value = ' value="" '; - } - $inputname = ' name="'.$nameprefix.'" '; - $r = stripslashes($r); - $f = ''; // student's response with corrections to be displayed in feedback div - $s = ''; // wrong part of student's response to be displayed in red crossed-out in feedback div - $ishint = false; - $l = strlen($r); - if (substr($r, $l-2) == '¶' ) { // hint asked for; remove paragraph mark from student's response - $r = substr($r, 0, $l - 2); - $ishint = true; - } - - if ( ($ispreview || $isadaptive) && $r ) { - - $s = $this->utf8_substr ($r, strlen (utf8_decode($response) ), 99); - if ($ishint and $s) { - $s = $this->utf8_substr ($r, strlen (utf8_decode($response) ) -1, 99); - $response = $this->utf8_substr($response, 0, strlen (utf8_decode($response)) - 1); - } - if ($closest) { - $f = ''.$response.''.$s."
"; - } else { - $f = ''.stripslashes($response)."
"; - } - } - $feedback = ''; - - if ($options->feedback) { - if ($closestcomplete) { // hint has added to response one letter which makes response match one correct answer: submission is correct! - // we must tell $state that everything is OK - $state->responses[''] = $closest; - $state->last_graded->responses[''] = $closest; - $state->last_graded->grade = $question->maxgrade - $state->last_graded->sumpenalty; - $state->raw_grade = $question->maxgrade; - $state->last_graded->raw_grade = $question->maxgrade; - } - - foreach($question->options->answers as $answer) { - if($this->test_response($question, $state, $answer)) { - if ($answer->feedback) { - $feedback = format_text($answer->feedback, true, $formatoptions, $cmoptions->course); - } - break; - } - } - } - $feedback = $f .$feedback; - $correctanswer = ''; - if ($options->readonly && $options->correct_responses) { - $delimiter = ''; - if ($correctanswers) { - foreach ($correctanswers as $ca) { - $correctanswer .= $delimiter.$ca; - $delimiter = ', '; - } - } - } - $correctanswer = stripslashes($correctanswer); - include("$CFG->dirroot/question/type/regexp/display.html"); - } - -// remove extra blank spaces from student's response - function remove_blanks($text) { - $pattern = "/ /"; // finds 2 successive spaces (note: \s does not work with French 'à' character! - while($w = preg_match($pattern, $text, $matches, PREG_OFFSET_CAPTURE) ) { - $text = substr($text, 0, $matches[0][1]) .substr($text ,$matches[0][1] + 1); - } - return $text; - } - - function utf8_substr($str, $from, $len){ - # utf8 substr - # www.yeap.lv - return preg_replace('#^(?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,'.$from.'}'. - '((?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,'.$len.'}).*#s', - '$1',$str); - } - - // ULPGC ecastro - // this function is used by the Item analysis module! - function check_response(&$question, &$state) { - //JR uncomment return false if you want real student answers displayed - //return false; - $answers = &$question->options->answers; - $testedstate = clone($state); - $teststate = clone($state); - $i = 0; - foreach($answers as $aid => $answer) { - $teststate->responses[''] = $answer->answer; - if($this->compare_responses($question, $testedstate, $teststate) ) { - return $aid; - } - } - return false; - } - - function grade_responses(&$question, &$state, $cmoptions) { - $teststate = clone($state); - $state->raw_grade = 0; - // Compare the response with every teacher answer in turn - // and return the first one that matches. - foreach($question->options->answers as $answer) { - // Now we use a bit of a hack: we put the answer into the response - // of a teststate so that we can use the function compare_responses() - $teststate->responses[''] = trim($answer->answer); - if($this->compare_responses($question, $state, $teststate)) { - $state->raw_grade = $answer->fraction; - break; - } - } - - // Make sure we don't assign negative or too high marks - $state->raw_grade = min(max((float) $state->raw_grade, - 0.0), 1.0) * $question->maxgrade; - $state->penalty = $question->penalty * $question->maxgrade; - // mark the state as graded - $state->event = ($state->event == QUESTION_EVENTCLOSE) ? QUESTION_EVENTCLOSEANDGRADE : QUESTION_EVENTGRADE; - return true; - } - - - function compare_responses(&$question, &$state, &$teststate) { - global $firstcorrectanswer; - if (isset($state->responses[''])) { - $response0 = $this->remove_blanks( stripslashes( $state->responses[''] ) ); - } else { - $response0 = ''; - } - if (!$response0) { - return false; - } - if (!$firstcorrectanswer) { - foreach ($question->options->answers as $answer) { - $firstcorrectanswer = $answer->answer; - break; - } - } - $r = $this->remove_blanks($state->responses['']); // $r = original full student response - if ($r && $question->options->usehint) { - $c = $r[strlen($r)-1]; - $d = ord($c); - if ($d == 9) { // hint button added \t char (code 9) at end of student response - $r = substr($r,0,strlen($r)-1) .'¶'; - $state->responses[''] = $r; - } - } - if (isset($teststate->responses[''])) { - $response1 = trim($teststate->responses['']); - } else { - $response1 = ''; - } -// testing for presence of final /i code meaning ignore case - $ignorecase = ""; - if ( substr($response1,strlen($response1) - 2, 2) == '/i') { - $response1 = substr($response1,0,strlen($response1) - 2); - $ignorecase = 'i'; - } -// testing for presence of (right or wrong) elements in student's answer - if ($response1 == $firstcorrectanswer) { // we must escape potential metacharacters in $firstcorrectanswer - $response1 = quotemeta($teststate->responses['']); - } - if ( (preg_match('/^'.$response1.'$/'.$ignorecase, $response0)) ) { - return true; - } -// testing for absence of needed (right) elements in student's answer, through initial -- coding - if (substr($response1,0,2) == '--') { - $response1 = substr($response1,2); - if (preg_match('/^'.$response1.'$/'.$ignorecase, $response0) == 0) { - return true; - } - } - return false; - } - - - // function to find whether student's response matches at least the beginning of one of the correct answers - function find_closest(&$question, &$state, &$teststate, $isadaptive, $ispreview) { - global $CFG; - global $firstcorrectanswer; - global $closestcomplete; - $closestcomplete = false; - - if (isset($state->responses[''])) { - $response0 = $this->remove_blanks(stripslashes($state->responses[''])); - } else { - return null; - } - if ( (!$isadaptive) && (!$ispreview) ) { - return null; // no need to generate alternate answers because no hint will be needed in non-adaptive mode - } - - // generate alternative answers for answers with score > 0% - // this means that TEACHER MUST write answers with a > 0% grade as regexp generating alternative answers - $correctanswers = array(); - $i = 0; - foreach ($question->options->answers as $answer) { - if ($i == 0) { - $i++; - if ($answer->fraction != 1) { - notify ("ATTENTION! first answer must be correct and its Grade must be 100%"); - } else { - $firstcorrectanswer = $answer->answer; - $correctanswer['answer'] = $answer->answer; - $correctanswer['fraction'] = $answer->fraction; - $correctanswers[] = $correctanswer; - } - } else if ($answer->fraction != 0) { - $correctanswer['answer'] = $answer->answer; - $correctanswer['fraction'] = $answer->fraction; - $correctanswers[] = $correctanswer; - } - } - $alternateanswers = array(); - $alternateanswersic = array(); - $i=0; - foreach ($correctanswers as $thecorrectanswer) { - $i++; - if ($i == 1) { - $alternateanswers[] = $firstcorrectanswer; - continue; - } - $correctanswer = $thecorrectanswer['answer']; - $fraction = $thecorrectanswer['fraction']*100; - $fraction = $fraction."%"; //JR 05-10-2007 - if ( substr($correctanswer,strlen($correctanswer) - 2, 2) == '/i') { // ignore case - $correctanswer = substr($correctanswer, 0, strlen($correctanswer) - 2); - $r = expand_regexp($correctanswer); // go to expand_regexp function to generate alternative answers - if ($r) { // if error in regular expression, expand_regexp will return nothing - if (is_array($r)) { - $alternateanswersic[] = "$fraction $correctanswer (ignore case)"; // CAUTION: the tag is detected in get_closest function! - $alternateanswersic = array_merge($alternateanswersic, $r); // ignorecase alternateanswers - } else { - $alternateanswersic[] = "$fraction $r"; - $alternateanswersic[] = "$r"; - } - } - } else { // do not ignorecase - $r = expand_regexp($correctanswer); - if ($r) { // if error in regular expression, expand_regexp will return nothing - if (is_array($r)) { - $alternateanswers[] = "$fraction $correctanswer"; - $alternateanswers = array_merge($alternateanswers, $r); // normal alternateanswers - } else { - $alternateanswers[] = "$fraction $r"; - $alternateanswers[] = "$r"; - } - } - } - } - -// print display button for teacher only - if (($ispreview) && ((sizeof($alternateanswersic) != 0) || (sizeof($alternateanswers) != 0))) { - $show = get_string("showalternate", "qtype_regexp"); - echo(""); - -// print alernate answers - echo('"); - } -// if student response is null (nothing typed in) then no need to go get closest correct answer - if (!$response0) { - return false; - } -// find closest answer matching student response - if (is_array ($alternateanswers) ) { - $closestanswer = get_closest( $response0, $alternateanswers, false); - } - if (is_array ($alternateanswersic) ) { - $closestansweric = get_closest( $response0, $alternateanswersic, true); - } - if ($closestanswer[1] == true) { - $closestcomplete = true; - return $closestanswer[0]; - } elseif ($closestansweric[1] == true) { - $closestcomplete = true; - return $closestansweric[0]; - } - if (strlen($closestanswer[0]) > strlen($closestansweric[0]) ) { // find longest closest answer from ignore case and don't ignore case - $closest = $closestanswer[0]; - } else { - $closest = $closestansweric[0]; - } -// if nothing correct, give first character of firstcorrectanswer to student (if ishint is true) - $ishint = false; - if (substr($response0, strlen($response0)-2) == '¶' ) { // hint asked for - $ishint = true; - } - if ($closest == '' && $ishint) { - $closest = $this->utf8_substr ($firstcorrectanswer, 0, 1); - } - return $closest; - } - - function test_response(&$question, &$state, &$answer) { - $teststate = clone($state); - $teststate->responses[''] = trim($answer->answer); - if($this->compare_responses($question, $state, $teststate)) { - return true; - } - return false; - } - - function get_actual_response($question, $state) { - // change length to truncate responses here if you want - $lmax = 255; - if (!empty($state->responses)) { - $responses[] = (strlen($state->responses['']) > $lmax) ? - substr($state->responses[''], 0, $lmax).'...' : $state->responses['']; - } else { - $responses[] = ''; - } - return $responses; - } - /** - * Return a summary of the student response - * - * This function returns a short string of no more than a given length that - * summarizes the student's response in the given $state. This is used for - * example in the response history table - * @return string The summary of the student response - * @param object $question - * @param object $state The state whose responses are to be summarized - * @param int $length The maximum length of the returned string - */ - function response_summary($question, $state, $length=255) { - // This should almost certainly be overridden - return substr(implode(',', $this->get_actual_response($question, $state)), 0, $length); - } - - /// BACKUP FUNCTIONS //////////////////////////// - - /* - * Backup the data in the question - * - * This is used in question/backuplib.php - */ - function backup($bf,$preferences,$question,$level=6) { - - $status = true; - - $regexps = get_records("question_regexp","question",$question,"id"); - //If there are regexps - if ($regexps) { - //Iterate over each regexp - foreach ($regexps as $regexp) { - $status = fwrite ($bf,start_tag("REGEXP",$level,true)); - //Print regexp contents - fwrite ($bf,full_tag("ANSWERS",$level+1,false,$regexp->answers)); - fwrite ($bf,full_tag("USEHINT",$level+1,false,$regexp->usehint)); - $status = fwrite ($bf,end_tag("REGEXP",$level,true)); - } - //Now print question_answers - $status = question_backup_answers($bf,$preferences,$question); - } - return $status; - } - -/// RESTORE FUNCTIONS ///////////////// - - /* - * Restores the data in the question - * - * This is used in question/restorelib.php - */ - function restore($old_question_id,$new_question_id,$info,$restore) { - - $status = true; - $regexp = ''; - - //Get the regexps array - $regexps = $info['#']['REGEXP']; - - //Iterate over regexps - for($i = 0; $i < sizeof($regexps); $i++) { - $sho_info = $regexps[$i]; - - //Now, build the question_regexp record structure - $regexp->question = $new_question_id; - $regexp->answers = backup_todb($sho_info['#']['ANSWERS']['0']['#']); - $regexp->usehint = backup_todb($sho_info['#']['USEHINT']['0']['#']); - - //We have to recode the answers field (a list of answers id) - //Extracts answer id from sequence - $answers_field = ""; - $in_first = true; - $tok = strtok($regexp->answers,","); - while ($tok) { - //Get the answer from backup_ids - $answer = backup_getid($restore->backup_unique_code,"question_answers",$tok); - if ($answer) { - if ($in_first) { - $answers_field .= $answer->new_id; - $in_first = false; - } else { - $answers_field .= ",".$answer->new_id; - } - } - //check for next - $tok = strtok(","); - } - //We have the answers field recoded to its new ids - $regexp->answers = $answers_field; - - //The structure is equal to the db, so insert the question_regexp - $newid = insert_record ("question_regexp",$regexp); - - //Do some output - if (($i+1) % 50 == 0) { - if (!defined('RESTORE_SILENTLY')) { - echo "."; - if (($i+1) % 1000 == 0) { - echo "
"; - } - } - backup_flush(300); - } - - if (!$newid) { - $status = false; - } - } - - return $status; - } - - - /** - * Provide export functionality for xml format - * @param question object the question object - * @param format object the format object so that helper methods can be used - * @param extra mixed any additional format specific data that may be passed by the format (see format code for info) - * @return string the data to append to the output buffer or false if error - */ - function export_to_xml( $question, $format, $extra=null ) { - $expout = " {$question->options->usehint}\n "; - foreach($question->options->answers as $answer) { - $percent = 100 * $answer->fraction; - $expout .= " \n"; - $expout .= $format->writetext( $answer->answer,3,false ); - $expout .= " \n"; - $expout .= $format->writetext( $answer->feedback,4,false ); - $expout .= " \n"; - $expout .= " \n"; - } - return $expout; - } - - function import_from_xml($data, $question, $format, $extra=null) { - // get common parts - $question = $format->import_headers($data); - - // header parts particular to regexp - $question->qtype = 'regexp'; - - // get usehint - $question->usehint = $data['#']['usehint'][0]['#']; - - // run through the answers - $answers = $data['#']['answer']; - $a_count = 0; - foreach ($answers as $answer) { - $ans = $format->import_answer( $answer ); - $question->answer[$a_count] = $ans->answer; - $question->fraction[$a_count] = $ans->fraction; - $question->feedback[$a_count] = $ans->feedback; - ++$a_count; - } - return $question; } - -} -//// END OF CLASS //// - -////////////////////////////////////////////////////////////////////////// -//// INITIATION - Without this line the question type is not in use... /// -////////////////////////////////////////////////////////////////////////// -$QTYPES['regexp']= new question_regexp_qtype(); -// The following adds the questiontype to the menu of types shown to teachers -$QTYPE_MENU['regexp'] = get_string("regexp", "qtype_regexp"); - -?> \ No newline at end of file diff --git a/question/type/regexp/version.php b/question/type/regexp/version.php deleted file mode 100644 index 66df0328883..00000000000 --- a/question/type/regexp/version.php +++ /dev/null @@ -1,6 +0,0 @@ -version = 2007012800; -$plugin->requires = 2006032200; - -?>