diff --git a/public/main/admin/statistics/index.php b/public/main/admin/statistics/index.php index 4094b37bab6..a607c384858 100644 --- a/public/main/admin/statistics/index.php +++ b/public/main/admin/statistics/index.php @@ -334,6 +334,7 @@ get_lang('Courses') => [ 'report=courses' => get_lang('Courses'), 'report=tools' => get_lang('Tools access'), + 'report=tool_usage' => get_lang('Tool-based resource count'), 'report=courselastvisit' => get_lang('Latest access'), 'report=coursebylanguage' => get_lang('Number of courses by language'), ], @@ -709,6 +710,66 @@ $content .= ''; $content .= Statistics::printToolStats(); break; + case 'tool_usage': + $courseTools = Statistics::getAvailableTools(); + + if (empty($courseTools)) { + $content .= '
'.get_lang('No tools available for this report').'
'; + break; + } + + $form = new FormValidator('tool_usage', 'get'); + $form->addHeader(get_lang('Tool-based resource count')); + $form->addSelect( + 'tool_ids', + get_lang('Select Tools'), + $courseTools, + ['multiple' => true, 'required' => true] + ); + $form->addButtonSearch(get_lang('Generate report')); + $form->addHidden('report', 'tool_usage'); + + $content .= $form->returnForm(); + + if ($form->validate()) { + $values = $form->getSubmitValues(); + $toolIds = $values['tool_ids']; + $reportData = Statistics::getToolUsageReportByTools($toolIds); + + $table = new HTML_Table(['class' => 'table table-hover table-striped data_table stats_table']); + $headers = [ + get_lang('Tool'), + get_lang('Session'), + get_lang('Course'), + get_lang('Resource count'), + get_lang('Last updated'), + ]; + $row = 0; + + foreach ($headers as $index => $header) { + $table->setHeaderContents($row, $index, $header); + } + $row++; + + foreach ($reportData as $data) { + $linkHtml = $data['link'] !== '-' + ? sprintf( + '%s', + $data['link'], + htmlspecialchars($data['tool_name']) + ) + : htmlspecialchars($data['tool_name']); + + $table->setCellContents($row, 0, $linkHtml); + $table->setCellContents($row, 1, htmlspecialchars($data['session_name'])); + $table->setCellContents($row, 2, htmlspecialchars($data['course_name'])); + $table->setCellContents($row, 3, (int) $data['resource_count']); + $table->setCellContents($row, 4, htmlspecialchars($data['last_updated'])); + $row++; + } + $content .= $table->toHtml(); + } + break; case 'coursebylanguage': $content .= ''; $result = Statistics::printCourseByLanguageStats(); diff --git a/public/main/inc/lib/statistics.lib.php b/public/main/inc/lib/statistics.lib.php index 02244026171..f9b8beab900 100644 --- a/public/main/inc/lib/statistics.lib.php +++ b/public/main/inc/lib/statistics.lib.php @@ -3,6 +3,7 @@ use Chamilo\CoreBundle\Component\Utils\ChamiloApi; use Chamilo\CoreBundle\Entity\MessageRelUser; +use Chamilo\CoreBundle\Entity\ResourceLink; use Chamilo\CoreBundle\Entity\UserRelUser; use Chamilo\CoreBundle\Component\Utils\ActionIcon; use Chamilo\CoreBundle\Framework\Container; @@ -1784,6 +1785,28 @@ public static function groupByMonth(array $registrations): array return $groupedData; } + /** + * Retrieves the available tools using the repository. + */ + public static function getAvailableTools(): array + { + $em = Database::getManager(); + $repo = $em->getRepository(ResourceLink::class); + + return $repo->getAvailableTools(); + } + + /** + * Generates a report of tool usage based on the provided tool IDs. + */ + public static function getToolUsageReportByTools(array $toolIds): array + { + $em = Database::getManager(); + $repo = $em->getRepository(ResourceLink::class); + + return $repo->getToolUsageReportByTools($toolIds); + } + /** * Return de number of certificates generated. * This function is resource intensive. diff --git a/src/CoreBundle/Repository/ResourceLinkRepository.php b/src/CoreBundle/Repository/ResourceLinkRepository.php index aa69577d77b..050344e5311 100644 --- a/src/CoreBundle/Repository/ResourceLinkRepository.php +++ b/src/CoreBundle/Repository/ResourceLinkRepository.php @@ -21,6 +21,23 @@ */ class ResourceLinkRepository extends SortableRepository { + private array $toolList = [ + 'course_description' => '/main/course_description/index.php', + 'document' => '/resources/document/%resource_node_id%/', + 'learnpath' => '/main/lp/lp_controller.php', + 'link' => '/resources/links/%resource_node_id%/', + 'quiz' => '/main/exercise/exercise.php', + 'announcement' => '/main/announcements/announcements.php', + 'glossary' => '/resources/glossary/%resource_node_id%/', + 'attendance' => '/main/attendance/index.php', + 'course_progress' => '/main/course_progress/index.php', + 'agenda' => '/resources/ccalendarevent', + 'forum' => '/main/forum/index.php', + 'student_publication' => '/resources/assignment/%resource_node_id%', + 'survey' => '/main/survey/survey_list.php', + 'notebook' => '/main/notebook/index.php', + ]; + public function __construct(EntityManagerInterface $em) { parent::__construct($em, $em->getClassMetadata(ResourceLink::class)); @@ -53,4 +70,100 @@ public function removeByResourceInContext( $this->remove($link); } } + + /** + * Retrieves the list of available tools filtered by a predefined tool list. + * + * @return array The list of tools with their IDs and titles. + */ + public function getAvailableTools(): array + { + $queryBuilder = $this->_em->createQueryBuilder(); + $queryBuilder + ->select('DISTINCT t.id, t.title') + ->from('ChamiloCoreBundle:ResourceLink', 'rl') + ->innerJoin('ChamiloCoreBundle:ResourceType', 'rt', 'WITH', 'rt.id = rl.resourceTypeGroup') + ->innerJoin('ChamiloCoreBundle:Tool', 't', 'WITH', 't.id = rt.tool') + ->where('rl.course IS NOT NULL') + ->andWhere('t.title IN (:toolList)') + ->setParameter('toolList', array_keys($this->toolList)); + + $result = $queryBuilder->getQuery()->getArrayResult(); + + $tools = []; + foreach ($result as $row) { + $tools[$row['id']] = ucfirst(str_replace('_', ' ', $row['title'])); + } + + return $tools; + } + + /** + * Retrieves a usage report of tools with dynamic links. + * + * @return array The tool usage data including counts, last update timestamps, and dynamic links. + */ + public function getToolUsageReportByTools(array $toolIds): array + { + $queryBuilder = $this->_em->createQueryBuilder(); + + $queryBuilder + ->select( + 'COUNT(rl.id) AS resource_count', + 'IDENTITY(rl.course) AS course_id', + 'IDENTITY(rl.session) AS session_id', + 'IDENTITY(c.resourceNode) AS course_resource_node_id', + 't.title AS tool_name', + 'c.title AS course_name', + 's.title AS session_name', + 'MAX(rl.updatedAt) AS last_updated' + ) + ->from('ChamiloCoreBundle:ResourceLink', 'rl') + ->innerJoin('ChamiloCoreBundle:ResourceType', 'rt', 'WITH', 'rt.id = rl.resourceTypeGroup') + ->innerJoin('ChamiloCoreBundle:Tool', 't', 'WITH', 't.id = rt.tool') + ->innerJoin('ChamiloCoreBundle:Course', 'c', 'WITH', 'c.id = rl.course') + ->leftJoin('ChamiloCoreBundle:Session', 's', 'WITH', 's.id = rl.session') + ->where($queryBuilder->expr()->in('t.id', ':toolIds')) + ->groupBy('rl.course, rl.session, t.title') + ->orderBy('t.title', 'ASC') + ->addOrderBy('c.title', 'ASC') + ->addOrderBy('s.title', 'ASC') + ->setParameter('toolIds', $toolIds); + + $result = $queryBuilder->getQuery()->getArrayResult(); + + return array_map(function ($row) { + $toolName = $row['tool_name']; + $baseLink = $this->toolList[$toolName] ?? null; + $link = '-'; + if ($baseLink) { + $link = str_replace( + ['%resource_node_id%'], + [$row['course_resource_node_id']], + $baseLink + ); + + $queryParams = [ + 'cid' => $row['course_id'], + ]; + + if (!empty($row['session_id'])) { + $queryParams['sid'] = $row['session_id']; + } + + $link .= '?' . http_build_query($queryParams); + } + + return [ + 'tool_name' => $toolName, + 'session_id' => $row['session_id'], + 'session_name' => $row['session_name'] ?: '-', + 'course_id' => $row['course_id'], + 'course_name' => $row['course_name'], + 'resource_count' => (int) $row['resource_count'], + 'last_updated' => $row['last_updated'] ?: '-', + 'link' => $link, + ]; + }, $result); + } }