Add conf setting resource_sequence_show_dependency_in_course_intro - refs BT#20395

Allows to show the sequence graphic in the course intro
pull/4473/head
Angel Fernando Quiroz Campos 3 years ago
parent e9353ba96c
commit 4562ee16d0
  1. 33
      main/inc/ajax/sequence.ajax.php
  2. 38
      main/inc/introductionSection.inc.php
  3. 3
      main/install/configuration.dist.php
  4. 24
      main/template/default/sequence_resource/course_requirements.tpl
  5. 16
      main/template/default/sequence_resource/session_requirements.tpl
  6. 299
      src/Chamilo/CoreBundle/Entity/Repository/SequenceResourceRepository.php

@ -48,7 +48,7 @@ switch ($action) {
echo Display::img( echo Display::img(
$graphImage, $graphImage,
get_lang('GraphDependencyTree'), get_lang('GraphDependencyTree'),
['class' => 'center-block'], ['class' => 'center-block img-responsive'],
false false
); );
} catch (UnexpectedValueException $e) { } catch (UnexpectedValueException $e) {
@ -401,6 +401,7 @@ switch ($action) {
break; break;
case 'get_requirements': case 'get_requirements':
case 'get_dependents':
$sessionId = isset($_REQUEST['sid']) ? (int) $_REQUEST['sid'] : 0; $sessionId = isset($_REQUEST['sid']) ? (int) $_REQUEST['sid'] : 0;
$userId = api_get_user_id(); $userId = api_get_user_id();
$resourceName = ''; $resourceName = '';
@ -408,6 +409,7 @@ switch ($action) {
switch ($type) { switch ($type) {
case SequenceResource::SESSION_TYPE: case SequenceResource::SESSION_TYPE:
$resourceData = api_get_session_info($id); $resourceData = api_get_session_info($id);
$resourceName = $resourceData['name']; $resourceName = $resourceData['name'];
$template = 'session_requirements.tpl'; $template = 'session_requirements.tpl';
break; break;
@ -422,19 +424,38 @@ switch ($action) {
exit; exit;
} }
if ('get_requirements' === $action) {
$sequences = $sequenceResourceRepository->getRequirements($id, $type); $sequences = $sequenceResourceRepository->getRequirements($id, $type);
if (empty($sequences)) {
exit;
}
$sequenceList = $sequenceResourceRepository->checkRequirementsForUser($sequences, $type, $userId, $sessionId); $sequenceList = $sequenceResourceRepository->checkRequirementsForUser($sequences, $type, $userId, $sessionId);
$allowSubscription = $sequenceResourceRepository->checkSequenceAreCompleted($sequenceList); $allowSubscription = $sequenceResourceRepository->checkSequenceAreCompleted($sequenceList);
} else {
$sequences = $sequenceResourceRepository->getDependents($id, $type);
$sequenceList = $sequenceResourceRepository->checkDependentsForUser($sequences, $type, $userId, $sessionId);
$allowSubscription = $sequenceResourceRepository->checkSequenceAreCompleted(
$sequenceList,
SequenceResourceRepository::VERTICES_TYPE_DEP
);
}
$view = new Template(null, false, false, false, false, false); $view = new Template(null, false, false, false, false, false);
$view->assign('sequences', $sequenceList); $view->assign('sequences', $sequenceList);
$view->assign('sequence_type', $type); $view->assign('sequence_type', $type);
$view->assign('allow_subscription', $allowSubscription); $view->assign('allow_subscription', $allowSubscription);
$view->assign(
'item_type',
'get_requirements' === $action
? SequenceResourceRepository::VERTICES_TYPE_REQ
: SequenceResourceRepository::VERTICES_TYPE_DEP
);
$course = api_get_course_entity();
if ($course) {
$view->assign(
'current_requirement_is_completed',
$sequenceResourceRepository->checkCourseRequirements($userId, $course, $sessionId)
);
}
if ($allowSubscription) { if ($allowSubscription) {
$view->assign( $view->assign(

@ -2,6 +2,7 @@
/* For licensing terms, see /license.txt */ /* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Entity\SequenceResource;
use Chamilo\CourseBundle\Entity\CToolIntro; use Chamilo\CourseBundle\Entity\CToolIntro;
/** /**
@ -363,7 +364,18 @@ if (!api_is_anonymous()) {
$intro_content = AnnouncementManager::parseContent(api_get_user_id(), $intro_content, api_get_course_id()); $intro_content = AnnouncementManager::parseContent(api_get_user_id(), $intro_content, api_get_course_id());
} }
$introduction_section .= '<div class="col-md-12">'; $showSequencesBlock = false;
if (api_get_configuration_value('resource_sequence_show_dependency_in_course_intro')) {
$sequenceResourceRepo = $em->getRepository(SequenceResource::class);
$sequences = $sequenceResourceRepo->getDependents($course_id, SequenceResource::COURSE_TYPE);
$firstSequence = current($sequences);
$showSequencesBlock = !empty($firstSequence['dependents']);
}
$introduction_section .= $showSequencesBlock ? '<div class="col-md-10">' : '<div class="col-md-12">';
if ($intro_dispDefault) { if ($intro_dispDefault) {
if (!empty($intro_content)) { if (!empty($intro_content)) {
$introduction_section .= '<div class="page-course">'; $introduction_section .= '<div class="page-course">';
@ -380,7 +392,29 @@ if ($intro_dispDefault) {
$introduction_section .= $toolbar; $introduction_section .= $toolbar;
$introduction_section .= '</div>'; $introduction_section .= '</div>';
$introduction_section .= '</div>';
if ($showSequencesBlock) {
$sequenceUrl = http_build_query(
[
'a' => 'get_dependents',
'id' => $course_id,
'type' => SequenceResource::COURSE_TYPE,
'sid' => $session_id,
]
);
$introduction_section .= '<div class="col-md-2 text-center" id="resource-sequence">
<span class="fa fa-spinner fa-spin fa-fw" aria-hidden="true"></span>
</div>
<script>
$(function () {
$(\'#resource-sequence\').load(_p.web_ajax + \'sequence.ajax.php?'.$sequenceUrl.'\')
});
</script>
';
}
$introduction_section .= '</div>'; //div.row
$browser = api_get_navigator(); $browser = api_get_navigator();

@ -1992,6 +1992,9 @@ $_configuration['auth_password_links'] = [
// Resource sequence: Validate course in the same session. // Resource sequence: Validate course in the same session.
//$_configuration['course_sequence_valid_only_in_same_session'] = false; //$_configuration['course_sequence_valid_only_in_same_session'] = false;
// Allows to show the sequence graphic in the course intro
//$_configuration['resource_sequence_show_dependency_in_course_intro'] = false;
// Allow time per question. BT#17791 // Allow time per question. BT#17791
// Requires a question text extra field called "time", value in seconds. // Requires a question text extra field called "time", value in seconds.
// ALTER TABLE track_e_attempt ADD COLUMN seconds_spent INT; // ALTER TABLE track_e_attempt ADD COLUMN seconds_spent INT;

@ -1,13 +1,29 @@
<h2 class="page-header">{{ 'RequiredCourses'|get_lang }}</h2> {% if 'requirements' == item_type %}
<h3>{{ 'RequiredCourses'|get_lang }}</h3>
{% else %}
<h3>{{ 'Dependencies'|get_lang }}</h3>
{% endif %}
{% for key, item in sequences %} {% for key, item in sequences %}
{% if 'requirements' == item_type %}
{% set courses = item.requirements %}
{% else %}
{% set courses = item.dependents %}
{% endif %}
<h4>{{ item.name }}</h4> <h4>{{ item.name }}</h4>
<div id="parents"> <div id="parents">
{% for course in item.requirements %} {% for c_id, course in courses %}
<div class="parent"> <div class="parent">
<div class="big-icon"> <div class="big-icon">
<img src="{{ 'item-sequence.png'|icon(48) }}" width="48" height="48"> <img src="{{ 'item-sequence.png'|icon(48) }}" width="48" height="48">
<p class="sequence-course">{{ course.name }}</p> <p class="sequence-course">
{% if current_requirement_is_completed %}
<a href="{{ _p.web_course ~ course.code ~ '/index.php?' ~ { 'id_session': _c.session_id }|url_encode }}">{{ course.name }}</a>
{% else %}
{{ course.name }}
{% endif %}
</p>
{% if _u.logged %} {% if _u.logged %}
<span class="label {{ course.status ? 'label-success' : 'label-danger' }}"> <span class="label {{ course.status ? 'label-success' : 'label-danger' }}">
@ -21,7 +37,7 @@
</div> </div>
</div> </div>
{% if loop.index != item.requirements|length %} {% if loop.index != courses|length %}
<em class="fa fa-plus fa-3x sequence-plus-icon"></em> <em class="fa fa-plus fa-3x sequence-plus-icon"></em>
{% endif %} {% endif %}
{% endfor %} {% endfor %}

@ -1,9 +1,19 @@
<h2 class="page-header">{{ 'RequiredSessions'|get_lang }}</h2> {% if 'requirements' == item_type %}
<h3>{{ 'RequiredCourses'|get_lang }}</h3>
{% else %}
<h3>{{ 'Dependencies'|get_lang }}</h3>
{% endif %}
{% for item in sequences %} {% for item in sequences %}
{% if 'requirements' == item_type %}
{% set sessions = item.requirements %}
{% else %}
{% set sessions = item.dependents %}
{% endif %}
<h4>{{ item.name }}</h4> <h4>{{ item.name }}</h4>
<div id="parents"> <div id="parents">
{% for session in item.requirements %} {% for session in sessions %}
<div class="parent"> <div class="parent">
<div class="big-icon"> <div class="big-icon">
<img src="{{ 'item-sequence.png'|icon(48) }}" width="48" height="48"> <img src="{{ 'item-sequence.png'|icon(48) }}" width="48" height="48">
@ -21,7 +31,7 @@
</div> </div>
</div> </div>
{% if loop.index != item.requirements|length %} {% if loop.index != sessions|length %}
<em class="fa fa-plus fa-3x sequence-plus-icon"></em> <em class="fa fa-plus fa-3x sequence-plus-icon"></em>
{% endif %} {% endif %}
{% endfor %} {% endfor %}

@ -6,6 +6,7 @@ namespace Chamilo\CoreBundle\Entity\Repository;
use Category; use Category;
use Chamilo\CoreBundle\Entity\Course; use Chamilo\CoreBundle\Entity\Course;
use Chamilo\CoreBundle\Entity\GradebookCategory;
use Chamilo\CoreBundle\Entity\SequenceResource; use Chamilo\CoreBundle\Entity\SequenceResource;
use Chamilo\CoreBundle\Entity\Session; use Chamilo\CoreBundle\Entity\Session;
use Chamilo\CoreBundle\Entity\SessionRelUser; use Chamilo\CoreBundle\Entity\SessionRelUser;
@ -21,6 +22,9 @@ use SessionManager;
*/ */
class SequenceResourceRepository extends EntityRepository class SequenceResourceRepository extends EntityRepository
{ {
public const VERTICES_TYPE_REQ = 'requirements';
public const VERTICES_TYPE_DEP = 'dependents';
/** /**
* Find the SequenceResource based in the resourceId and type. * Find the SequenceResource based in the resourceId and type.
* *
@ -112,52 +116,15 @@ class SequenceResourceRepository extends EntityRepository
*/ */
public function getRequirements($resourceId, $type): array public function getRequirements($resourceId, $type): array
{ {
$sequencesResource = $this->findBy(['resourceId' => $resourceId, 'type' => $type]); return $this->getRequirementsOrDependents($resourceId, $type, self::VERTICES_TYPE_REQ);
$em = $this->getEntityManager();
$result = [];
/** @var SequenceResource $sequenceResource */
foreach ($sequencesResource as $sequenceResource) {
if (!$sequenceResource->hasGraph()) {
continue;
} }
$sequence = $sequenceResource->getSequence(); /**
$graph = $sequence->getUnSerializeGraph(); * Get the requirements for a resource only.
$vertex = $graph->getVertex($resourceId); */
$from = $vertex->getVerticesEdgeFrom(); public function getDependents(int $resourceId, int $type): array
{
$sequenceInfo = [ return $this->getRequirementsOrDependents($resourceId, $type, self::VERTICES_TYPE_DEP);
'name' => $sequence->getName(),
'requirements' => [],
];
foreach ($from as $subVertex) {
$vertexId = $subVertex->getId();
$resource = null;
switch ($type) {
case SequenceResource::SESSION_TYPE:
$repo = $em->getRepository('ChamiloCoreBundle:Session');
$resource = $repo->find($vertexId);
break;
case SequenceResource::COURSE_TYPE:
$repo = $em->getRepository('ChamiloCoreBundle:Course');
$resource = $repo->find($vertexId);
break;
}
if (null === $resource) {
continue;
}
$sequenceInfo['requirements'][$vertexId] = $resource;
}
$result[$sequence->getId()] = $sequenceInfo;
}
return $result;
} }
/** /**
@ -207,13 +174,139 @@ class SequenceResourceRepository extends EntityRepository
*/ */
public function checkRequirementsForUser(array $sequences, int $type, $userId, $sessionId = 0): array public function checkRequirementsForUser(array $sequences, int $type, $userId, $sessionId = 0): array
{ {
$sequenceList = []; return $this->checkRequirementsOrDependentsForUser(
$sequences,
$type,
self::VERTICES_TYPE_REQ,
$userId,
$sessionId
);
}
/**
* Check if the ser has completed the requirements for the sequences.
*
* @param array $sequences The sequences
* @param int $type The type of sequence resource
* @param int $userId
* @param int $sessionId
*/
public function checkDependentsForUser(array $sequences, int $type, $userId, $sessionId = 0): array
{
return $this->checkRequirementsOrDependentsForUser(
$sequences,
$type,
self::VERTICES_TYPE_DEP,
$userId,
$sessionId
);
}
public function checkCourseRequirements($userId, Course $course, $sessionId): bool
{
$em = $this->getEntityManager(); $em = $this->getEntityManager();
$sessionId = (int) $sessionId;
$gradebookCategoryRepo = $em->getRepository('ChamiloCoreBundle:GradebookCategory'); $gradebookCategoryRepo = $em->getRepository('ChamiloCoreBundle:GradebookCategory');
$gradebooks = $gradebookCategoryRepo->findBy(
[
'courseCode' => $course->getCode(),
'sessionId' => $sessionId,
'isRequirement' => true,
]
);
if (empty($gradebooks)) {
return false;
}
$status = true;
foreach ($gradebooks as $gradebook) {
$category = Category::createCategoryObjectFromEntity($gradebook);
$userFinishedCourse = Category::userFinishedCourse(
$userId,
$category,
true
);
if (0 === $sessionId) {
if (false === $userFinishedCourse) {
$status = false;
break;
}
} else {
if (false === $userFinishedCourse) {
$status = false;
break;
}
}
}
return $status;
}
/**
* Check if at least one sequence are completed.
*/
public function checkSequenceAreCompleted(array $sequences, $itemType = self::VERTICES_TYPE_REQ): bool
{
foreach ($sequences as $sequence) {
$status = true;
foreach ($sequence[$itemType] as $item) {
$status = $status && $item['status'];
}
if ($status) {
return true;
}
}
return false;
}
/**
* Get sessions from vertices.
*/
protected function findVerticesEdges(Vertices $verticesEdges, int $type): array
{
$sessionVertices = [];
$em = $this->getEntityManager();
foreach ($verticesEdges as $supVertex) {
$vertexId = $supVertex->getId();
switch ($type) {
case SequenceResource::SESSION_TYPE:
$resource = $em->getRepository('ChamiloCoreBundle:Session')->find($vertexId);
break;
case SequenceResource::COURSE_TYPE:
$resource = $em->getRepository('ChamiloCoreBundle:Course')->find($vertexId);
break;
}
if (empty($resource)) {
continue;
}
$sessionVertices[$vertexId] = $resource;
}
return $sessionVertices;
}
private function checkRequirementsOrDependentsForUser(
array $sequences,
int $resourceType,
string $itemType,
int $userId,
int $sessionId = 0
): array {
$sequenceList = [];
$em = $this->getEntityManager();
$gradebookCategoryRepo = $em->getRepository(GradebookCategory::class);
$sessionUserList = []; $sessionUserList = [];
$checkOnlySameSession = api_get_configuration_value('course_sequence_valid_only_in_same_session'); $checkOnlySameSession = api_get_configuration_value('course_sequence_valid_only_in_same_session');
if (SequenceResource::COURSE_TYPE == $type) { if (SequenceResource::COURSE_TYPE === $resourceType) {
if ($checkOnlySameSession) { if ($checkOnlySameSession) {
$sessionUserList = [$sessionId]; $sessionUserList = [$sessionId];
} else { } else {
@ -231,12 +324,12 @@ class SequenceResourceRepository extends EntityRepository
foreach ($sequences as $sequenceId => $sequence) { foreach ($sequences as $sequenceId => $sequence) {
$item = [ $item = [
'name' => $sequence['name'], 'name' => $sequence['name'],
'requirements' => [], $itemType => [],
]; ];
$resourceItem = null; $resourceItem = null;
foreach ($sequence['requirements'] as $resource) { foreach ($sequence[$itemType] as $resource) {
switch ($type) { switch ($resourceType) {
case SequenceResource::SESSION_TYPE: case SequenceResource::SESSION_TYPE:
/** @var Session $resource */ /** @var Session $resource */
$id = $resource->getId(); $id = $resource->getId();
@ -289,6 +382,7 @@ class SequenceResourceRepository extends EntityRepository
$resourceItem = [ $resourceItem = [
'name' => $resource->getTitle(), 'name' => $resource->getTitle(),
'code' => $resource->getCode(),
'status' => $status, 'status' => $status,
]; ];
@ -299,7 +393,7 @@ class SequenceResourceRepository extends EntityRepository
continue; continue;
} }
$item['requirements'][$id] = $resourceItem; $item[$itemType][$id] = $resourceItem;
} }
$sequenceList[$sequenceId] = $item; $sequenceList[$sequenceId] = $item;
} }
@ -307,94 +401,63 @@ class SequenceResourceRepository extends EntityRepository
return $sequenceList; return $sequenceList;
} }
public function checkCourseRequirements($userId, Course $course, $sessionId): bool
{
$em = $this->getEntityManager();
$sessionId = (int) $sessionId;
$gradebookCategoryRepo = $em->getRepository('ChamiloCoreBundle:GradebookCategory');
$gradebooks = $gradebookCategoryRepo->findBy(
[
'courseCode' => $course->getCode(),
'sessionId' => $sessionId,
'isRequirement' => true,
]
);
if (empty($gradebooks)) {
return false;
}
$status = true;
foreach ($gradebooks as $gradebook) {
$category = Category::createCategoryObjectFromEntity($gradebook);
$userFinishedCourse = Category::userFinishedCourse(
$userId,
$category,
true
);
if (0 === $sessionId) {
if (false === $userFinishedCourse) {
$status = false;
break;
}
} else {
if (false === $userFinishedCourse) {
$status = false;
break;
}
}
}
return $status;
}
/** /**
* Check if at least one sequence are completed. * Get the requirements or dependants for a resource only.
*/ */
public function checkSequenceAreCompleted(array $sequences): bool private function getRequirementsOrDependents($resourceId, int $resourceType, string $itemType): array
{ {
foreach ($sequences as $sequence) { $em = $this->getEntityManager();
$status = true;
foreach ($sequence['requirements'] as $item) { $sequencesResource = $this->findBy(['resourceId' => $resourceId, 'type' => $resourceType]);
$status = $status && $item['status']; $result = [];
}
if ($status) { /** @var SequenceResource $sequenceResource */
return true; foreach ($sequencesResource as $sequenceResource) {
} if (!$sequenceResource->hasGraph()) {
continue;
} }
return false; $sequence = $sequenceResource->getSequence();
$graph = $sequence->getUnSerializeGraph();
$vertex = $graph->getVertex($resourceId);
if (self::VERTICES_TYPE_REQ === $itemType) {
$edges = $vertex->getVerticesEdgeFrom();
} else {
$edges = $vertex->getVerticesEdgeTo();
} }
/** $sequenceInfo = [
* Get sessions from vertices. 'name' => $sequence->getName(),
*/ $itemType => [],
protected function findVerticesEdges(Vertices $verticesEdges, int $type): array ];
{
$sessionVertices = [];
$em = $this->getEntityManager();
foreach ($verticesEdges as $supVertex) { foreach ($edges as $edge) {
$vertexId = $supVertex->getId(); $vertexId = $edge->getId();
switch ($type) { $resource = null;
switch ($resourceType) {
case SequenceResource::SESSION_TYPE: case SequenceResource::SESSION_TYPE:
$resource = $em->getRepository('ChamiloCoreBundle:Session')->find($vertexId); $repo = $em->getRepository(Session::class);
$resource = $repo->find($vertexId);
break; break;
case SequenceResource::COURSE_TYPE: case SequenceResource::COURSE_TYPE:
$resource = $em->getRepository('ChamiloCoreBundle:Course')->find($vertexId); $repo = $em->getRepository(Course::class);
$resource = $repo->find($vertexId);
break; break;
} }
if (empty($resource)) { if (null === $resource) {
continue; continue;
} }
$sessionVertices[$vertexId] = $resource; $sequenceInfo[$itemType][$vertexId] = $resource;
} }
return $sessionVertices; $result[$sequence->getId()] = $sequenceInfo;
}
return $result;
} }
} }

Loading…
Cancel
Save