From b6389b549adefbdbd5441e39b6514e7ed1f0e566 Mon Sep 17 00:00:00 2001 From: Marcus Eibrink-Lunzenauer <lunzenauer@elan-ev.de> Date: Fri, 21 Jan 2022 10:10:40 +0000 Subject: [PATCH] Courseware-Fortschrittseite beschleunigen (SORMless edition) --- app/controllers/course/courseware.php | 47 +++++++++++++++++---------- lib/models/Courseware/Instance.php | 35 ++++++++++++++++---- 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/app/controllers/course/courseware.php b/app/controllers/course/courseware.php index c4aae64c745..1d93a0a7516 100755 --- a/app/controllers/course/courseware.php +++ b/app/controllers/course/courseware.php @@ -131,7 +131,7 @@ class Course_CoursewareController extends AuthenticatedController return array_combine(array_column($elements, 'id'), $elements); } - private function computeChildrenOf(iterable $elements): iterable + private function computeChildrenOf(iterable &$elements): iterable { $childrenOf = []; foreach ($elements as $elementId => $element) { @@ -149,19 +149,28 @@ class Course_CoursewareController extends AuthenticatedController private function computeSelfProgresses( Instance $instance, User $user, - iterable $elements, + iterable &$elements, bool $showProgressForAllParticipants ): iterable { $progress = []; /** @var \Course $course */ $course = $instance->getRange(); - $allBlocks = $instance->findAllBlocksGroupedByStructuralElementId(); + $allBlockIds = $instance->findAllBlocksGroupedByStructuralElementId(function ($row) { + return $row['id']; + }); $courseMemberIds = $showProgressForAllParticipants ? array_column($course->getMembersWithStatus('autor'), 'user_id') : [$user->getId()]; - $userProgresses = UserProgress::findBySQL('user_id IN (?)', [$courseMemberIds]); + + $sql = + 'SELECT block_id, COUNT(grade) as count, SUM(grade) as grade ' . + 'FROM cw_user_progresses ' . + 'WHERE block_id IN (?) AND user_id IN (?) ' . + 'GROUP BY block_id'; + $userProgresses = \DBManager::get()->fetchGrouped($sql, [$allBlockIds, $courseMemberIds]); + foreach ($elements as $elementId => $element) { - $selfProgress = $this->getSelfProgresses($allBlocks, $elementId, $userProgresses, $courseMemberIds); + $selfProgress = $this->getSelfProgresses($allBlockIds, $elementId, $userProgresses, $courseMemberIds); $progress[$elementId] = [ 'self' => $selfProgress['counter'] ? $selfProgress['progress'] / $selfProgress['counter'] : 1, ]; @@ -171,31 +180,35 @@ class Course_CoursewareController extends AuthenticatedController } private function getSelfProgresses( - array $allBlocks, + array &$allBlockIds, string $elementId, - array $userProgresses, - array $courseMemberIds + array &$userProgresses, + array &$courseMemberIds ): array { - $blks = $allBlocks[$elementId] ?: []; + $blks = $allBlockIds[$elementId] ?: []; + if (!count($blks)) { + return [ + 'counter' => 0, + 'progress' => 1, + ]; + } $data = [ 'counter' => count($blks), 'progress' => 0, ]; + $usersCounter = count($courseMemberIds); foreach ($blks as $blk) { - $progresses = array_filter($userProgresses, function ($progress) use ($blk, $courseMemberIds) { - return $progress->block_id === $blk->getId() && in_array($progress->user_id, $courseMemberIds); - }); - $usersProgress = count($progresses) ? array_sum(array_column($progresses, 'grade')) : 0; - + $progresses = $userProgresses[$blk]; + $usersProgress = $progresses['count'] ? (float) $progresses['sum'] : 0; $data['progress'] += $usersProgress / $usersCounter; } return $data; } - private function computeCumulativeProgresses(Instance $instance, iterable $elements, iterable $progress): iterable + private function computeCumulativeProgresses(Instance $instance, iterable &$elements, iterable &$progress): iterable { $childrenOf = $this->computeChildrenOf($elements); @@ -225,7 +238,7 @@ class Course_CoursewareController extends AuthenticatedController return $progress; } - private function prepareProgressData(iterable $elements, iterable $progress): iterable + private function prepareProgressData(iterable &$elements, iterable &$progress): iterable { $data = []; foreach ($elements as $elementId => $element) { @@ -246,7 +259,7 @@ class Course_CoursewareController extends AuthenticatedController return $data; } - private function getChapterCounter(array $chapters): array + private function getChapterCounter(array &$chapters): array { $finished = 0; $started = 0; diff --git a/lib/models/Courseware/Instance.php b/lib/models/Courseware/Instance.php index cc9189c6a8e..c823a4edbde 100755 --- a/lib/models/Courseware/Instance.php +++ b/lib/models/Courseware/Instance.php @@ -37,12 +37,19 @@ class Instance $range->getConfiguration()->delete('COURSEWARE_SEQUENTIAL_PROGRESSION'); $range->getConfiguration()->delete('COURSEWARE_EDITING_PERMISSION'); - $last_element_configs = \ConfigValue::findBySQL('field = ? AND value LIKE ?', ['COURSEWARE_LAST_ELEMENT', '%'.$range->getRangeId().'%']); + $last_element_configs = \ConfigValue::findBySQL('field = ? AND value LIKE ?', [ + 'COURSEWARE_LAST_ELEMENT', + '%' . $range->getRangeId() . '%', + ]); foreach ($last_element_configs as $config) { $arr = json_decode($config->value, true); - $arr = array_filter($arr, function ($key) use ($range) { - return $key !== $range->id; - }, ARRAY_FILTER_USE_KEY); + $arr = array_filter( + $arr, + function ($key) use ($range) { + return $key !== $range->id; + }, + ARRAY_FILTER_USE_KEY + ); \UserConfig::get($config->range_id)->unsetValue('COURSEWARE_LAST_ELEMENT'); \UserConfig::get($config->range_id)->store('COURSEWARE_LAST_ELEMENT', $arr); } @@ -270,8 +277,23 @@ class Instance return $data; } - public function findAllBlocksGroupedByStructuralElementId(): iterable + /** + * Find all blocks of this instance and group them by their structural element's ID. + * You may specify your own `$formatter` instead of the default one which stores the blocks as instances of \Courseware\Block. + * + * @param ?callable(array $row): mixed $formatter Provide your own callable if you need something else instead of + * full-blown instances of \Courseware\Block. + * @return iterable all the (optionally formatted) blocks grouped by the IDs of the structural element containing + * that block. + */ + public function findAllBlocksGroupedByStructuralElementId(callable $formatter = null): iterable { + if (!$formatter) { + $formatter = function ($row) { + return \Courseware\Block::build($row, false); + }; + } + $sql = 'SELECT se.id AS structural_element_id, b.* FROM cw_structural_elements se JOIN cw_containers c ON se.id = c.structural_element_id @@ -286,11 +308,10 @@ class Instance $structuralElementId = $row['structural_element_id']; unset($row['structural_element_id']); - $block = \Courseware\Block::build($row, false); if (!isset($data[$structuralElementId])) { $data[$structuralElementId] = []; } - $data[$structuralElementId][] = $block; + $data[$structuralElementId][] = $formatter($row); } return $data; -- GitLab