00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 include_once "./assessment/classes/class.assQuestion.php";
00025 include_once "./assessment/classes/inc.AssessmentConstants.php";
00026
00037 class assClozeTest extends assQuestion
00038 {
00047 var $cloze_text;
00048
00056 var $gaps;
00057
00065 var $start_tag;
00066
00074 var $end_tag;
00075
00086 var $textgap_rating;
00087
00100 function assClozeTest(
00101 $title = "",
00102 $comment = "",
00103 $author = "",
00104 $owner = -1,
00105 $cloze_text = ""
00106 )
00107 {
00108 $this->start_tag = "[gap]";
00109 $this->end_tag = "[/gap]";
00110 $this->assQuestion($title, $comment, $author, $owner);
00111 $this->gaps = array();
00112 $this->setClozeText($cloze_text);
00113 }
00114
00123 function isComplete()
00124 {
00125 if (($this->title) and ($this->author) and ($this->cloze_text) and (count($this->gaps)) and ($this->getMaximumPoints() > 0))
00126 {
00127 return true;
00128 }
00129 else
00130 {
00131 return false;
00132 }
00133 }
00134
00143 function &createCloseTextArray()
00144 {
00145 $result = array();
00146 $search_pattern = "|\[gap([^\]]*?)\](.*?)\[/gap\]|i";
00147 preg_match_all($search_pattern, $this->cloze_text, $gaps);
00148 if (count($gaps[0]))
00149 {
00150
00151 $delimiters = preg_split($search_pattern, $this->cloze_text, -1, PREG_SPLIT_OFFSET_CAPTURE);
00152 $result["gaps"] = array();
00153 foreach ($gaps[0] as $index => $gap)
00154 {
00155 $result["gaps"][$index] = array();
00156 $result["gaps"][$index]["gap"] = $gap;
00157 $result["gaps"][$index]["params"] = array();
00158 $result["gaps"][$index]["params"]["text"] = $gaps[1][$index];
00159
00160 if (preg_match("/name\=\"([^\"]*?)\"/", $gaps[1][$index], $params))
00161 {
00162 $result["gaps"][$index]["params"]["name"] = $params[1];
00163 }
00164 else
00165 {
00166 $result["gaps"][$index]["params"]["name"] = $this->lng->txt("gap") . " " . ($index+1);
00167 }
00168 if (preg_match("/type\=\"([^\"]*?)\"/", $gaps[1][$index], $params))
00169 {
00170 $result["gaps"][$index]["params"]["type"] = $params[1];
00171 }
00172 else
00173 {
00174 $result["gaps"][$index]["params"]["type"] = "text";
00175 }
00176 if (preg_match("/shuffle\=\"([^\"]*?)\"/", $gaps[1][$index], $params))
00177 {
00178 $result["gaps"][$index]["params"]["shuffle"] = $params[1];
00179 }
00180 else
00181 {
00182 if (strcmp(strtolower($result["gaps"][$index]["params"]["type"]), "select") == 0)
00183 {
00184 $result["gaps"][$index]["params"]["shuffle"] = "yes";
00185 }
00186 }
00187 $result["gaps"][$index]["text"] = array();
00188 $result["gaps"][$index]["text"]["text"] = $gaps[2][$index];
00189 $textparams = preg_split("/(?<!\\\\),/", $gaps[2][$index]);
00190 foreach ($textparams as $key => $value)
00191 {
00192 $result["gaps"][$index]["text"][$key] = $value;
00193 }
00194 }
00195 $result["delimiters"] = $delimiters;
00196 }
00197 else
00198 {
00199 $result["gaps"] = array();
00200 $result["delimiters"] =
00201 array(
00202 array(0 => $this->cloze_text, 1 => "0")
00203 );
00204 }
00205 return $result;
00206 }
00207
00216 function createCloseTextFromArray($assoc_array)
00217 {
00218 $this->cloze_text = "";
00219 if (count($assoc_array))
00220 {
00221 $gap = 0;
00222 foreach ($assoc_array["delimiters"] as $key => $value)
00223 {
00224 if (($key > 0) && ($key < count($assoc_array["delimiters"])))
00225 {
00226 if (strcmp($assoc_array["gaps"][$gap]["params"]["shuffle"], "") == 0)
00227 {
00228 $shuffle = "";
00229 }
00230 else
00231 {
00232 $shuffle = " shuffle=\"" . $assoc_array["gaps"][$gap]["params"]["shuffle"] . "\"";
00233 }
00234 $textarray = array();
00235 if (is_array($assoc_array["gaps"][$gap]["text"]))
00236 {
00237 foreach ($assoc_array["gaps"][$gap]["text"] as $textindex => $textvalue)
00238 {
00239 if (preg_match("/\d+/", $textindex))
00240 {
00241 array_push($textarray, $textvalue);
00242 }
00243 }
00244 }
00245 if (count($textarray))
00246 {
00247 $this->cloze_text .= sprintf("[gap name=\"%s\" type=\"%s\"%s]%s[/gap]",
00248 $assoc_array["gaps"][$gap]["params"]["name"],
00249 $assoc_array["gaps"][$gap]["params"]["type"],
00250 $shuffle,
00251 join(",", $textarray)
00252 );
00253 }
00254 $gap++;
00255 }
00256 $this->cloze_text .= $value[0];
00257 }
00258 }
00259 }
00260
00269 function saveToDb($original_id = "")
00270 {
00271 global $ilDB;
00272
00273 $complete = 0;
00274 if ($this->isComplete())
00275 {
00276 $complete = 1;
00277 }
00278
00279 $estw_time = $this->getEstimatedWorkingTime();
00280 $estw_time = sprintf("%02d:%02d:%02d", $estw_time['h'], $estw_time['m'], $estw_time['s']);
00281 if ($original_id)
00282 {
00283 $original_id = $ilDB->quote($original_id);
00284 }
00285 else
00286 {
00287 $original_id = "NULL";
00288 }
00289
00290
00291 include_once("./Services/RTE/classes/class.ilRTE.php");
00292 ilRTE::_cleanupMediaObjectUsage($this->cloze_text, "qpl:html",
00293 $this->getId());
00294
00295 if ($this->id == -1)
00296 {
00297
00298 $now = getdate();
00299 $created = sprintf("%04d%02d%02d%02d%02d%02d", $now['year'], $now['mon'], $now['mday'], $now['hours'], $now['minutes'], $now['seconds']);
00300 $query = sprintf("INSERT INTO qpl_questions (question_id, question_type_fi, obj_fi, title, comment, points, author, owner, question_text, working_time, complete, created, original_id, TIMESTAMP) VALUES (NULL, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NULL)",
00301 $ilDB->quote($this->getQuestionType()),
00302 $ilDB->quote($this->obj_id),
00303 $ilDB->quote($this->title),
00304 $ilDB->quote($this->comment),
00305 $ilDB->quote($this->getMaximumPoints() . ""),
00306 $ilDB->quote($this->author),
00307 $ilDB->quote($this->owner),
00308 $ilDB->quote(ilRTE::_replaceMediaObjectImageSrc($this->cloze_text, 0)),
00309 $ilDB->quote($estw_time),
00310 $ilDB->quote("$complete"),
00311 $ilDB->quote($created),
00312 $original_id
00313 );
00314 $result = $ilDB->query($query);
00315 if ($result == DB_OK)
00316 {
00317 $this->id = $ilDB->getLastInsertId();
00318 $query = sprintf("INSERT INTO qpl_question_cloze (question_fi, textgap_rating) VALUES (%s, %s)",
00319 $ilDB->quote($this->id . ""),
00320 $ilDB->quote($this->textgap_rating . "")
00321 );
00322 $ilDB->query($query);
00323
00324
00325 $this->createPageObject();
00326
00327
00328 if ($this->getTestId() > 0)
00329 {
00330 $this->insertIntoTest($this->getTestId());
00331 }
00332 }
00333 }
00334 else
00335 {
00336
00337 $query = sprintf("UPDATE qpl_questions SET obj_fi = %s, title = %s, comment = %s, points = %s, author = %s, question_text = %s, working_time = %s, complete = %s WHERE question_id = %s",
00338 $ilDB->quote($this->obj_id. ""),
00339 $ilDB->quote($this->title),
00340 $ilDB->quote($this->comment),
00341 $ilDB->quote($this->getMaximumPoints() . ""),
00342 $ilDB->quote($this->author),
00343 $ilDB->quote(ilRTE::_replaceMediaObjectImageSrc($this->cloze_text, 0)),
00344 $ilDB->quote($estw_time),
00345 $ilDB->quote("$complete"),
00346 $ilDB->quote($this->id)
00347 );
00348 $result = $ilDB->query($query);
00349 $query = sprintf("UPDATE qpl_question_cloze SET textgap_rating = %s WHERE question_fi = %s",
00350 $ilDB->quote($this->textgap_rating . ""),
00351 $ilDB->quote($this->id . "")
00352 );
00353 $result = $ilDB->query($query);
00354 }
00355
00356 if ($result == DB_OK)
00357 {
00358
00359
00360
00361 $query = sprintf("DELETE FROM qpl_answer_cloze WHERE question_fi = %s",
00362 $ilDB->quote($this->id)
00363 );
00364 $result = $ilDB->query($query);
00365
00366 foreach ($this->gaps as $key => $value)
00367 {
00368 foreach ($value as $answer_id => $answer_obj)
00369 {
00370 $query = sprintf("INSERT INTO qpl_answer_cloze (answer_id, question_fi, gap_id, answertext, points, aorder, cloze_type, name, shuffle, correctness) VALUES (NULL, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
00371 $ilDB->quote($this->id),
00372 $ilDB->quote($key),
00373 $ilDB->quote($answer_obj->getAnswertext() . ""),
00374 $ilDB->quote($answer_obj->getPoints() . ""),
00375 $ilDB->quote($answer_obj->getOrder() . ""),
00376 $ilDB->quote($answer_obj->getClozeType() . ""),
00377 $ilDB->quote($answer_obj->getName() . ""),
00378 $ilDB->quote($answer_obj->getShuffle() . ""),
00379 $ilDB->quote($answer_obj->getState() . "")
00380 );
00381 $answer_result = $ilDB->query($query);
00382 }
00383 }
00384 }
00385 parent::saveToDb($original_id);
00386 }
00387
00397 function loadFromDb($question_id)
00398 {
00399 global $ilDB;
00400
00401 include_once "./assessment/classes/class.assAnswerCloze.php";
00402 $query = sprintf("SELECT qpl_questions.*, qpl_question_cloze.* FROM qpl_questions, qpl_question_cloze WHERE question_id = %s AND qpl_questions.question_id = qpl_question_cloze.question_fi",
00403 $ilDB->quote($question_id)
00404 );
00405 $result = $ilDB->query($query);
00406 if (strcmp(strtolower(get_class($result)), db_result) == 0) {
00407 if ($result->numRows() == 1) {
00408 $data = $result->fetchRow(DB_FETCHMODE_OBJECT);
00409 $this->id = $question_id;
00410 $this->obj_id = $data->obj_fi;
00411 $this->title = $data->title;
00412 $this->comment = $data->comment;
00413 $this->solution_hint = $data->solution_hint;
00414 $this->original_id = $data->original_id;
00415 $this->author = $data->author;
00416 $this->points = $data->points;
00417 $this->owner = $data->owner;
00418 $this->cloze_text = $data->question_text;
00419
00420 $this->cloze_text = preg_replace("/<gap([^>]*?)>/", "[gap" . "\\1" . "]", $this->cloze_text);
00421 $this->cloze_text = str_replace("</gap>", "[/gap]", $this->cloze_text);
00422 include_once("./Services/RTE/classes/class.ilRTE.php");
00423 $this->cloze_text = ilRTE::_replaceMediaObjectImageSrc($this->cloze_text, 1);
00424 $this->setTextgapRating($data->textgap_rating);
00425 $this->setEstimatedWorkingTime(substr($data->working_time, 0, 2), substr($data->working_time, 3, 2), substr($data->working_time, 6, 2));
00426 }
00427
00428 $query = sprintf("SELECT * FROM qpl_answer_cloze WHERE question_fi = %s ORDER BY gap_id, aorder ASC",
00429 $ilDB->quote($question_id)
00430 );
00431 $result = $ilDB->query($query);
00432 if (strcmp(strtolower(get_class($result)), db_result) == 0) {
00433 $counter = -1;
00434 while ($data = $result->fetchRow(DB_FETCHMODE_OBJECT)) {
00435 if ($data->gap_id != $counter) {
00436 $answer_array = array();
00437 array_push($this->gaps, $answer_array);
00438 $counter = $data->gap_id;
00439 }
00440 if ($data->cloze_type == CLOZE_SELECT)
00441 {
00442 if ($data->correctness == 0)
00443 {
00444
00445 $data->correctness = 1;
00446 $data->points = 0;
00447 }
00448 }
00449 array_push($this->gaps[$counter], new ASS_AnswerCloze($data->answertext, $data->points, $data->aorder, $data->correctness, $data->cloze_type, $data->name, $data->shuffle, $data->answer_id));
00450 }
00451 }
00452 }
00453 parent::loadFromDb($question_id);
00454 }
00455
00463 function addAnswer($gap, $answertext, $points, $answerorder, $correctness, $clozetype, $name, $shuffle, $answer_id = -1)
00464 {
00465 include_once "./assessment/classes/class.assAnswerCloze.php";
00466 if (!is_array($this->gaps[$gap]))
00467 {
00468 $this->gaps[$gap] = array();
00469 }
00470 array_push($this->gaps[$gap], new ASS_AnswerCloze($answertext, $points, $answerorder, $correctness, $clozetype, $name, $shuffle, $answer_id));
00471 }
00472
00480 function duplicate($for_test = true, $title = "", $author = "", $owner = "")
00481 {
00482 if ($this->id <= 0)
00483 {
00484
00485 return;
00486 }
00487
00488 $this_id = $this->getId();
00489 $clone = $this;
00490 include_once ("./assessment/classes/class.assQuestion.php");
00491 $original_id = assQuestion::_getOriginalId($this->id);
00492 $clone->id = -1;
00493 if ($title)
00494 {
00495 $clone->setTitle($title);
00496 }
00497 if ($author)
00498 {
00499 $clone->setAuthor($author);
00500 }
00501 if ($owner)
00502 {
00503 $clone->setOwner($owner);
00504 }
00505 if ($for_test)
00506 {
00507 $clone->saveToDb($original_id);
00508 }
00509 else
00510 {
00511 $clone->saveToDb();
00512 }
00513
00514
00515 $clone->copyPageOfQuestion($this_id);
00516
00517 $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
00518
00519 return $clone->id;
00520 }
00521
00529 function copyObject($target_questionpool, $title = "")
00530 {
00531 if ($this->id <= 0)
00532 {
00533
00534 return;
00535 }
00536
00537 $clone = $this;
00538 include_once ("./assessment/classes/class.assQuestion.php");
00539 $original_id = assQuestion::_getOriginalId($this->id);
00540 $clone->id = -1;
00541 $source_questionpool = $this->getObjId();
00542 $clone->setObjId($target_questionpool);
00543 if ($title)
00544 {
00545 $clone->setTitle($title);
00546 }
00547 $clone->saveToDb();
00548
00549
00550 $clone->copyPageOfQuestion($original_id);
00551
00552 $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
00553
00554 return $clone->id;
00555 }
00556
00570 function fromXML(&$item, &$questionpool_id, &$tst_id, &$tst_object, &$question_counter, &$import_mapping)
00571 {
00572 global $ilUser;
00573
00574
00575 unset($_SESSION["import_mob_xhtml"]);
00576 $presentation = $item->getPresentation();
00577 $duration = $item->getDuration();
00578 $questiontext = array();
00579 $shuffle = 0;
00580 $now = getdate();
00581 $created = sprintf("%04d%02d%02d%02d%02d%02d", $now['year'], $now['mon'], $now['mday'], $now['hours'], $now['minutes'], $now['seconds']);
00582 $gaps = array();
00583 foreach ($presentation->order as $entry)
00584 {
00585 switch ($entry["type"])
00586 {
00587 case "material":
00588
00589 $material = $presentation->material[$entry["index"]];
00590 array_push($questiontext, $this->QTIMaterialToString($material));
00591 break;
00592 case "response":
00593 $response = $presentation->response[$entry["index"]];
00594 $rendertype = $response->getRenderType();
00595 array_push($questiontext, "<<" . $response->getIdent() . ">>");
00596 switch (strtolower(get_class($response->getRenderType())))
00597 {
00598 case "ilqtirenderfib":
00599 array_push($gaps, array("ident" => $response->getIdent(), "type" => "text", "answers" => array()));
00600 break;
00601 case "ilqtirenderchoice":
00602 $answers = array();
00603 $shuffle = $rendertype->getShuffle();
00604 $answerorder = 0;
00605 foreach ($rendertype->response_labels as $response_label)
00606 {
00607 $ident = $response_label->getIdent();
00608 $answertext = "";
00609 foreach ($response_label->material as $mat)
00610 {
00611 $answertext .= $this->QTIMaterialToString($mat);
00612 }
00613 $answers[$ident] = array(
00614 "answertext" => $answertext,
00615 "points" => 0,
00616 "answerorder" => $answerorder++,
00617 "action" => "",
00618 "shuffle" => $rendertype->getShuffle()
00619 );
00620 }
00621 array_push($gaps, array("ident" => $response->getIdent(), "type" => "choice", "shuffle" => $rendertype->getShuffle(), "answers" => $answers));
00622 break;
00623 }
00624 break;
00625 }
00626 }
00627 $responses = array();
00628 foreach ($item->resprocessing as $resprocessing)
00629 {
00630 foreach ($resprocessing->respcondition as $respcondition)
00631 {
00632 $ident = "";
00633 $correctness = 1;
00634 $conditionvar = $respcondition->getConditionvar();
00635 foreach ($conditionvar->order as $order)
00636 {
00637 switch ($order["field"])
00638 {
00639 case "varequal":
00640 $equals = $conditionvar->varequal[$order["index"]]->getContent();
00641 $gapident = $conditionvar->varequal[$order["index"]]->getRespident();
00642 break;
00643 }
00644 }
00645 foreach ($respcondition->setvar as $setvar)
00646 {
00647 if (strcmp($gapident, "") != 0)
00648 {
00649 foreach ($gaps as $gi => $g)
00650 {
00651 if (strcmp($g["ident"], $gapident) == 0)
00652 {
00653 if (strcmp($g["type"], "choice") == 0)
00654 {
00655 foreach ($gaps[$gi]["answers"] as $ai => $answer)
00656 {
00657 if (strcmp($answer["answertext"], $equals) == 0)
00658 {
00659 $gaps[$gi]["answers"][$ai]["action"] = $setvar->getAction();
00660 $gaps[$gi]["answers"][$ai]["points"] = $setvar->getContent();
00661 }
00662 }
00663 }
00664 else if (strcmp($g["type"], "text") == 0)
00665 {
00666 array_push($gaps[$gi]["answers"], array(
00667 "answertext" => $equals,
00668 "points" => $setvar->getContent(),
00669 "answerorder" => count($gaps[$gi]["answers"]),
00670 "action" => $setvar->getAction(),
00671 "shuffle" => 1
00672 ));
00673 }
00674 }
00675 }
00676 }
00677 }
00678 }
00679 }
00680 $this->setTitle($item->getTitle());
00681 $this->setComment($item->getComment());
00682 $this->setAuthor($item->getAuthor());
00683 $this->setOwner($ilUser->getId());
00684 $this->setObjId($questionpool_id);
00685 $this->setEstimatedWorkingTime($duration["h"], $duration["m"], $duration["s"]);
00686 $textgap_rating = $item->getMetadataEntry("textgaprating");
00687 if (strlen($textgap_rating) == 0) $textgap_rating = "ci";
00688 $this->setTextgapRating($textgap_rating);
00689 $gaptext = array();
00690 foreach ($gaps as $gapidx => $gap)
00691 {
00692 $gapcontent = array();
00693 $type = 0;
00694 $typetext = "text";
00695 $shuffletext = "";
00696 if (strcmp($gap["type"], "choice") == 0)
00697 {
00698 $type = 1;
00699 $typetext = "select";
00700 if ($gap["shuffle"] == 0)
00701 {
00702 $shuffletext = " shuffle=\"no\"";
00703 }
00704 else
00705 {
00706 $shuffletext = " shuffle=\"yes\"";
00707 }
00708 }
00709 foreach ($gap["answers"] as $index => $answer)
00710 {
00711 $this->addAnswer($gapidx, $answer["answertext"], $answer["points"], $answer["answerorder"], 1, $type, $gap["ident"], $answer["shuffle"]);
00712 array_push($gapcontent, $answer["answertext"]);
00713 }
00714 $gaptext[$gap["ident"]] = "[gap type=\"$typetext\" name=\"" . $gap["ident"] . "\"$shuffletext]" . join(",", $gapcontent). "[/gap]";
00715 }
00716 $clozetext = join("", $questiontext);
00717 foreach ($gaptext as $idx => $val)
00718 {
00719 $clozetext = str_replace("<<" . $idx . ">>", $val, $clozetext);
00720 }
00721 $this->cloze_text = $clozetext;
00722 $this->saveToDb();
00723
00724 if (is_array($_SESSION["import_mob_xhtml"]))
00725 {
00726 include_once "./content/classes/Media/class.ilObjMediaObject.php";
00727 include_once "./Services/RTE/classes/class.ilRTE.php";
00728 foreach ($_SESSION["import_mob_xhtml"] as $mob)
00729 {
00730 if ($tst_id > 0)
00731 {
00732 include_once "./assessment/classes/class.ilObjTest.php";
00733 $importfile = ilObjTest::_getImportDirectory() . "/" . $_SESSION["tst_import_subdir"] . "/" . $mob["uri"];
00734 }
00735 else
00736 {
00737 include_once "./assessment/classes/class.ilObjQuestionPool.php";
00738 $importfile = ilObjQuestionPool::_getImportDirectory() . "/" . $_SESSION["qpl_import_subdir"] . "/" . $mob["uri"];
00739 }
00740 $media_object =& ilObjMediaObject::_saveTempFileAsMediaObject(basename($importfile), $importfile, FALSE);
00741
00742 $this->cloze_text = ilRTE::_replaceMediaObjectImageSrc(str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $this->cloze_text), 1);
00743 }
00744 $this->saveToDb();
00745 }
00746 if (count($item->suggested_solutions))
00747 {
00748 foreach ($item->suggested_solutions as $suggested_solution)
00749 {
00750 $this->setSuggestedSolution($suggested_solution["solution"]->getContent(), $suggested_solution["gap_index"], true);
00751 }
00752 $this->saveToDb();
00753 }
00754 if ($tst_id > 0)
00755 {
00756 $q_1_id = $this->getId();
00757 $question_id = $this->duplicate(true);
00758 $tst_object->questions[$question_counter++] = $question_id;
00759 $import_mapping[$item->getIdent()] = array("pool" => $q_1_id, "test" => $question_id);
00760 }
00761 else
00762 {
00763 $import_mapping[$item->getIdent()] = array("pool" => $this->getId(), "test" => 0);
00764 }
00765
00766 }
00767
00777 function to_xml($a_include_header = true, $a_include_binary = true, $a_shuffle = false, $test_output = false, $force_image_references = false)
00778 {
00779 include_once("./classes/class.ilXmlWriter.php");
00780 $a_xml_writer = new ilXmlWriter;
00781
00782 $a_xml_writer->xmlHeader();
00783 $a_xml_writer->xmlStartTag("questestinterop");
00784 $attrs = array(
00785 "ident" => "il_".IL_INST_ID."_qst_".$this->getId(),
00786 "title" => $this->getTitle()
00787 );
00788 $a_xml_writer->xmlStartTag("item", $attrs);
00789
00790 $a_xml_writer->xmlElement("qticomment", NULL, $this->getComment());
00791
00792 $workingtime = $this->getEstimatedWorkingTime();
00793 $duration = sprintf("P0Y0M0DT%dH%dM%dS", $workingtime["h"], $workingtime["m"], $workingtime["s"]);
00794 $a_xml_writer->xmlElement("duration", NULL, $duration);
00795
00796 $a_xml_writer->xmlStartTag("itemmetadata");
00797 $a_xml_writer->xmlStartTag("qtimetadata");
00798 $a_xml_writer->xmlStartTag("qtimetadatafield");
00799 $a_xml_writer->xmlElement("fieldlabel", NULL, "ILIAS_VERSION");
00800 $a_xml_writer->xmlElement("fieldentry", NULL, $this->ilias->getSetting("ilias_version"));
00801 $a_xml_writer->xmlEndTag("qtimetadatafield");
00802 $a_xml_writer->xmlStartTag("qtimetadatafield");
00803 $a_xml_writer->xmlElement("fieldlabel", NULL, "QUESTIONTYPE");
00804 $a_xml_writer->xmlElement("fieldentry", NULL, CLOZE_TEST_IDENTIFIER);
00805 $a_xml_writer->xmlEndTag("qtimetadatafield");
00806 $a_xml_writer->xmlStartTag("qtimetadatafield");
00807 $a_xml_writer->xmlElement("fieldlabel", NULL, "AUTHOR");
00808 $a_xml_writer->xmlElement("fieldentry", NULL, $this->getAuthor());
00809 $a_xml_writer->xmlEndTag("qtimetadatafield");
00810 $a_xml_writer->xmlStartTag("qtimetadatafield");
00811 $a_xml_writer->xmlElement("fieldlabel", NULL, "textgaprating");
00812 $a_xml_writer->xmlElement("fieldentry", NULL, $this->getTextgapRating());
00813 $a_xml_writer->xmlEndTag("qtimetadatafield");
00814 $a_xml_writer->xmlEndTag("qtimetadata");
00815 $a_xml_writer->xmlEndTag("itemmetadata");
00816
00817
00818 $attrs = array(
00819 "label" => $this->getTitle()
00820 );
00821 $a_xml_writer->xmlStartTag("presentation", $attrs);
00822
00823 $a_xml_writer->xmlStartTag("flow");
00824 $text_parts = preg_split("/\[gap.*?\[\/gap\]/", $this->getClozeText());
00825
00826 for ($i = 0; $i <= $this->getGapCount(); $i++)
00827 {
00828
00829 if ($i == 0)
00830 {
00831 $this->addQTIMaterial($a_xml_writer, $text_parts[$i]);
00832 }
00833 else
00834 {
00835 $this->addQTIMaterial($a_xml_writer, $text_parts[$i], TRUE, FALSE);
00836 }
00837
00838 if ($i < $this->getGapCount())
00839 {
00840
00841 $gap = $this->getGap($i);
00842 if ($gap[0]->getClozeType() == CLOZE_SELECT)
00843 {
00844
00845 $attrs = array(
00846 "ident" => "gap_$i",
00847 "rcardinality" => "Single"
00848 );
00849 $a_xml_writer->xmlStartTag("response_str", $attrs);
00850 $solution = $this->getSuggestedSolution($i);
00851 if (count($solution))
00852 {
00853 if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $solution["internal_link"], $matches))
00854 {
00855 $attrs = array(
00856 "label" => "suggested_solution"
00857 );
00858 $a_xml_writer->xmlStartTag("material", $attrs);
00859 $intlink = "il_" . IL_INST_ID . "_" . $matches[2] . "_" . $matches[3];
00860 if (strcmp($matches[1], "") != 0)
00861 {
00862 $intlink = $solution["internal_link"];
00863 }
00864 $a_xml_writer->xmlElement("mattext", NULL, $intlink);
00865 $a_xml_writer->xmlEndTag("material");
00866 }
00867 }
00868
00869 $attrs = array();
00870 if ($gap[0]->getShuffle())
00871 {
00872 $attrs = array("shuffle" => "Yes");
00873 }
00874 else
00875 {
00876 $attrs = array("shuffle" => "No");
00877 }
00878 $a_xml_writer->xmlStartTag("render_choice", $attrs);
00879
00880
00881 $gkeys = array_keys($gap);
00882 if ($gap[0]->getShuffle() && $a_shuffle)
00883 {
00884 $gkeys = $this->pcArrayShuffle($gkeys);
00885 }
00886
00887
00888 foreach ($gkeys as $key)
00889 {
00890 $value = $gap[$key];
00891 $attrs = array(
00892 "ident" => $key
00893 );
00894 $a_xml_writer->xmlStartTag("response_label", $attrs);
00895 $a_xml_writer->xmlStartTag("material");
00896 $a_xml_writer->xmlElement("mattext", NULL, $value->getAnswertext());
00897 $a_xml_writer->xmlEndTag("material");
00898 $a_xml_writer->xmlEndTag("response_label");
00899 }
00900 $a_xml_writer->xmlEndTag("render_choice");
00901 $a_xml_writer->xmlEndTag("response_str");
00902 }
00903 else
00904 {
00905
00906 $attrs = array(
00907 "ident" => "gap_$i",
00908 "rcardinality" => "Single"
00909 );
00910 $a_xml_writer->xmlStartTag("response_str", $attrs);
00911 $solution = $this->getSuggestedSolution($i);
00912 if (count($solution))
00913 {
00914 if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $solution["internal_link"], $matches))
00915 {
00916 $attrs = array(
00917 "label" => "suggested_solution"
00918 );
00919 $a_xml_writer->xmlStartTag("material", $attrs);
00920 $intlink = "il_" . IL_INST_ID . "_" . $matches[2] . "_" . $matches[3];
00921 if (strcmp($matches[1], "") != 0)
00922 {
00923 $intlink = $solution["internal_link"];
00924 }
00925 $a_xml_writer->xmlElement("mattext", NULL, $intlink);
00926 $a_xml_writer->xmlEndTag("material");
00927 }
00928 }
00929 $attrs = array(
00930 "fibtype" => "String",
00931 "prompt" => "Box",
00932 "columns" => $this->getColumnSize($gap)
00933 );
00934 $a_xml_writer->xmlStartTag("render_fib");
00935 $attrs = array(
00936 "ident" => $i
00937 );
00938 $a_xml_writer->xmlEndTag("render_fib");
00939 $a_xml_writer->xmlEndTag("response_str");
00940 }
00941 }
00942 }
00943 $a_xml_writer->xmlEndTag("flow");
00944 $a_xml_writer->xmlEndTag("presentation");
00945
00946
00947 $a_xml_writer->xmlStartTag("resprocessing");
00948 $a_xml_writer->xmlStartTag("outcomes");
00949 $a_xml_writer->xmlStartTag("decvar");
00950 $a_xml_writer->xmlEndTag("decvar");
00951 $a_xml_writer->xmlEndTag("outcomes");
00952
00953
00954 for ($i = 0; $i < $this->getGapCount(); $i++)
00955 {
00956 $gap = $this->getGap($i);
00957 if ($gap[0]->getClozeType() == CLOZE_SELECT)
00958 {
00959 foreach ($gap as $index => $answer)
00960 {
00961 $attrs = array(
00962 "continue" => "Yes"
00963 );
00964 $a_xml_writer->xmlStartTag("respcondition", $attrs);
00965
00966 $a_xml_writer->xmlStartTag("conditionvar");
00967
00968 if (!$answer->isStateSet())
00969 {
00970 $a_xml_writer->xmlStartTag("not");
00971 }
00972
00973 $attrs = array(
00974 "respident" => "gap_$i"
00975 );
00976 $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
00977 if (!$answer->isStateSet())
00978 {
00979 $a_xml_writer->xmlEndTag("not");
00980 }
00981 $a_xml_writer->xmlEndTag("conditionvar");
00982
00983 $attrs = array(
00984 "action" => "Add"
00985 );
00986 $a_xml_writer->xmlElement("setvar", $attrs, $answer->getPoints());
00987
00988 $linkrefid = "";
00989 if ($answer->getPoints() > 0)
00990 {
00991 $linkrefid = "$i" . "_True";
00992 }
00993 else
00994 {
00995 $linkrefid = "$i" . "_False_$index";
00996 }
00997 $attrs = array(
00998 "feedbacktype" => "Response",
00999 "linkrefid" => $linkrefid
01000 );
01001 $a_xml_writer->xmlElement("displayfeedback", $attrs);
01002 $a_xml_writer->xmlEndTag("respcondition");
01003 }
01004 }
01005 else
01006 {
01007 foreach ($gap as $index => $answer)
01008 {
01009 $attrs = array(
01010 "continue" => "Yes"
01011 );
01012 $a_xml_writer->xmlStartTag("respcondition", $attrs);
01013
01014 $a_xml_writer->xmlStartTag("conditionvar");
01015 $attrs = array(
01016 "respident" => "gap_$i"
01017 );
01018 $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
01019 $a_xml_writer->xmlEndTag("conditionvar");
01020
01021 $attrs = array(
01022 "action" => "Add"
01023 );
01024 $a_xml_writer->xmlElement("setvar", $attrs, $answer->getPoints());
01025
01026 $attrs = array(
01027 "feedbacktype" => "Response",
01028 "linkrefid" => "$i" . "_True_$index"
01029 );
01030 $a_xml_writer->xmlElement("displayfeedback", $attrs);
01031 $a_xml_writer->xmlEndTag("respcondition");
01032 }
01033 }
01034 }
01035 $a_xml_writer->xmlEndTag("resprocessing");
01036
01037
01038 for ($i = 0; $i < $this->getGapCount(); $i++)
01039 {
01040 $gap = $this->getGap($i);
01041 if ($gap[0]->getClozeType() == CLOZE_SELECT)
01042 {
01043 foreach ($gap as $index => $answer)
01044 {
01045 $linkrefid = "";
01046 if ($answer->isStateSet())
01047 {
01048 $linkrefid = "$i" . "_True";
01049 }
01050 else
01051 {
01052 $linkrefid = "$i" . "_False_$index";
01053 }
01054 $attrs = array(
01055 "ident" => $linkrefid,
01056 "view" => "All"
01057 );
01058 $a_xml_writer->xmlStartTag("itemfeedback", $attrs);
01059
01060 $a_xml_writer->xmlStartTag("flow_mat");
01061 $a_xml_writer->xmlStartTag("material");
01062 $a_xml_writer->xmlElement("mattext");
01063 $a_xml_writer->xmlEndTag("material");
01064 $a_xml_writer->xmlEndTag("flow_mat");
01065 $a_xml_writer->xmlEndTag("itemfeedback");
01066 }
01067 }
01068 else
01069 {
01070 foreach ($gap as $index => $answer)
01071 {
01072 $linkrefid = "";
01073 if ($answer->isStateSet())
01074 {
01075 $linkrefid = "$i" . "_True_$index";
01076 }
01077 else
01078 {
01079 $linkrefid = "$i" . "_False_$index";
01080 }
01081 $attrs = array(
01082 "ident" => $linkrefid,
01083 "view" => "All"
01084 );
01085 $a_xml_writer->xmlStartTag("itemfeedback", $attrs);
01086
01087 $a_xml_writer->xmlStartTag("flow_mat");
01088 $a_xml_writer->xmlStartTag("material");
01089 $a_xml_writer->xmlElement("mattext");
01090 $a_xml_writer->xmlEndTag("material");
01091 $a_xml_writer->xmlEndTag("flow_mat");
01092 $a_xml_writer->xmlEndTag("itemfeedback");
01093 }
01094 }
01095 }
01096
01097 $a_xml_writer->xmlEndTag("item");
01098 $a_xml_writer->xmlEndTag("questestinterop");
01099
01100 $xml = $a_xml_writer->xmlDumpMem(FALSE);
01101 if (!$a_include_header)
01102 {
01103 $pos = strpos($xml, "?>");
01104 $xml = substr($xml, $pos + 2);
01105 }
01106 return $xml;
01107 }
01108
01120 function setClozeText($cloze_text = "")
01121 {
01122 $this->gaps = array();
01123 $this->cloze_text =& $cloze_text;
01124 $close = $this->createCloseTextArray();
01125 if (count($close))
01126 {
01127 foreach ($close["gaps"] as $key => $value)
01128 {
01129 if (strcmp(strtolower($value["params"]["type"]), "select") == 0)
01130 {
01131 $type = CLOZE_SELECT;
01132 }
01133 else
01134 {
01135 $type = CLOZE_TEXT;
01136 }
01137 if ($type == CLOZE_TEXT)
01138 {
01139 $default_state = 1;
01140 }
01141 else
01142 {
01143 $default_state = 0;
01144 }
01145 $name = $value["params"]["name"];
01146 if (strcmp(strtolower($value["params"]["shuffle"]), "no") == 0)
01147 {
01148 $shuffle = 0;
01149 }
01150 else
01151 {
01152 $shuffle = 1;
01153 }
01154 $answer_array = array();
01155 include_once "./assessment/classes/class.assAnswerCloze.php";
01156 foreach ($value["text"] as $index => $textvalue)
01157 {
01158 if (preg_match("/\d+/", $index))
01159 {
01160 $textvalue = str_replace("\,", ",", $textvalue);
01161 array_push($answer_array, new ASS_AnswerCloze($textvalue, 0, $index, $default_state, $type, $name, $shuffle));
01162 }
01163 }
01164 array_push($this->gaps, $answer_array);
01165 }
01166 }
01167 }
01168
01178 function getClozeText() {
01179 return $this->cloze_text;
01180 }
01181
01191 function getStartTag() {
01192 return $this->start_tag;
01193 }
01194
01204 function getEndTag() {
01205 return $this->end_tag;
01206 }
01207
01217 function setStartTag($start_tag = "[gap]") {
01218 $this->start_tag = $start_tag;
01219 }
01220
01221
01231 function setEndTag($end_tag = "[/gap]") {
01232 $this->end_tag = $end_tag;
01233 }
01234
01243 function rebuildClozeText()
01244 {
01245 $close =& $this->createCloseTextArray();
01246 if (count($close))
01247 {
01248 for ($i = 0; $i < count($this->gaps); $i++)
01249 {
01250 $gaptext = $this->getGapTextList($i);
01251 $textparams = preg_split("/(?<!\\\\),/", $gaptext);
01252 $close["gaps"][$i]["text"] = array();
01253 $close["gaps"][$i]["text"]["text"] = $gaptext;
01254 foreach ($textparams as $key => $value)
01255 {
01256 $close["gaps"][$i]["text"][$key] = $value;
01257 }
01258 }
01259 }
01260 $this->createCloseTextFromArray($close);
01261 }
01262
01274 function getGap($index = 0) {
01275 if ($index < 0) return array();
01276 if (count($this->gaps) < 1) return array();
01277 if ($index >= count($this->gaps)) return array();
01278 return $this->gaps[$index];
01279 }
01280
01290 function getGapCount() {
01291 return count($this->gaps);
01292 }
01293
01306 function getGapTextList($index = 0, $separator = ",") {
01307 if ($index < 0) return "";
01308 if (count($this->gaps) < 1) return "";
01309 if ($index >= count($this->gaps)) return "";
01310 $result = array();
01311 foreach ($this->gaps[$index] as $key => $value) {
01312 array_push($result, str_replace(",", "\,", $value->getAnswertext()));
01313 }
01314 return join($separator, $result);
01315 }
01325 function getGapTextCount($index = 0) {
01326 if ($index < 0) return 0;
01327 if (count($this->gaps) < 1) return 0;
01328 if ($index >= count($this->gaps)) return 0;
01329 return count($this->gaps[$index]);
01330 }
01341 function deleteGap($index = 0) {
01342 if ($index < 0) return;
01343 if (count($this->gaps) < 1) return;
01344 if ($index >= count($this->gaps)) return;
01345 $close = $this->createCloseTextArray();
01346 unset($close["gaps"][$index]);
01347 $this->createCloseTextFromArray($close);
01348 unset($this->gaps[$index]);
01349 $this->gaps = array_values($this->gaps);
01350 }
01351
01360 function flushGaps() {
01361 $this->gaps = array();
01362 }
01363
01375 function deleteAnswertextByIndex($gap_index = 0, $answertext_index = 0) {
01376 if ($gap_index < 0) return;
01377 if (count($this->gaps) < 1) return;
01378 if ($gap_index >= count($this->gaps)) return;
01379 $old_text = $this->getGapTextList($gap_index);
01380 if (count($this->gaps[$gap_index]) == 1) {
01381 $this->deleteGap($gap_index);
01382 } else {
01383 $close = $this->createCloseTextArray();
01384 unset($this->gaps[$gap_index][$answertext_index]);
01385 $this->gaps[$gap_index] = array_values($this->gaps[$gap_index]);
01386 unset($close["gaps"][$gap_index]["text"][$answertext_index]);
01387 $this->createCloseTextFromArray($close);
01388 }
01389 }
01390
01403 function setAnswertext($index = 0, $answertext_index = 0, $answertext = "", $add_gaptext=0)
01404 {
01405 $answertext = str_replace("\,", ",", $answertext);
01406 if ($add_gaptext == 1)
01407 {
01408 $arr = $this->gaps[$index][0];
01409 if (strlen($this->gaps[$index][count($this->gaps[$index])-1]->getAnswertext()) != 0)
01410 {
01411 $default_state = 0;
01412 $default_points = 0;
01413 if ($arr->getClozeType() == CLOZE_TEXT)
01414 {
01415 $default_state = 1;
01416 if ($answertext_index > 0) $default_points = $this->gaps[$index][0]->getPoints();
01417 }
01418 include_once "./assessment/classes/class.assAnswerCloze.php";
01419 array_push($this->gaps[$index], new ASS_AnswerCloze($answertext, $default_points, count($this->gaps[$index]),
01420 $default_state, $arr->getClozeType(),
01421 $arr->getName(), $arr->getShuffle()));
01422 $this->rebuildClozeText();
01423 }
01424 return;
01425 }
01426 if ($index < 0) return;
01427 if (count($this->gaps) < 1) return;
01428 if ($index >= count($this->gaps)) return;
01429 if ($answertext_index < 0) return;
01430 if (count($this->gaps[$index]) < 1) return;
01431 if ($answertext_index >= count($this->gaps[$index])) return;
01432
01433
01434 if (strlen($answertext) == 0)
01435 {
01436
01437 $this->deleteAnswertext($index, $this->gaps[$index][$answertext_index]->getAnswertext());
01438 }
01439 else
01440 {
01441 $this->gaps[$index][$answertext_index]->setAnswertext($answertext);
01442 $this->rebuildClozeText();
01443 }
01444 }
01445
01454 function updateAllGapParams()
01455 {
01456 global $lng;
01457 $close = $this->createCloseTextArray();
01458 for ($i = 0; $i < $this->getGapCount(); $i++)
01459 {
01460 $gaptext = $this->getGapTextList($i);
01461 if ($this->gaps[$i][0]->getClozeType() == CLOZE_TEXT)
01462 {
01463 $close["gaps"][$i]["params"]["type"] = "text";
01464 if (array_key_exists("shuffle", $close["gaps"][$i]["params"]))
01465 {
01466 unset($close["gaps"][$i]["params"]["shuffle"]);
01467 }
01468 }
01469 else
01470 {
01471 $close["gaps"][$i]["params"]["type"] = "select";
01472 if ($this->gaps[$i][0]->getShuffle() == 0)
01473 {
01474 $close["gaps"][$i]["params"]["shuffle"] = "no";
01475 }
01476 else
01477 {
01478 $close["gaps"][$i]["params"]["shuffle"] = "yes";
01479 }
01480 }
01481 $name = $this->gaps[$i][0]->getName();
01482 if (!$name)
01483 {
01484 $name = $this->lng->txt("gap") . " " . ($i+1);
01485 }
01486 $close["gaps"][$i]["params"]["name"] = $name;
01487 }
01488 $this->createCloseTextFromArray($close);
01489 }
01490
01501 function setClozeType($index, $cloze_type = CLOZE_TEXT) {
01502 if ($index < 0) return;
01503 if (count($this->gaps) < 1) return;
01504 if ($index >= count($this->gaps)) return;
01505 $close = $this->createCloseTextArray();
01506 foreach ($this->gaps[$index] as $key => $value) {
01507 $this->gaps[$index][$key]->setClozeType($cloze_type);
01508 $this->gaps[$index][$key]->setState(1);
01509 }
01510 if ($cloze_type == CLOZE_TEXT)
01511 {
01512 $type = "text";
01513 }
01514 else
01515 {
01516 $type = "select";
01517 }
01518 $close["gaps"][$index]["type"] = $type;
01519 $this->createCloseTextFromArray($close);
01520 }
01521
01533 function setGapPoints($index = 0, $points = 0.0) {
01534 if ($index < 0) return;
01535 if (count($this->gaps) < 1) return;
01536 if ($index >= count($this->gaps)) return;
01537 foreach ($this->gaps[$index] as $key => $value) {
01538 $this->gaps[$index][$key]->setPoints($points);
01539 }
01540 }
01541
01553 function setGapShuffle($index = 0, $shuffle = 1) {
01554 if ($index < 0) return;
01555 if (count($this->gaps) < 1) return;
01556 if ($index >= count($this->gaps)) return;
01557 foreach ($this->gaps[$index] as $key => $value) {
01558 $this->gaps[$index][$key]->setShuffle($shuffle);
01559 }
01560 }
01561
01562
01575 function setSingleAnswerPoints($index_gaps = 0, $index_answerobject = 0, $points = 0.0) {
01576 if ($index_gaps < 0) return;
01577 if (count($this->gaps) < 1) return;
01578 if ($index_gaps >= count($this->gaps)) return;
01579 if ($index_answerobject < 0) return;
01580 if (count($this->gaps[$index_gaps]) < 1) return;
01581 if ($index_answerobject >= count($this->gaps[$index_gaps])) return;
01582 $this->gaps[$index_gaps][$index_answerobject]->setPoints($points);
01583 }
01584
01597 function setSingleAnswerState($index_gaps = 0, $index_answerobject = 0, $state = 0) {
01598 if ($index_gaps < 0) return;
01599 if (count($this->gaps) < 1) return;
01600 if ($index_gaps >= count($this->gaps)) return;
01601 if ($index_answerobject < 0) return;
01602 if (count($this->gaps[$index_gaps]) < 1) return;
01603 if ($index_answerobject >= count($this->gaps[$index_gaps])) return;
01604 $this->gaps[$index_gaps][$index_answerobject]->setState($state);
01605 }
01606
01618 function getTextgapPoints($a_original, $a_entered, $max_points)
01619 {
01620 $result = 0;
01621 $gaprating = $this->getTextgapRating();
01622 switch ($gaprating)
01623 {
01624 case TEXTGAP_RATING_CASEINSENSITIVE:
01625 if (strcmp(strtolower(utf8_decode($a_original)), strtolower(utf8_decode($a_entered))) == 0) $result = $max_points;
01626 break;
01627 case TEXTGAP_RATING_CASESENSITIVE:
01628 if (strcmp(utf8_decode($a_original), utf8_decode($a_entered)) == 0) $result = $max_points;
01629 break;
01630 case TEXTGAP_RATING_LEVENSHTEIN1:
01631 if (levenshtein(utf8_decode($a_original), utf8_decode($a_entered)) <= 1) $result = $max_points;
01632 break;
01633 case TEXTGAP_RATING_LEVENSHTEIN2:
01634 if (levenshtein(utf8_decode($a_original), utf8_decode($a_entered)) <= 2) $result = $max_points;
01635 break;
01636 case TEXTGAP_RATING_LEVENSHTEIN3:
01637 if (levenshtein(utf8_decode($a_original), utf8_decode($a_entered)) <= 3) $result = $max_points;
01638 break;
01639 case TEXTGAP_RATING_LEVENSHTEIN4:
01640 if (levenshtein(utf8_decode($a_original), utf8_decode($a_entered)) <= 4) $result = $max_points;
01641 break;
01642 case TEXTGAP_RATING_LEVENSHTEIN5:
01643 if (levenshtein(utf8_decode($a_original), utf8_decode($a_entered)) <= 5) $result = $max_points;
01644 break;
01645 }
01646 return $result;
01647 }
01648
01660 function calculateReachedPoints($active_id, $pass = NULL)
01661 {
01662 global $ilDB;
01663
01664 $found_value1 = array();
01665 $found_value2 = array();
01666 if (is_null($pass))
01667 {
01668 $pass = $this->getSolutionMaxPass($active_id);
01669 }
01670 $query = sprintf("SELECT * FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
01671 $ilDB->quote($active_id . ""),
01672 $ilDB->quote($this->getId() . ""),
01673 $ilDB->quote($pass . "")
01674 );
01675 $result = $ilDB->query($query);
01676 $user_result = array();
01677 while ($data = $result->fetchRow(DB_FETCHMODE_OBJECT))
01678 {
01679 if (strcmp($data->value2, "") != 0)
01680 {
01681 $user_result[$data->value1] = array(
01682 "gap_id" => $data->value1,
01683 "value" => $data->value2
01684 );
01685 }
01686 }
01687 $points = 0;
01688 $counter = 0;
01689 foreach ($user_result as $gap_id => $value)
01690 {
01691 if ($this->gaps[$gap_id][0]->getClozeType() == CLOZE_TEXT)
01692 {
01693 $gappoints = 0;
01694 foreach ($this->gaps[$gap_id] as $k => $v)
01695 {
01696 $gotpoints = $this->getTextgapPoints($v->getAnswertext(), $value["value"], $v->getPoints());
01697 if ($gotpoints > $gappoints) $gappoints = $gotpoints;
01698 }
01699 $points += $gappoints;
01700 }
01701 else
01702 {
01703 if ($value["value"] >= 0)
01704 {
01705 foreach ($this->gaps[$gap_id] as $answerkey => $answer)
01706 {
01707 if ($value["value"] == $answerkey)
01708 {
01709 $points += $answer->getPoints();
01710 }
01711 }
01712 }
01713 }
01714 }
01715
01716 $points = parent::calculateReachedPoints($active_id, $pass = NULL, $points);
01717 return $points;
01718 }
01719
01728 function getMaximumPoints() {
01729 $points = 0;
01730 foreach ($this->gaps as $key => $value) {
01731 if ($value[0]->getClozeType() == CLOZE_TEXT)
01732 {
01733 $gap_max_points = 0;
01734 foreach ($value as $key2 => $value2)
01735 {
01736 if ($value2->getPoints() > $gap_max_points)
01737 {
01738 $gap_max_points = $value2->getPoints();
01739 }
01740 }
01741 $points += $gap_max_points;
01742 } else
01743 {
01744 $srpoints = 0;
01745 foreach ($value as $key2 => $value2)
01746 {
01747 if ($value2->getPoints() > $srpoints)
01748 {
01749 $srpoints = $value2->getPoints();
01750 }
01751 }
01752 $points += $srpoints;
01753 }
01754 }
01755 return $points;
01756 }
01757
01768 function saveWorkingData($active_id, $pass = NULL)
01769 {
01770 global $ilDB;
01771 global $ilUser;
01772
01773 include_once "./assessment/classes/class.ilObjTest.php";
01774 $activepass = ilObjTest::_getPass($active_id);
01775
01776 $query = sprintf("DELETE FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
01777 $ilDB->quote($active_id),
01778 $ilDB->quote($this->getId()),
01779 $ilDB->quote($activepass . "")
01780 );
01781 $result = $ilDB->query($query);
01782
01783 $entered_values = 0;
01784 foreach ($_POST as $key => $value) {
01785 if (preg_match("/^gap_(\d+)/", $key, $matches))
01786 {
01787 $value = ilUtil::stripSlashes($value);
01788 if (strlen($value))
01789 {
01790 $gap = $this->getGap($matches[1]);
01791 if (!(($gap[0]->getClozeType() == CLOZE_SELECT) && ($value == -1)))
01792 {
01793 $query = sprintf("INSERT INTO tst_solutions (solution_id, active_fi, question_fi, value1, value2, pass, TIMESTAMP) VALUES (NULL, %s, %s, %s, %s, %s, NULL)",
01794 $ilDB->quote($active_id),
01795 $ilDB->quote($this->getId()),
01796 $ilDB->quote(trim($matches[1])),
01797 $ilDB->quote(trim($value)),
01798 $ilDB->quote($activepass . "")
01799 );
01800 $result = $ilDB->query($query);
01801 $entered_values++;
01802 }
01803 }
01804 }
01805 }
01806 if ($entered_values)
01807 {
01808 include_once ("./classes/class.ilObjAssessmentFolder.php");
01809 if (ilObjAssessmentFolder::_enabledAssessmentLogging())
01810 {
01811 $this->logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
01812 }
01813 }
01814 else
01815 {
01816 include_once ("./classes/class.ilObjAssessmentFolder.php");
01817 if (ilObjAssessmentFolder::_enabledAssessmentLogging())
01818 {
01819 $this->logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
01820 }
01821 }
01822 parent::saveWorkingData($active_id, $pass);
01823 return true;
01824 }
01825
01826 function syncWithOriginal()
01827 {
01828 global $ilDB;
01829
01830 if ($this->original_id)
01831 {
01832 $complete = 0;
01833 if ($this->isComplete())
01834 {
01835 $complete = 1;
01836 }
01837
01838 $estw_time = $this->getEstimatedWorkingTime();
01839 $estw_time = sprintf("%02d:%02d:%02d", $estw_time['h'], $estw_time['m'], $estw_time['s']);
01840
01841 $query = sprintf("UPDATE qpl_questions SET obj_fi = %s, title = %s, comment = %s, points = %s, author = %s, question_text = %s, working_time = %s, complete = %s WHERE question_id = %s",
01842 $ilDB->quote($this->obj_id. ""),
01843 $ilDB->quote($this->title . ""),
01844 $ilDB->quote($this->comment . ""),
01845 $ilDB->quote($this->getMaximumPoints() . ""),
01846 $ilDB->quote($this->author . ""),
01847 $ilDB->quote($this->cloze_text . ""),
01848 $ilDB->quote($estw_time . ""),
01849 $ilDB->quote($complete . ""),
01850 $ilDB->quote($this->original_id . "")
01851 );
01852 $result = $ilDB->query($query);
01853 $query = sprintf("UPDATE qpl_question_cloze SET textgap_rating = %s WHERE question_fi = %s",
01854 $ilDB->quote($this->textgap_rating . ""),
01855 $ilDB->quote($this->original_id . "")
01856 );
01857 $result = $ilDB->query($query);
01858
01859 if ($result == DB_OK)
01860 {
01861
01862
01863 $query = sprintf("DELETE FROM qpl_answer_cloze WHERE question_fi = %s",
01864 $ilDB->quote($this->original_id)
01865 );
01866 $result = $ilDB->query($query);
01867
01868 foreach ($this->gaps as $key => $value)
01869 {
01870 foreach ($value as $answer_id => $answer_obj)
01871 {
01872 $query = sprintf("INSERT INTO qpl_answer_cloze (answer_id, question_fi, gap_id, answertext, points, aorder, cloze_type, name, shuffle, correctness) VALUES (NULL, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
01873 $ilDB->quote($this->original_id . ""),
01874 $ilDB->quote($key . ""),
01875 $ilDB->quote($answer_obj->getAnswertext() . ""),
01876 $ilDB->quote($answer_obj->getPoints() . ""),
01877 $ilDB->quote($answer_obj->getOrder() . ""),
01878 $ilDB->quote($answer_obj->getClozeType() . ""),
01879 $ilDB->quote($answer_obj->getName() . ""),
01880 $ilDB->quote($answer_obj->getShuffle() . ""),
01881 $ilDB->quote($answer_obj->getState() . "")
01882 );
01883 $answer_result = $ilDB->query($query);
01884 }
01885 }
01886 }
01887 parent::syncWithOriginal();
01888 }
01889 }
01890
01899 function getQuestionType()
01900 {
01901 return 3;
01902 }
01903
01912 function getColumnSize($gap)
01913 {
01914 $size = 0;
01915 foreach ($gap as $answer)
01916 {
01917 include_once "./classes/class.ilStr.php";
01918 $answertextsize = ilStr::strLen($answer->getAnswertext());
01919 if ($answertextsize > $size) $size = $answertextsize;
01920 }
01921 return $size;
01922 }
01923
01933 function getTextgapRating()
01934 {
01935 return $this->textgap_rating;
01936 }
01937
01947 function setTextgapRating($a_textgap_rating)
01948 {
01949 switch ($a_textgap_rating)
01950 {
01951 case TEXTGAP_RATING_CASEINSENSITIVE:
01952 case TEXTGAP_RATING_CASESENSITIVE:
01953 case TEXTGAP_RATING_LEVENSHTEIN1:
01954 case TEXTGAP_RATING_LEVENSHTEIN2:
01955 case TEXTGAP_RATING_LEVENSHTEIN3:
01956 case TEXTGAP_RATING_LEVENSHTEIN4:
01957 case TEXTGAP_RATING_LEVENSHTEIN5:
01958 $this->textgap_rating = $a_textgap_rating;
01959 break;
01960 default:
01961 $this->textgap_rating = TEXTGAP_RATING_CASEINSENSITIVE;
01962 break;
01963 }
01964 }
01965
01974 function getAdditionalTableName()
01975 {
01976 return "qpl_question_cloze";
01977 }
01978
01987 function getAnswerTableName()
01988 {
01989 return "qpl_answer_cloze";
01990 }
01991
02002 function testGapSolution($value, $gap)
02003 {
02004 if (strlen($value) == 0) return FALSE;
02005 $max_points = 0;
02006 foreach ($gap as $answer)
02007 {
02008 if ($answer->getPoints() > $max_points) $max_points = $answer->getPoints();
02009 }
02010 if ($gap[0]->getClozeType() == CLOZE_SELECT)
02011 {
02012 $positive = FALSE;
02013 if ($gap[$value]->getPoints() > 0)
02014 {
02015 $positive = TRUE;
02016 }
02017 if ($max_points == $gap[$value]->getPoints())
02018 {
02019 return array("best" => TRUE, "positive" => $positive);
02020 }
02021 else
02022 {
02023 return array("best" => FALSE, "positive" => $positive);
02024 }
02025 }
02026 else
02027 {
02028 $gappoints = 0;
02029 $max_points = 0;
02030 foreach ($gap as $k => $v)
02031 {
02032 $gotpoints = $this->getTextgapPoints($v->getAnswertext(), $value, $v->getPoints());
02033 if ($gotpoints > $gappoints) $gappoints = $gotpoints;
02034 if ($v->getPoints() > $max_points) $max_points = $v->getPoints();
02035 }
02036 $positive = FALSE;
02037 if ($gappoints > 0)
02038 {
02039 $positive = TRUE;
02040 }
02041 if ($gappoints == $max_points)
02042 {
02043 return array("best" => TRUE, "positive" => $positive);
02044 }
02045 else
02046 {
02047 return array("best" => FALSE, "positive" => $positive);
02048 }
02049 }
02050 }
02051
02062 function getMaximumGapPoints($gap_id)
02063 {
02064 $points = 0;
02065 foreach ($this->gaps as $key => $value)
02066 {
02067 if ($key == $gap_id)
02068 {
02069 if ($value[0]->getClozeType() == CLOZE_TEXT)
02070 {
02071 $gap_max_points = 0;
02072 foreach ($value as $key2 => $value2)
02073 {
02074 if ($value2->getPoints() > $gap_max_points)
02075 {
02076 $gap_max_points = $value2->getPoints();
02077 }
02078 }
02079 $points += $gap_max_points;
02080 } else
02081 {
02082 $srpoints = 0;
02083 foreach ($value as $key2 => $value2)
02084 {
02085 if ($value2->getPoints() > $srpoints)
02086 {
02087 $srpoints = $value2->getPoints();
02088 }
02089 }
02090 $points += $srpoints;
02091 }
02092 }
02093 }
02094 return $points;
02095 }
02096
02097 }
02098
02099 ?>