Чего не хватает в модуле обучения, так это настроек
Например нельзя сделать вопросы не по одному, а несколько на страницу, или все на страницу.
У меня была цель, сделать тесты со всеми вопросами на странице. Ниже я приведу реализацию.
[spoiler]
Исходные данные:
- список всех вопросов на 1 странице (легко можно разбить на несколько);
- нет ограничений на попытки;
- нет ограничений на время;
- случайные порядок вопросов;
- случайный порядок ответов.
bitrix:learning.student.gradebook / template.php
Изменены ссылки, убрана колонка время (не актуальна)
bitrix:learning.test.list / template.php
изменён под нужды, убраны не нужные данные
ЗЫ: остаётся пожелать, что бы сделали подсветку синтаксиса!
ЗЗЫ: и хочется спросить, когда будет БитриксСтор для модулей?
Например нельзя сделать вопросы не по одному, а несколько на страницу, или все на страницу.
У меня была цель, сделать тесты со всеми вопросами на странице. Ниже я приведу реализацию.
[spoiler]
Исходные данные:
- список всех вопросов на 1 странице (легко можно разбить на несколько);
- нет ограничений на попытки;
- нет ограничений на время;
- случайные порядок вопросов;
- случайный порядок ответов.
<? // Тесты определённо должен сдавать авторизированный пользователь. // Если это не так, вот эта форма попросит логин-пароль/регистрацию if (!$USER->IsAuthorized()) { echo "<h2>For passing tests u must register or authorize</h2><br/>"; $APPLICATION->AuthForm(GetMessage("LEARNING_NO_AUTHORIZE")); }?> <? // Тест будем проходить только когда указан АйДи теста, // подключен модуль Обучения и мы не выводим результаты if (CModule::IncludeModule("learning") && !isset($_GET["result"]) && isset($_GET["TEST_ID"])): // Функция скопирована верно с компонента learning.test // Она проверяет не начинал ли пользователь этот тест рание if (!function_exists("_AttemptExists")) { function _AttemptExists($testID, $attemptID = false){ $arFields = Array( "STUDENT_ID" => intval($GLOBALS["USER"]->GetID()), "TEST_ID" => $testID, "STATUS"=>"B", ); if ($attemptID !== false) $arFields["ID"] = $attemptID; $rsAttempt = CTestAttempt::GetList(Array(), $arFields); return ($rsAttempt->GetNext()); } } // АйДи теста это определённо число $TEST_ID = intval($_GET["TEST_ID"]); // Если нету результатов теста, и кнопка Done не нажималась, // генерируем тест if (!$_POST["qest"] && !$_POST["done"]): $i = 0; // Делаем проверочку на старую попытку if ($arAttempt = _AttemptExists($TEST_ID)){ // Функция вернула Объект попытки, из него мы выдрали АйДи ;) $attemptID = $arAttempt["ID"]; } else { // Нету старых попыток, создаём $rsAttempt = new CTestAttempt(); // Указываем АйДи теста, АйДи юзверя, и статус (читаем мануал) $attemptID = $rsAttempt->Add(Array("TEST_ID" => $TEST_ID, "STUDENT_ID" => $USER->GetID(), "STATUS" => "B")); // Дальше генерируем список вопросов для этого теста CTestAttempt::CreateAttemptQuestions($attemptID); } // Предзакажем два массива для списка вопросов и списка ответов $qlist = $alist = array(); // Получаем по АйДи попытки, список вопросов $res = CTestResult::GetList(Array("ID"=>"ASC"), Array("ATTEMPT_ID" => $attemptID)); // Загоняем их в массивчик qlist, и шафлим(то есть мешаем) while ($arQuestion = $res->GetNext()) $qlist[] = $arQuestion; shuffle($qlist); // выводим заголовок формы, туда пихаем АйДи попытки echo "<form action='?TEST_ID=$TEST_ID' method='post'> <input type='hidden' name='ATTEMPT_ID' value='$attemptID'/>\n"; // Дальше выводим вопросы, параллельно считая их foreach ($qlist as $arQuestion){$i++; echo $i.") ".$arQuestion["QUESTION_NAME"]."<br>"; $QUESTION_ID = $arQuestion["QUESTION_ID"]; // Дальше получаем список ответов на наш вопрос $resA = CLAnswer::GetList( Array("SORT"=>"DESC"), Array("QUESTION_ID" => $QUESTION_ID) ); // Если вопрос по каким-то причинам отвечен, // мы его закрасим серым, и сделаем дисаблед варианты ответов echo "<blockquote ".($arQuestion["ANSWERED"] == Y ? "style='background-color: #ddd;'" : "").">\n"; // Получаем список ответов в alist и шафлим while ($arAnswer = $resA->GetNext()) $alist[] = $arAnswer; shuffle($alist); // выводим ответы // возможно несколько вариантов (radio или checkbox) foreach ($alist as $arAnswer){ echo "<label><input type='".($arQuestion["QUESTION_TYPE"]=="M"?"checkbox":"radio")."' name='qest[".$arQuestion["QUESTION_ID"]."]".($arQuestion["QUESTION_TYPE"]=="M"?"[".$arAnswer["ID"]."]":"")."' value='".$arAnswer["ID"]."' ".($arQuestion["ANSWERED"] == Y ? "DISABLED" : "")."/>".$arAnswer["ANSWER"]."</label><br>\n"; } echo "</blockquote>\n"; // обнуляем массив ответов $alist = array(); } // выводим кнопку и закрываем форму echo "<input type='Submit' name='done' value='done' /></form>"; // удаляем переменные, что бы не текло :) unset($arAnswer, $arQuestion, $res, $resA, $qlist, $alist); // Тест готов else: // После нажатия на кпопку Done мы попадаём сюда %) // Получаем массив АйДишников вопросов, на которые отвечали юзвери $QUESTION_ID = array_keys($_POST["qest"]); $TEST_ID = intval($_GET["TEST_ID"]); $attemptID = intval($_POST["ATTEMPT_ID"]); // Получаем список правельных ответов на наши вопросы $res = CLAnswer::GetList( Array("SORT"=>"DESC"), Array("QUESTION_ID" => $QUESTION_ID) ); // Дальше проверяем наши вопросы $ra = $an = $chk = array(); while ($arAnswer = $res->GetNext()) { $an[$arAnswer["ID"]] = $arAnswer["ANSWER"]; if ($arAnswer["CORRECT"] == "Y") $ra[$arAnswer["QUESTION_ID"]][$arAnswer["ID"]] = $arAnswer["ANSWER"]; } // Теперь составим булевый массив с АйДишками вопрсов // что равны 0/1 если ответ на вопрос правильный foreach ($_POST["qest"] as $key => $value){ if (is_array($value)) { $chk[$key] = 1; foreach ($ra[$key] as $k_ey => $v_alue) { if (!isset($value[$k_ey])) $chk[$key] = 0; } } else $chk[$key] = isset($ra[$key][$value]) ? 1 : 0; } // сохраняем ответы пользователей // Получаем список вопросов по Айди попытки $rsTestResult = CTestResult::GetList(Array("ID"=>"ASC"), Array("ATTEMPT_ID" => $attemptID)); while ($arQuestionPlan = $rsTestResult->GetNext()){ $questionID = $arQuestionPlan["QUESTION_ID"]; $qattemptID = $arQuestionPlan["ID"]; $answerRs = $_POST["qest"][$questionID]; $correct = $chk[$questionID] ? "Y" : "N"; // Если вопрос не отвечен, даем ответ // тут проверка, балы и прочие проходит автоматически if ($arQuestionPlan["ANSWERED"] == "N") { $res = CTestResult::AddResponse(intval($qattemptID), $answerRs); } else { // если на ответе отвечали, мы его обновляем (по желанию) // тут нам пригодился булевый массив ответов $crct = new CTestResult; $res = $crct->Update($qattemptID, array("ANSWER" => $answerRs, "CORRECT" => $correct, "FEEDBACK" => "Обновлён")); } } // проверяем состояние (вот здесь можно реализовать постраничный вывод) $arProgress = CTestResult::GetProgress($attemptID); // Если не отвеченных не осталось, делаем финиш if($arProgress["TODO"]==0){ $rsTestAttempt = new CTestAttempt; $rsTestAttempt->AttemptFinished($attemptID); } else {echo "Test N $attemptID not over. Some errors happend. Mail to us test number and we check. Good Luck ;)";} // убиваем созданные переменные, чистим после себя ;) unset($res, $rsAttempt, $rsTestAttempt); // уходим на страницу с результатами ;) LocalRedirect("?result=show&TEST_ID=$TEST_ID"); endif; endif; ?> <?php if ($_GET["result"] == "show"): // Для вывода воспользуемся стандартным компонентом, // но немного измененным (листинг будет ниже) $APPLICATION->IncludeComponent("bitrix:learning.student.gradebook", "", Array( "TEST_DETAIL_TEMPLATE" => "", "COURSE_DETAIL_TEMPLATE" => "", "TEST_ID_VARIABLE" => $_GET["TEST_ID"], "SET_TITLE" => "N" ), array() ); endif; ?> <?php if (!isset($_GET["result"]) && !isset($_GET["TEST_ID"])): // Если ничего нету, выводим список доступных тестов // Сначала берем список курсов $res = CCourse::GetList(Array("SORT"=>"ASC"),Array()); while ($arCourse = $res->GetNext()) { // а потом в цикле, с помощью стандартного компонента, // выводим список тестов (тоже видоизменён под нужды, // листинг ниже) $APPLICATION->IncludeComponent("bitrix:learning.test.list", "", Array( "COURSE_ID" => $arCourse["ID"], "TEST_DETAIL_TEMPLATE" => "", "SET_TITLE" => "N", "TESTS_PER_PAGE" => "100", ), array() ); } endif; ?> |
bitrix:learning.student.gradebook / template.php
Изменены ссылки, убрана колонка время (не актуальна)
<?if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();?> <table class="dpinfo"> <tr> <th><?=GetMessage("LEARNING_PROFILE_COURSE")?></th> <th><?=GetMessage("LEARNING_PROFILE_TEST")?></th> <th><?=GetMessage("LEARNING_PROFILE_SCORE")?></th> <th><?=GetMessage("LEARNING_PROFILE_RESULT")?></th> <th><?=GetMessage("LEARNING_PROFILE_ATTEMPTS")?></th> <th><?=GetMessage("LEARNING_PROFILE_ACTION")?></th> </tr> <?if (!empty($arResult["RECORDS"])):?> <?foreach($arResult["RECORDS"] as $arGradebook):?> <tr> <td><a href="./?COURSE_ID=<?=$arGradebook["COURSE_ID"]?>"><?=$arGradebook["COURSE_NAME"]?></a></td> <td><a href="./?TEST_ID=<?=$arGradebook["TEST_ID"]?>"><?=$arGradebook["TEST_NAME"]?></a></td> <td><?=$arGradebook["RESULT"]?><?=(intval($arGradebook["MAX_RESULT"]) > 0 ? " / ".intval($arGradebook["MAX_RESULT"]) : "")?></td> <td><?=$arGradebook["COMPLETED"]=="Y"?GetMessage("LEARNING_PROFILE_YES"):GetMessage("LEARNING_PROFILE_NO")?></td> <td> <a title="<?=GetMessage("LEARNING_PROFILE_TEST_DETAIL")?>" href="<?=$arGradebook["ATTEMPT_DETAIL_URL"]?>"><?=$arGradebook["ATTEMPTS"]?></a> <?if ($arGradebook["ATTEMPT_LIMIT"]>0):?> / <?=$arGradebook["ATTEMPT_LIMIT"]?> <?endif?> </td> <td><a href="?TEST_ID=<?=$arGradebook["COURSE_ID"] ?>"><?=GetMessage("LEARNING_PROFILE_TRY")?></a></td> </tr> <?endforeach?> <?else:?> <tr> <td colspan="6">- <?=GetMessage("LEARNING_PROFILE_NO_DATA")?> -</td> </tr> <?endif?> </table> <?if (!empty($arResult["ATTEMPTS"])):?> <br /><b><?=GetMessage("LEARNING_ATTEMPTS_TITLE")?></b><br /><br /> <table class="dpinfo"> <tr> <th><?=GetMessage("LEARNING_PROFILE_DATE_END")?></th> <th><?=GetMessage("LEARNING_PROFILE_QUESTIONS")?></th> <th><?=GetMessage("LEARNING_PROFILE_SCORE")?></th> <th><?=GetMessage("LEARNING_PROFILE_RESULT")?></th> </tr> <?foreach ($arResult["ATTEMPTS"] as $arAttempt):?> <tr> <?if (strlen($arAttempt["DATE_END"])>0):?> <td><?=$arAttempt["DATE_END"]?></td> <?else:?> <td><?=$arAttempt["DATE_START"]?><br> <?=GetMessage("LEARNING_ATTEMPT_NOT_FINISHED")?> </td> <?endif?> <td><?=$arAttempt["QUESTIONS"]?></td> <td><?=$arAttempt["SCORE"]?><?=(intval($arAttempt["MAX_SCORE"]) > 0 ? " / ".intval($arAttempt["MAX_SCORE"]) : "")?></td> <td><?=$arAttempt["COMPLETED"]=="Y"?GetMessage("LEARNING_PROFILE_YES"):GetMessage("LEARNING_PROFILE_NO")?></td> </tr> <?endforeach?> </table> <br /> <a href="./?result=show"><?=GetMessage("LEARNING_BACK_TO_GRADEBOOK")?></a> <?endif;?> |
bitrix:learning.test.list / template.php
изменён под нужды, убраны не нужные данные
<?if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die(); $i=0?> <?ShowError($arResult["ERROR_MESSAGE"]);?> <?if ($arResult["TESTS_COUNT"] > 0):?> <?foreach ($arResult["TESTS"] as $arTest):?> <? if($i > 0): ?> <div class="test-list-hr"></div><? endif ?> <h2> <?=$arTest["NAME"];?> </h2><br /> <?if (strlen($arTest["DESCRIPTION"]) > 0):?> <span class="tdescr"><?=$arTest["DESCRIPTION"]?></span> <br /> <?endif?> <form action="?" method="get"> <input type="hidden" name="TEST_ID" value="<?=$arTest["ID"]?>" /> <input type="image" src="/imgs/startbut.png" name="start" value="Y" class="startbutt"/> </form> <br clear="all"/> <br /> <? $i++; endforeach ?> <?=$arResult["NAV_STRING"];?> <?endif?> |
ЗЫ: остаётся пожелать, что бы сделали подсветку синтаксиса!
ЗЗЫ: и хочется спросить, когда будет БитриксСтор для модулей?