Exercise: Improve and fix questions pool

pull/5235/head
christianbeeznst 9 months ago
parent 6d35a37ce3
commit 684981bd9f
  1. 343
      public/main/exercise/question_pool.php
  2. 139
      public/main/inc/lib/document.lib.php
  3. 39
      public/main/inc/lib/exercise.lib.php

@ -3,6 +3,8 @@
use Chamilo\CoreBundle\Entity\ExtraField as ExtraFieldEntity;
use Chamilo\CoreBundle\Framework\Container;
use Chamilo\CourseBundle\Entity\CQuizQuestion;
use Chamilo\CourseBundle\Entity\CQuizRelQuestion;
use ChamiloSession as Session;
use Knp\Component\Pager\Paginator;
use Chamilo\CoreBundle\Component\Utils\ActionIcon;
@ -576,8 +578,8 @@ function getQuestions(
$length,
$exerciseId,
$courseCategoryId,
$selected_course,
$session_id,
$selectedCourse,
$sessionId,
$exerciseLevel,
$answerType,
$questionId,
@ -585,254 +587,84 @@ function getQuestions(
$fromExercise = 0,
$formValues = []
) {
$start = (int) $start;
$length = (int) $length;
$exerciseId = (int) $exerciseId;
$courseCategoryId = (int) $courseCategoryId;
$selected_course = (int) $selected_course;
$session_id = (int) $session_id;
$exerciseLevel = (int) $exerciseLevel;
$answerType = (int) $answerType;
$questionId = (int) $questionId;
$fromExercise = (int) $fromExercise;
$description = Database::escape_string($description);
$entityManager = Database::getManager();
$qb = $entityManager->createQueryBuilder();
$TBL_EXERCISE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
$TBL_EXERCISES = Database::get_course_table(TABLE_QUIZ_TEST);
$TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION);
$TBL_COURSE_REL_CATEGORY = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY);
$qb->select('qq', 'IDENTITY(qr.quiz) as exerciseId')
->from(CQuizQuestion::class, 'qq')
->leftJoin('qq.relQuizzes', 'qr');
$currentExerciseCondition = '';
if (!empty($fromExercise)) {
$currentCourseId = api_get_course_int_id();
$currentExerciseCondition = "
AND qu.iid NOT IN (
SELECT question_id FROM $TBL_EXERCISE_QUESTION
WHERE quiz_id = $fromExercise AND c_id = $currentCourseId
)";
}
// if we have selected an exercise in the list-box 'Filter'
if ($exerciseId > 0) {
$efConditions = getExtraFieldConditions($formValues, 'from');
$where = '';
$from = '';
if (isset($courseCategoryId) && $courseCategoryId > 0) {
$from = ", $TBL_COURSE_REL_CATEGORY crc ";
$where .= " AND
crc.c_id = $selected_course AND
crc.question_id = qu.iid AND
crc.category_id = $courseCategoryId";
}
if (isset($exerciseLevel) && -1 != $exerciseLevel) {
$where .= ' AND level='.$exerciseLevel;
}
if (isset($answerType) && $answerType > 0) {
$where .= ' AND type='.$answerType;
}
if (!empty($questionId)) {
$where .= ' AND qu.iid='.$questionId;
}
if (!empty($description)) {
$where .= " AND qu.description LIKE '%$description%'";
}
$select = 'DISTINCT
iid,
question,
type,
level,
qt.quiz_id exerciseId';
if ($getCount) {
$select = 'count(qu.iid) as count';
}
$sql = "SELECT $select
FROM
$TBL_EXERCISE_QUESTION qt
INNER JOIN $TBL_QUESTIONS qu
ON qt.question_id = qu.iid
$from
{$efConditions['from']}
WHERE
qt.quiz_id = $exerciseId AND
qt.c_id = $selected_course AND
qu.c_id = $selected_course
$where
$currentExerciseCondition
{$efConditions['where']}
ORDER BY BINARY qu.question ASC
";
} elseif (-1 == $exerciseId) {
$efConditions = getExtraFieldConditions($formValues, 'join');
// If we have selected the option 'Orphan questions' in the list-box 'Filter'
$level_where = '';
$from = '';
if (isset($courseCategoryId) && $courseCategoryId > 0) {
$from = " INNER JOIN $TBL_COURSE_REL_CATEGORY crc
ON crc.question_id = q.iid AND crc.c_id = q.c_id ";
$level_where .= " AND
crc.c_id = $selected_course AND
crc.category_id = $courseCategoryId";
}
if (isset($exerciseLevel) && -1 != $exerciseLevel) {
$level_where = ' AND level='.$exerciseLevel;
}
$answer_where = '';
if (isset($answerType) && $answerType > 0 - 1) {
$answer_where = ' AND qu.type='.$answerType;
}
if (!empty($questionId)) {
$answer_where .= ' AND q.iid='.$questionId;
}
if (!empty($description)) {
$answer_where .= " AND q.description LIKE '%$description%'";
}
$qb->andWhere('qr.quiz = :exerciseId')
->setParameter('exerciseId', $exerciseId);
} elseif ($exerciseId == -1) {
$qb->andWhere($qb->expr()->isNull('qr.quiz'));
}
$select = ' qu.*, r.quiz_id exerciseId ';
if ($getCount) {
$select = 'count(qu.iid) as count';
}
if ($courseCategoryId > 0) {
$qb->join('qq.categories', 'qc')
->andWhere('qc.id = :categoryId')
->setParameter('categoryId', $courseCategoryId);
}
// @todo fix this query with the new id field
$sql = " (
SELECT $select
FROM $TBL_QUESTIONS qu
INNER JOIN $TBL_EXERCISE_QUESTION r
ON (qu.c_id = r.c_id AND qu.iid = r.question_id)
INNER JOIN $TBL_EXERCISES ex
ON (ex.iid = r.quiz_id AND ex.c_id = r.c_id)
$from
{$efConditions['from']}
WHERE
ex.c_id = '$selected_course' AND
ex.active = '-1'
$level_where
$answer_where
{$efConditions['where']}
)
UNION
(
SELECT $select
FROM $TBL_QUESTIONS qu
LEFT OUTER JOIN $TBL_EXERCISE_QUESTION r
ON (qu.c_id = r.c_id AND qu.iid = r.question_id)
$from
{$efConditions['from']}
WHERE
qu.c_id = '$selected_course' AND
r.question_id is null
$level_where
$answer_where
{$efConditions['where']}
)
UNION
(
SELECT $select
FROM $TBL_QUESTIONS qu
INNER JOIN $TBL_EXERCISE_QUESTION r
ON (qu.c_id = r.c_id AND qu.iid = r.question_id)
$from
{$efConditions['from']}
WHERE
r.c_id = '$selected_course' AND
(r.quiz_id = '-1' OR r.quiz_id = '0')
$level_where
$answer_where
{$efConditions['where']}
)
";
if ($getCount) {
$sql = "SELECT SUM(count) count FROM ($sql) as total";
}
} else {
$efConditions = getExtraFieldConditions($formValues, 'from');
// All tests for selected course
// If we have not selected any option in the list-box 'Filter'
$filter = '';
$from = '';
if (isset($courseCategoryId) && $courseCategoryId > 0) {
$from = ", $TBL_COURSE_REL_CATEGORY crc ";
$filter .= " AND
crc.c_id = $selected_course AND
crc.question_id = qu.iid AND
crc.category_id = $courseCategoryId";
}
if (isset($exerciseLevel) && -1 != $exerciseLevel) {
$filter .= ' AND level='.$exerciseLevel.' ';
}
if (isset($answerType) && $answerType > 0) {
$filter .= ' AND qu.type='.$answerType.' ';
}
if ($exerciseLevel !== null && $exerciseLevel != -1) {
$qb->andWhere('qq.level = :level')
->setParameter('level', $exerciseLevel);
}
if (!empty($questionId)) {
$filter .= ' AND qu.iid='.$questionId;
}
if ($answerType !== null && $answerType > 0) {
$qb->andWhere('qq.type = :type')
->setParameter('type', $answerType);
}
if (!empty($description)) {
$filter .= " AND qu.description LIKE '%$description%'";
}
if (!empty($questionId)) {
$qb->andWhere('qq.iid = :questionId')
->setParameter('questionId', $questionId);
}
if (-1 == $session_id || empty($session_id)) {
$session_id = 0;
}
$sessionCondition = api_get_session_condition($session_id, true, 'q.session_id');
if (!empty($description)) {
$qb->andWhere('qq.description LIKE :description')
->setParameter('description', '%' . $description . '%');
}
$select = 'qu.iid, question, qu.type, level, q.session_id, qt.quiz_id exerciseId ';
if ($getCount) {
$select = 'count(qu.iid) as count';
}
if (!empty($fromExercise)) {
$subQb = $entityManager->createQueryBuilder();
$subQb->select('IDENTITY(sq.question)')
->from(CQuizRelQuestion::class, 'sq')
->where('sq.quiz = :fromExercise');
// All tests for the course selected, not in session
$sql = "SELECT DISTINCT
$select
FROM
$TBL_QUESTIONS as qu
INNER JOIN $TBL_EXERCISE_QUESTION as qt
ON (qu.iid = qt.question_id AND qu.c_id = qt.c_id)
INNER JOIN $TBL_EXERCISES as q
ON (q.c_id = qu.c_id AND q.iid = qt.quiz_id)
{$efConditions['from']}
$from
WHERE
qu.c_id = $selected_course AND
qt.c_id = $selected_course AND
q.c_id = $selected_course
$sessionCondition
$filter
$currentExerciseCondition
{$efConditions['where']}
GROUP BY qu.iid
ORDER BY BINARY qu.question ASC
";
$qb->andWhere($qb->expr()->notIn('qq.iid', $subQb->getDQL()))
->setParameter('fromExercise', $fromExercise);
}
if ($getCount) {
$result = Database::query($sql);
$row = Database::fetch_array($result, 'ASSOC');
if ($row) {
return (int) $row['count'];
$qb->select('COUNT(qq.iid)');
try {
return (int) $qb->getQuery()->getSingleScalarResult();
} catch (\Doctrine\ORM\NoResultException $e) {
return 0;
}
return 0;
}
$sql .= " LIMIT $start, $length";
$result = Database::query($sql);
$mainQuestionList = [];
while ($row = Database::fetch_array($result, 'ASSOC')) {
if (-1 == $exerciseId && isQuestionInActiveQuiz($row['iid'])) {
continue;
} else {
$qb->select('qq.iid as id', 'qq.question', 'qq.type', 'qq.level', 'IDENTITY(qr.quiz) as exerciseId')
->setFirstResult($start)
->setMaxResults($length);
$results = $qb->getQuery()->getArrayResult();
$questions = [];
foreach ($results as $result) {
$question = [
'iid' => $result['id'],
'question' => $result['question'],
'type' => $result['type'],
'level' => $result['level'],
'exerciseId' => $result['exerciseId'],
];
$questions[] = $question;
}
$mainQuestionList[] = $row;
return $questions;
}
return $mainQuestionList;
}
$formValues = $form->validate() ? $form->exportValues() : [];
@ -853,11 +685,11 @@ $nbrQuestions = getQuestions(
$formValues
);
$length = api_get_setting('exercise.question_pagination_length');
$length = intval(api_get_setting('exercise.question_pagination_length'));
if (empty($length)) {
$length = 20;
}
$page = intval($page);
$start = ($page - 1) * $length;
$mainQuestionList = getQuestions(
@ -883,19 +715,36 @@ $pagination->setTotalItemCount($nbrQuestions);
$pagination->setItemNumberPerPage($length);
$pagination->setCurrentPageNumber($page);
$pagination->renderer = function ($data) use ($url) {
$render = '';
if ($data['pageCount'] > 1) {
$render = '<ul class="pagination">';
for ($i = 1; $i <= $data['pageCount']; $i++) {
$pageContent = '<li><a href="'.$url.'&page='.$i.'">'.$i.'</a></li>';
if ($data['current'] == $i) {
$pageContent = '<li class="active"><a href="#" >'.$i.'</a></li>';
}
$render .= $pageContent;
}
$render .= '</ul>';
$render = '<nav aria-label="Page navigation" style="display: flex; justify-content: center;">';
$render .= '<ul class="pagination">';
if ($data['current'] > 1) {
$render .= '<li class="page-item"><a class="page-link" href="'.$url.'&page=1" aria-label="First"><span aria-hidden="true">&laquo;&laquo;</span></a></li>';
}
if ($data['current'] > 1) {
$prevPage = $data['current'] - 1;
$render .= '<li class="page-item"><a class="page-link" href="'.$url.'&page='.$prevPage.'" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>';
}
$startPage = max(1, $data['current'] - 2);
$endPage = min($data['pageCount'], $data['current'] + 2);
for ($i = $startPage; $i <= $endPage; $i++) {
$isActive = $data['current'] == $i ? ' active' : '';
$render .= '<li class="page-item'.$isActive.'"><a class="page-link" href="'.$url.'&page='.$i.'">'.$i.'</a></li>';
}
if ($data['current'] < $data['pageCount']) {
$nextPage = $data['current'] + 1;
$render .= '<li class="page-item"><a class="page-link" href="'.$url.'&page='.$nextPage.'" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>';
}
if ($data['current'] < $data['pageCount']) {
$render .= '<li class="page-item"><a class="page-link" href="'.$url.'&page='.$data['pageCount'].'" aria-label="Last"><span aria-hidden="true">&raquo;&raquo;</span></a></li>';
}
$render .= '</ul></nav>';
return $render;
};
@ -1051,7 +900,9 @@ echo '<script>$(function () {
<div class="clear"></div>
<?php
echo '<div class="text-center">';
echo $pagination;
echo '</div>';
$tableId = 'question_pool_id';
echo '<form id="'.$tableId.'" method="get" action="'.$url.'">';
echo '<input type="hidden" name="fromExercise" value="'.$fromExercise.'">';

@ -1986,124 +1986,22 @@ class DocumentManager
$orig_source_html = self::get_resources_from_source_html($content_html);
$orig_course_info = api_get_course_info($origin_course_code);
// Course does not exist in the current DB probably this came from a zip file?
if (empty($orig_course_info)) {
if (!empty($origin_course_path_from_zip)) {
$orig_course_path = $origin_course_path_from_zip.'/';
$orig_course_info_path = $origin_course_info_path;
}
} else {
$orig_course_path = api_get_path(SYS_COURSE_PATH).$orig_course_info['path'].'/';
$orig_course_info_path = $orig_course_info['path'];
}
$destination_course_code = CourseManager::getCourseCodeFromDirectory($destination_course_directory);
$destination_course_info = api_get_course_info($destination_course_code);
$dest_course_path = api_get_path(SYS_COURSE_PATH).$destination_course_directory.'/';
$dest_course_path_rel = api_get_path(REL_COURSE_PATH).$destination_course_directory.'/';
$user_id = api_get_user_id();
if (!empty($orig_source_html)) {
foreach ($orig_source_html as $source) {
// Get information about source url
$real_orig_url = $source[0]; // url
$scope_url = $source[1]; // scope (local, remote)
$type_url = $source[2]; // type (rel, abs, url)
// Get path and query from origin url
$orig_parse_url = parse_url($real_orig_url);
$real_orig_path = isset($orig_parse_url['path']) ? $orig_parse_url['path'] : null;
$real_orig_query = isset($orig_parse_url['query']) ? $orig_parse_url['query'] : null;
// Replace origin course code by destination course code from origin url query
$dest_url_query = '';
if (!empty($real_orig_query)) {
$dest_url_query = '?'.$real_orig_query;
if (false !== strpos($dest_url_query, $origin_course_code)) {
$dest_url_query = str_replace($origin_course_code, $destination_course_code, $dest_url_query);
}
}
if ('local' == $scope_url) {
if ('abs' == $type_url || 'rel' == $type_url) {
$document_file = strstr($real_orig_path, 'document');
if (false !== strpos($real_orig_path, $document_file)) {
$origin_filepath = $orig_course_path.$document_file;
$destination_filepath = $dest_course_path.$document_file;
// copy origin file inside destination course
if (file_exists($origin_filepath)) {
$filepath_dir = dirname($destination_filepath);
if (!is_dir($filepath_dir)) {
$perm = api_get_permissions_for_new_directories();
$result = @mkdir($filepath_dir, $perm, true);
if ($result) {
$filepath_to_add = str_replace(
[$dest_course_path, 'document'],
'',
$filepath_dir
);
// Add to item properties to the new folder
self::addDocument(
$destination_course_info,
$filepath_to_add,
'folder',
0,
basename($filepath_to_add)
);
}
}
if (!file_exists($destination_filepath)) {
$result = @copy($origin_filepath, $destination_filepath);
if ($result) {
$filepath_to_add = str_replace(
[$dest_course_path, 'document'],
'',
$destination_filepath
);
$size = filesize($destination_filepath);
// Add to item properties to the file
self::addDocument(
$destination_course_info,
$filepath_to_add,
'file',
$size,
basename($filepath_to_add)
);
}
}
}
$real_orig_url = $source[0];
$scope_url = $source[1];
$type_url = $source[2];
// Replace origin course path by destination course path.
if (false !== strpos($content_html, $real_orig_url)) {
$url_course_path = str_replace(
$orig_course_info_path.'/'.$document_file,
'',
$real_orig_path
);
// See BT#7780
$destination_url = $dest_course_path_rel.$document_file.$dest_url_query;
// If the course code doesn't exist in the path? what we do? Nothing! see BT#1985
if (false === strpos($real_orig_path, $origin_course_code)) {
$url_course_path = $real_orig_path;
$destination_url = $real_orig_path;
}
$content_html = str_replace($real_orig_url, $destination_url, $content_html);
}
}
if ('local' === $scope_url) {
$document_file = strstr($real_orig_url, 'document');
// replace origin course code by destination course code from origin url
if (0 === strpos($real_orig_url, '?')) {
$dest_url = str_replace($origin_course_code, $destination_course_code, $real_orig_url);
$content_html = str_replace($real_orig_url, $dest_url, $content_html);
}
if (false !== $document_file) {
$new_url = self::generateNewUrlForCourseResource($destination_course_info, $document_file);
$content_html = str_replace($real_orig_url, $new_url, $content_html);
}
}
}
@ -2112,6 +2010,27 @@ class DocumentManager
return $content_html;
}
/**
* Generates a new URL for a resource within the context of the target course.
*
* This function constructs a URL to access a given resource, such as a document
* or image, which has been copied into the target course. It's essential for
* updating resource links in course content to point to the correct location
* after resources have been duplicated or moved between courses.
*/
public static function generateNewUrlForCourseResource(array $destination_course_info, string $document_file): string
{
$courseCode = $destination_course_info['code'];
$courseWebPath = api_get_path(WEB_COURSE_PATH) . $courseCode . "/document/";
$document_file = ltrim($document_file, '/');
$newUrl = $courseWebPath . $document_file;
return $newUrl;
}
/**
* Obtains the text inside the file with the right parser.
*/

@ -3074,39 +3074,32 @@ EOT;
$course_id = 0,
$only_active_exercises = true
) {
$table = Database::get_course_table(TABLE_QUIZ_TEST);
$course = api_get_course_entity($course_id);
$session = api_get_session_entity($session_id);
$repo = Container::getQuizRepository();
$qb = $repo->getResourcesByCourse($course, $session);
if ($only_active_exercises) {
// Only active exercises.
$sql_active_exercises = "active = 1 AND ";
$qb->andWhere('resource.active = 1');
} else {
// Not only active means visible and invisible NOT deleted (-2)
$sql_active_exercises = "active IN (1, 0) AND ";
$qb->andWhere('resource.active IN (1, 0)');
}
if (-1 == $session_id) {
$session_id = 0;
}
$qb->orderBy('resource.title', 'ASC');
$params = [
$session_id,
$course_id,
];
$exercises = $qb->getQuery()->getResult();
if (empty($session_id)) {
$conditions = [
'where' => ["$sql_active_exercises (session_id = 0 OR session_id IS NULL) AND c_id = ?" => [$course_id]],
'order' => 'title',
];
} else {
// All exercises
$conditions = [
'where' => ["$sql_active_exercises (session_id = 0 OR session_id IS NULL OR session_id = ? ) AND c_id=?" => $params],
'order' => 'title',
$exerciseList = [];
foreach ($exercises as $exercise) {
$exerciseList[] = [
'iid' => $exercise->getIid(),
'title' => $exercise->getTitle(),
];
}
return Database::select('*', $table, $conditions);
return $exerciseList;
}
/**

Loading…
Cancel
Save