Exercise: Improve and fix questions pool

pull/5235/head
christianbeeznst 2 years 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\Entity\ExtraField as ExtraFieldEntity;
use Chamilo\CoreBundle\Framework\Container; use Chamilo\CoreBundle\Framework\Container;
use Chamilo\CourseBundle\Entity\CQuizQuestion;
use Chamilo\CourseBundle\Entity\CQuizRelQuestion;
use ChamiloSession as Session; use ChamiloSession as Session;
use Knp\Component\Pager\Paginator; use Knp\Component\Pager\Paginator;
use Chamilo\CoreBundle\Component\Utils\ActionIcon; use Chamilo\CoreBundle\Component\Utils\ActionIcon;
@ -576,8 +578,8 @@ function getQuestions(
$length, $length,
$exerciseId, $exerciseId,
$courseCategoryId, $courseCategoryId,
$selected_course, $selectedCourse,
$session_id, $sessionId,
$exerciseLevel, $exerciseLevel,
$answerType, $answerType,
$questionId, $questionId,
@ -585,254 +587,84 @@ function getQuestions(
$fromExercise = 0, $fromExercise = 0,
$formValues = [] $formValues = []
) { ) {
$start = (int) $start; $entityManager = Database::getManager();
$length = (int) $length; $qb = $entityManager->createQueryBuilder();
$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);
$TBL_EXERCISE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); $qb->select('qq', 'IDENTITY(qr.quiz) as exerciseId')
$TBL_EXERCISES = Database::get_course_table(TABLE_QUIZ_TEST); ->from(CQuizQuestion::class, 'qq')
$TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION); ->leftJoin('qq.relQuizzes', 'qr');
$TBL_COURSE_REL_CATEGORY = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY);
$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) { if ($exerciseId > 0) {
$efConditions = getExtraFieldConditions($formValues, 'from'); $qb->andWhere('qr.quiz = :exerciseId')
->setParameter('exerciseId', $exerciseId);
$where = ''; } elseif ($exerciseId == -1) {
$from = ''; $qb->andWhere($qb->expr()->isNull('qr.quiz'));
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%'";
}
$select = ' qu.*, r.quiz_id exerciseId '; if ($courseCategoryId > 0) {
if ($getCount) { $qb->join('qq.categories', 'qc')
$select = 'count(qu.iid) as count'; ->andWhere('qc.id = :categoryId')
} ->setParameter('categoryId', $courseCategoryId);
}
// @todo fix this query with the new id field if ($exerciseLevel !== null && $exerciseLevel != -1) {
$sql = " ( $qb->andWhere('qq.level = :level')
SELECT $select ->setParameter('level', $exerciseLevel);
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 (!empty($questionId)) { if ($answerType !== null && $answerType > 0) {
$filter .= ' AND qu.iid='.$questionId; $qb->andWhere('qq.type = :type')
} ->setParameter('type', $answerType);
}
if (!empty($description)) { if (!empty($questionId)) {
$filter .= " AND qu.description LIKE '%$description%'"; $qb->andWhere('qq.iid = :questionId')
} ->setParameter('questionId', $questionId);
}
if (-1 == $session_id || empty($session_id)) { if (!empty($description)) {
$session_id = 0; $qb->andWhere('qq.description LIKE :description')
} ->setParameter('description', '%' . $description . '%');
$sessionCondition = api_get_session_condition($session_id, true, 'q.session_id'); }
$select = 'qu.iid, question, qu.type, level, q.session_id, qt.quiz_id exerciseId '; if (!empty($fromExercise)) {
if ($getCount) { $subQb = $entityManager->createQueryBuilder();
$select = 'count(qu.iid) as count'; $subQb->select('IDENTITY(sq.question)')
} ->from(CQuizRelQuestion::class, 'sq')
->where('sq.quiz = :fromExercise');
// All tests for the course selected, not in session $qb->andWhere($qb->expr()->notIn('qq.iid', $subQb->getDQL()))
$sql = "SELECT DISTINCT ->setParameter('fromExercise', $fromExercise);
$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
";
} }
if ($getCount) { if ($getCount) {
$result = Database::query($sql); $qb->select('COUNT(qq.iid)');
$row = Database::fetch_array($result, 'ASSOC'); try {
if ($row) { return (int) $qb->getQuery()->getSingleScalarResult();
return (int) $row['count']; } catch (\Doctrine\ORM\NoResultException $e) {
return 0;
} }
} else {
return 0; $qb->select('qq.iid as id', 'qq.question', 'qq.type', 'qq.level', 'IDENTITY(qr.quiz) as exerciseId')
} ->setFirstResult($start)
->setMaxResults($length);
$sql .= " LIMIT $start, $length";
$result = Database::query($sql); $results = $qb->getQuery()->getArrayResult();
$mainQuestionList = [];
while ($row = Database::fetch_array($result, 'ASSOC')) { $questions = [];
if (-1 == $exerciseId && isQuestionInActiveQuiz($row['iid'])) { foreach ($results as $result) {
continue; $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() : []; $formValues = $form->validate() ? $form->exportValues() : [];
@ -853,11 +685,11 @@ $nbrQuestions = getQuestions(
$formValues $formValues
); );
$length = api_get_setting('exercise.question_pagination_length'); $length = intval(api_get_setting('exercise.question_pagination_length'));
if (empty($length)) { if (empty($length)) {
$length = 20; $length = 20;
} }
$page = intval($page);
$start = ($page - 1) * $length; $start = ($page - 1) * $length;
$mainQuestionList = getQuestions( $mainQuestionList = getQuestions(
@ -883,19 +715,36 @@ $pagination->setTotalItemCount($nbrQuestions);
$pagination->setItemNumberPerPage($length); $pagination->setItemNumberPerPage($length);
$pagination->setCurrentPageNumber($page); $pagination->setCurrentPageNumber($page);
$pagination->renderer = function ($data) use ($url) { $pagination->renderer = function ($data) use ($url) {
$render = ''; $render = '<nav aria-label="Page navigation" style="display: flex; justify-content: center;">';
if ($data['pageCount'] > 1) { $render .= '<ul class="pagination">';
$render = '<ul class="pagination">';
for ($i = 1; $i <= $data['pageCount']; $i++) { if ($data['current'] > 1) {
$pageContent = '<li><a href="'.$url.'&page='.$i.'">'.$i.'</a></li>'; $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'] == $i) { }
$pageContent = '<li class="active"><a href="#" >'.$i.'</a></li>';
} if ($data['current'] > 1) {
$render .= $pageContent; $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>';
$render .= '</ul>';
} }
$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; return $render;
}; };
@ -1051,7 +900,9 @@ echo '<script>$(function () {
<div class="clear"></div> <div class="clear"></div>
<?php <?php
echo '<div class="text-center">';
echo $pagination; echo $pagination;
echo '</div>';
$tableId = 'question_pool_id'; $tableId = 'question_pool_id';
echo '<form id="'.$tableId.'" method="get" action="'.$url.'">'; echo '<form id="'.$tableId.'" method="get" action="'.$url.'">';
echo '<input type="hidden" name="fromExercise" value="'.$fromExercise.'">'; 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_source_html = self::get_resources_from_source_html($content_html);
$orig_course_info = api_get_course_info($origin_course_code); $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_code = CourseManager::getCourseCodeFromDirectory($destination_course_directory);
$destination_course_info = api_get_course_info($destination_course_code); $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)) { if (!empty($orig_source_html)) {
foreach ($orig_source_html as $source) { 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)) { $real_orig_url = $source[0];
$result = @copy($origin_filepath, $destination_filepath); $scope_url = $source[1];
if ($result) { $type_url = $source[2];
$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)
);
}
}
}
// Replace origin course path by destination course path. if ('local' === $scope_url) {
if (false !== strpos($content_html, $real_orig_url)) { $document_file = strstr($real_orig_url, 'document');
$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);
}
}
// replace origin course code by destination course code from origin url if (false !== $document_file) {
if (0 === strpos($real_orig_url, '?')) { $new_url = self::generateNewUrlForCourseResource($destination_course_info, $document_file);
$dest_url = str_replace($origin_course_code, $destination_course_code, $real_orig_url); $content_html = str_replace($real_orig_url, $new_url, $content_html);
$content_html = str_replace($real_orig_url, $dest_url, $content_html);
}
} }
} }
} }
@ -2112,6 +2010,27 @@ class DocumentManager
return $content_html; 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. * Obtains the text inside the file with the right parser.
*/ */

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

Loading…
Cancel
Save