Add "allow_gradebook_stats" config to improve gradebook speed

See BT#14357
Requires:
- DB changes see configuration.dist.php
- editing the entities: GradebookLink.php and GradebookEvaluation
pull/2864/head
Julio 7 years ago
parent 48fcdbb6f0
commit efcd6d14dd
  1. 149
      main/exercise/exercise.class.php
  2. 8
      main/gradebook/gradebook_add_eval.php
  3. 13
      main/gradebook/gradebook_add_result.php
  4. 93
      main/gradebook/gradebook_display_summary.php
  5. 11
      main/gradebook/gradebook_edit_result.php
  6. 6
      main/gradebook/gradebook_view_result.php
  7. 133
      main/gradebook/index.php
  8. 40
      main/gradebook/lib/GradebookUtils.php
  9. 52
      main/gradebook/lib/be/abstractlink.class.php
  10. 12
      main/gradebook/lib/be/category.class.php
  11. 111
      main/gradebook/lib/be/evaluation.class.php
  12. 72
      main/gradebook/lib/be/exerciselink.class.php
  13. 4
      main/gradebook/lib/be/linkfactory.class.php
  14. 5
      main/gradebook/lib/be/result.class.php
  15. 370
      main/gradebook/lib/fe/gradebooktable.class.php
  16. 303
      main/gradebook/lib/gradebook_data_generator.class.php
  17. 10
      main/inc/lib/exercise.lib.php
  18. 13
      main/inc/lib/pdf.lib.php
  19. 21
      main/inc/lib/text.lib.php
  20. 7
      main/install/configuration.dist.php
  21. 112
      src/Chamilo/CoreBundle/Entity/GradebookEvaluation.php
  22. 112
      src/Chamilo/CoreBundle/Entity/GradebookLink.php

@ -2767,6 +2767,7 @@ class Exercise
*/
public function cleanResults($cleanLpTests = false, $cleanResultBeforeDate = null)
{
$sessionId = api_get_session_id();
$table_track_e_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
$table_track_e_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
@ -2796,7 +2797,7 @@ class Exercise
WHERE
c_id = ".api_get_course_int_id()." AND
exe_exo_id = ".$this->id." AND
session_id = ".api_get_session_id()." ".
session_id = ".$sessionId." ".
$sql_where;
$result = Database::query($sql);
@ -2814,15 +2815,16 @@ class Exercise
}
}
$session_id = api_get_session_id();
// delete TRACK_E_EXERCISES table
$sql = "DELETE FROM $table_track_e_exercises
WHERE
c_id = ".api_get_course_int_id()." AND
exe_exo_id = ".$this->id." $sql_where AND
session_id = ".$session_id;
session_id = ".$sessionId;
Database::query($sql);
$this->generateStats($this->id, api_get_course_info(), $sessionId);
Event::addEvent(
LOG_EXERCISE_RESULT_DELETE,
LOG_EXERCISE_ID,
@ -2830,7 +2832,7 @@ class Exercise
null,
null,
api_get_course_int_id(),
$session_id
$sessionId
);
return $i;
@ -7962,6 +7964,145 @@ class Exercise
return $questionList;
}
/**
* @param int $exerciseId
* @param array $courseInfo
* @param int $sessionId
*
* @return bool
* @throws \Doctrine\ORM\OptimisticLockException
*/
public function generateStats($exerciseId, $courseInfo, $sessionId)
{
$allowStats = api_get_configuration_value('allow_gradebook_stats');
if (!$allowStats) {
return false;
}
if (empty($courseInfo)) {
return false;
}
$courseId = $courseInfo['real_id'];
$sessionId = (int) $sessionId;
$result = $this->read($exerciseId);
if (empty($result)) {
api_not_allowed(true);
}
$statusToFilter = empty($sessionId) ? STUDENT : 0;
$studentList = CourseManager::get_user_list_from_course_code(
api_get_course_id(),
$sessionId,
null,
null,
$statusToFilter
);
if (empty($studentList)) {
Display::addFlash(Display::return_message(get_lang('NoUsersInCourse')));
header('Location: '.api_get_path(WEB_CODE_PATH).'exercise/exercise.php?'.api_get_cidreq());
exit;
}
$tblStats = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
$studentIdList = [];
if (!empty($studentList)) {
$studentIdList = array_column($studentList, 'user_id');
}
if ($this->exercise_was_added_in_lp == false) {
$sql = "SELECT * FROM $tblStats
WHERE
exe_exo_id = $exerciseId AND
orig_lp_id = 0 AND
orig_lp_item_id = 0 AND
status <> 'incomplete' AND
session_id = $sessionId AND
c_id = $courseId
";
} else {
$lpId = null;
if (!empty($this->lpList)) {
// Taking only the first LP
$lpId = current($this->lpList);
$lpId = $lpId['lp_id'];
}
$sql = "SELECT *
FROM $tblStats
WHERE
exe_exo_id = $exerciseId AND
orig_lp_id = $lpId AND
status <> 'incomplete' AND
session_id = $sessionId AND
c_id = $courseId ";
}
$sql .= ' ORDER BY exe_id DESC';
$studentCount = 0;
$sum = 0;
$bestResult = 0;
$weight = 0;
$sumResult = 0;
$result = Database::query($sql);
while ($data = Database::fetch_array($result, 'ASSOC')) {
// Only take into account users in the current student list.
if (!empty($studentIdList)) {
if (!in_array($data['exe_user_id'], $studentIdList)) {
continue;
}
}
if (!isset($students[$data['exe_user_id']])) {
if ($data['exe_weighting'] != 0) {
$students[$data['exe_user_id']] = $data['exe_result'];
$studentCount++;
if ($data['exe_result'] > $bestResult) {
$bestResult = $data['exe_result'];
}
$sum += $data['exe_result'] / $data['exe_weighting'];
$sumResult += $data['exe_result'];
$weight = $data['exe_weighting'];
}
}
}
$count = count($studentList);
$average = $sumResult / $count;
$em = Database::getManager();
$links = AbstractLink::getGradebookLinksFromItem(
$this->selectId(),
LINK_EXERCISE,
api_get_course_id(),
api_get_session_id()
);
$repo = $em->getRepository('ChamiloCoreBundle:GradebookLink');
foreach ($links as $link) {
$linkId = $link['id'];
/** @var \Chamilo\CoreBundle\Entity\GradebookLink $exerciseLink */
$exerciseLink = $repo->find($linkId);
if ($exerciseLink) {
$exerciseLink
->setUserScoreList($students)
->setBestScore($bestResult)
->setAverageScore($average)
->setScoreWeight($this->get_max_score())
;
$em->persist($exerciseLink);
$em->flush();
}
}
}
/**
* Gets the question list ordered by the question_order setting (drag and drop).
*

@ -19,7 +19,7 @@ $evaladd = new Evaluation();
$evaladd->set_user_id($_user['user_id']);
if (!empty($select_cat)) {
$evaladd->set_category_id($_GET['selectcat']);
$cat = Category :: load($_GET['selectcat']);
$cat = Category::load($_GET['selectcat']);
$evaladd->set_course_code($cat[0]->get_course_code());
} else {
$evaladd->set_category_id(0);
@ -49,18 +49,16 @@ if ($form->validate()) {
$eval->set_course_code(api_get_course_id());
$eval->set_category_id($values['hid_category_id']);
$parent_cat = Category :: load($values['hid_category_id']);
$parent_cat = Category::load($values['hid_category_id']);
$global_weight = $cat[0]->get_weight();
//$values['weight'] = $values['weight_mask']/$global_weight*$parent_cat[0]->get_weight();
$values['weight'] = $values['weight_mask'];
$eval->set_weight($values['weight']);
$eval->set_max($values['max']);
$visible = 1;
if (empty($values['visible'])) {
$visible = 0;
} else {
$visible = 1;
}
$eval->set_visible($visible);
$eval->add();

@ -37,18 +37,25 @@ if ($add_result_form->validate()) {
header('Location: gradebook_view_result.php?addresultnostudents=&selecteval='.$selectEval.'&'.api_get_cidreq());
exit;
}
$scores = ($values['score']);
foreach ($scores as $row) {
$scores = $values['score'];
$sumResult = 0;
$bestResult = 0;
$studentScoreList = [];
foreach ($scores as $userId => $row) {
$res = new Result();
$res->set_evaluation_id($values['evaluation_id']);
$res->set_user_id(key($scores));
//if no scores are given, don't set the score
if ((!empty($row)) || ($row == '0')) {
if (!empty($row) || $row == '0') {
$res->set_score($row);
}
$res->add();
next($scores);
}
Evaluation::generateStats($values['evaluation_id']);
Display::addFlash(Display::return_message(get_lang('ResultAdded'), 'confirmation', false));
header('Location: gradebook_view_result.php?addresult=&selecteval='.$selectEval.'&'.api_get_cidreq());
exit;

@ -9,7 +9,7 @@ use ChamiloSession as Session;
require_once __DIR__.'/../inc/global.inc.php';
$current_course_tool = TOOL_GRADEBOOK;
api_protect_course_script();
api_protect_course_script(true);
api_set_more_memory_and_time_limits();
api_block_anonymous_users();
GradebookUtils::block_students();
@ -18,6 +18,7 @@ $cat_id = isset($_GET['selectcat']) ? (int) $_GET['selectcat'] : null;
$action = isset($_GET['action']) && $_GET['action'] ? $_GET['action'] : null;
$sessionId = api_get_session_id();
$courseInfo = api_get_course_info();
$statusToFilter = empty($sessionId) ? STUDENT : 0;
$userList = CourseManager::get_user_list_from_course_code(
@ -28,19 +29,68 @@ $userList = CourseManager::get_user_list_from_course_code(
$statusToFilter
);
$loadStats = [];
if (api_get_setting('gradebook_detailed_admin_view') === 'true') {
$loadStats = [1, 2, 3];
} else {
if (api_get_configuration_value('gradebook_enable_best_score') !== false) {
$loadStats = [2];
}
}
/*Session::write('use_gradebook_cache', false);
$useCache = api_get_configuration_value('gradebook_use_apcu_cache');
$cacheAvailable = api_get_configuration_value('apc') && $useCache;
if ($cacheAvailable) {
$cacheDriver = new \Doctrine\Common\Cache\ApcuCache();
$cacheDriver->deleteAll();
$cacheDriver->flushAll();
}*/
switch ($action) {
case 'export_all':
//Session::write('use_gradebook_cache', true);
$cats = Category::load($cat_id, null, null, null, null, null, false);
$studentList = CourseManager::get_user_list_from_course_code(
/** @var Category $cat */
$cat = $cats[0];
$allcat = $cat->get_subcategories(
null,
api_get_course_id(),
api_get_session_id()
);
$alleval = $cat->get_evaluations(
null,
true,
api_get_course_id(),
$sessionId,
api_get_session_id()
);
$alllink = $cat->get_links(
null,
true,
api_get_course_id(),
api_get_session_id()
);
$gradebooktable = new GradebookTable(
$cat,
$allcat,
$alleval,
$alllink,
null, // params
true, // $exportToPdf
false, // showteacher
null,
$statusToFilter
$userList,
$loadStats
);
$key = $gradebooktable->getPreloadDataKey();
// preloads data
Session::erase($key);
$defaultData = $gradebooktable->preloadData();
$tpl = new Template('', false, false, false);
$courseInfo = api_get_course_info();
$params = [
'pdf_title' => sprintf(get_lang('GradeFromX'), $courseInfo['name']),
'session_info' => '',
@ -54,44 +104,53 @@ switch ($action) {
'orientation' => 'P',
];
$pdf = new PDF('A4', $params['orientation'], $params, $tpl);
$counter = 0;
$htmlList = [];
foreach ($userList as $index => $value) {
$htmlList[] = GradebookUtils::generateTable(
$courseInfo,
$value['user_id'],
$cats,
false,
true,
true,
$studentList,
$userList,
$pdf
);
$counter++;
}
if (!empty($htmlList)) {
// Print certificates (without the common header/footer/watermark
// stuff) and return as one multiple-pages PDF
/*$address = api_get_setting('institution_address');
$phone = api_get_setting('administratorTelephone');
$address = str_replace('\n', '<br />', $address);
$pdf->custom_header = array('html' => "<h5 align='right'>$address <br />$phone</h5>");*/
// stuff) and return as one multiple-pages PDF
$counter = 0;
//error_log('Loading html list');
$content = '';
foreach ($htmlList as $value) {
$content .= $value.'<pagebreak>';
//error_log('Loading html: '.$counter);
$counter++;
}
$tempFile = api_get_path(SYS_ARCHIVE_PATH).uniqid('gradebook_export_all').'.html';
file_put_contents($tempFile, $content);
//error_log('generating pdf');
$pdf->html_to_pdf(
$htmlList,
$tempFile,
null,
null,
false,
true,
true
);
//error_log('End generating');
}
// Delete calc_score session data
Session::erase('calc_score');
break;
case 'download':
//Session::write('use_gradebook_cache', true);
$userId = isset($_GET['user_id']) && $_GET['user_id'] ? $_GET['user_id'] : null;
$cats = Category::load($cat_id, null, null, null, null, null, false);
GradebookUtils::generateTable($userId, $cats);
GradebookUtils::generateTable($courseInfo, $userId, $cats);
break;
}

@ -17,6 +17,9 @@ if (empty($select_eval)) {
$resultedit = Result::load(null, null, $select_eval);
$evaluation = Evaluation::load($select_eval);
$evaluation[0]->check_lock_permissions();
$courseInfo = api_get_course_info();
$sessionId = api_get_session_id();
$edit_result_form = new EvalForm(
EvalForm::TYPE_ALL_RESULTS_EDIT,
$evaluation[0],
@ -28,6 +31,8 @@ $edit_result_form = new EvalForm(
if ($edit_result_form->validate()) {
$values = $edit_result_form->exportValues();
$scores = $values['score'];
$bestResult = 0;
$scoreFinalList = [];
foreach ($scores as $userId => $score) {
/** @var array $resultedit */
$resultedit = Result::load($userId);
@ -37,6 +42,12 @@ if ($edit_result_form->validate()) {
if (empty($score)) {
$score = 0;
}
$scoreFinalList[$result->get_user_id()] = $score;
if ($score > $bestResult) {
$bestResult = $score;
}
$score = api_number_format($score, api_get_setting('gradebook_number_decimals'));
$result->set_score($score);
$result->save();

@ -25,13 +25,13 @@ $interbreadcrumb[] = [
];
//load the evaluation & category
$select_eval = intval($_GET['selecteval']);
$select_eval = (int) $_GET['selecteval'];
if (empty($select_eval)) {
api_not_allowed();
}
$displayscore = ScoreDisplay::instance();
$eval = Evaluation :: load($select_eval);
$eval = Evaluation::load($select_eval);
$overwritescore = 0;
if ($eval[0]->get_category_id() < 0) {
// if category id is negative, then the evaluation's origin is a link
@ -43,7 +43,7 @@ if ($eval[0]->get_category_id() < 0) {
//load the result with the evaluation id
if (isset($_GET['delete_mark'])) {
$result = Result :: load($_GET['delete_mark']);
$result = Result::load($_GET['delete_mark']);
if (!empty($result[0])) {
$result[0]->delete();
}

@ -17,12 +17,66 @@ $current_course_tool = TOOL_GRADEBOOK;
api_block_anonymous_users();
api_protect_course_script(true);
ob_start();
$course_code = api_get_course_id();
$stud_id = api_get_user_id();
$session_id = api_get_session_id();
$course_id = api_get_course_int_id();
$courseInfo = api_get_course_info();
$action = isset($_GET['action']) ? $_GET['action'] : null;
$itemId = isset($_GET['itemId']) ? $_GET['itemId'] : 0;
switch ($action) {
case 'generate_eval_stats':
if (!empty($itemId)) {
Evaluation::generateStats($itemId);
Display::addFlash(Display::return_message(get_lang('Updated')));
}
header('Location: '.api_get_self().'?'.api_get_cidreq());
exit;
break;
case 'generate_link_stats':
if (!empty($itemId)) {
$link = LinkFactory::create(LINK_EXERCISE);
$links = $link::load($itemId);
/** @var ExerciseLink $link */
foreach ($links as $link) {
$exercise = new Exercise(api_get_course_int_id());
$exercise->read($link->get_ref_id());
$exercise->generateStats($link->get_ref_id(), api_get_course_info(), api_get_session_id());
}
Display::addFlash(Display::return_message(get_lang('Updated')));
}
header('Location: '.api_get_self().'?'.api_get_cidreq());
exit;
break;
case 'lock':
$category_to_lock = Category::load($_GET['category_id']);
$category_to_lock[0]->lockAllItems(1);
$confirmation_message = get_lang('GradebookLockedAlert');
break;
case 'unlock':
if (api_is_platform_admin()) {
$category_to_lock = Category::load($_GET['category_id']);
$category_to_lock[0]->lockAllItems(0);
$confirmation_message = get_lang('EvaluationHasBeenUnLocked');
}
break;
case 'export_table':
$hidePdfReport = api_get_configuration_value('gradebook_hide_pdf_report_button');
if ($hidePdfReport) {
api_not_allowed(true);
}
if (isset($_GET['category_id'])) {
$cats = Category::load($_GET['category_id'], null, null, null, null, null, false);
GradebookUtils::generateTable($courseInfo, api_get_user_id(), $cats);
exit;
}
break;
}
ob_start();
// Make sure the destination for scripts is index.php instead of gradebook.php
Category::setUrl('index.php');
@ -262,15 +316,16 @@ if (isset($_GET['moveeval'])) {
//move a link
if (isset($_GET['movelink'])) {
$moveLink = (int) $_GET['movelink'];
GradebookUtils::block_students();
$link = LinkFactory::load($_GET['movelink']);
$link = LinkFactory::load($moveLink);
$move_form = new LinkForm(
LinkForm::TYPE_MOVE,
null,
$link[0],
'move_link_form',
null,
api_get_self().'?movelink='.$_GET['movelink'].'&selectcat='.$selectCat.'&'.api_get_cidreq()
api_get_self().'?movelink='.$moveLink.'&selectcat='.$selectCat.'&'.api_get_cidreq()
);
if ($move_form->validate()) {
@ -323,10 +378,9 @@ if (isset($_GET['deletecat'])) {
// Parameters for evaluations.
if (isset($_GET['visibleeval'])) {
GradebookUtils::block_students();
$visibility_command = 0;
if (isset($_GET['set_visible'])) {
$visibility_command = 1;
} else {
$visibility_command = 0;
}
$eval = Evaluation::load($_GET['visibleeval']);
$eval[0]->set_visible($visibility_command);
@ -344,7 +398,7 @@ if (isset($_GET['visibleeval'])) {
// Parameters for evaluations.
if (isset($_GET['lockedeval'])) {
GradebookUtils::block_students();
$locked = Security::remove_XSS($_GET['lockedeval']);
$locked = (int) $_GET['lockedeval'];
if (isset($_GET['typelocked']) && api_is_platform_admin()) {
$type_locked = 0;
$confirmation_message = get_lang('EvaluationHasBeenUnLocked');
@ -373,10 +427,9 @@ if (isset($_GET['deleteeval'])) {
// Parameters for links.
if (isset($_GET['visiblelink'])) {
GradebookUtils::block_students();
$visibility_command = 0;
if (isset($_GET['set_visible'])) {
$visibility_command = 1;
} else {
$visibility_command = 0;
}
$link = LinkFactory::load($_GET['visiblelink']);
if (isset($link) && isset($link[0])) {
@ -395,7 +448,7 @@ if (isset($_GET['visiblelink'])) {
if (isset($_GET['deletelink'])) {
GradebookUtils::block_students();
$get_delete_link = intval($_GET['deletelink']);
$get_delete_link = (int) $_GET['deletelink'];
//fixing #5229
if (!empty($get_delete_link)) {
$link = LinkFactory::load($get_delete_link);
@ -441,38 +494,13 @@ if (!empty($course_to_crsind) && !isset($_GET['confirm'])) {
$filter_warning_msg = false;
}
$action = isset($_GET['action']) ? $_GET['action'] : null;
switch ($action) {
case 'lock':
$category_to_lock = Category::load($_GET['category_id']);
$category_to_lock[0]->lockAllItems(1);
$confirmation_message = get_lang('GradebookLockedAlert');
break;
case 'unlock':
if (api_is_platform_admin()) {
$category_to_lock = Category::load($_GET['category_id']);
$category_to_lock[0]->lockAllItems(0);
$confirmation_message = get_lang('EvaluationHasBeenUnLocked');
}
break;
case 'export_table':
$hidePdfReport = api_get_configuration_value('gradebook_hide_pdf_report_button');
if ($hidePdfReport) {
api_not_allowed(true);
}
//table will be export below
ob_start();
break;
}
// Actions on the sortabletable.
if (isset($_POST['action'])) {
GradebookUtils::block_students();
$number_of_selected_items = count($_POST['id']);
if ($number_of_selected_items == '0') {
if ($number_of_selected_items == 0) {
$warning_message = get_lang('NoItemsSelected');
$filter_warning_msg = false;
} else {
@ -751,11 +779,9 @@ if (isset($_GET['studentoverview'])) {
}
$cats = Category::load($selectCat, null, null, null, null, null, false);
//with this fix the teacher only can view 1 gradebook
// With this fix the teacher only can view 1 gradebook
if (api_is_platform_admin()) {
$stud_id = (api_is_allowed_to_edit() ? null : api_get_user_id());
} else {
$stud_id = $stud_id;
}
$allcat = $cats[0]->get_subcategories($stud_id, $course_code, $session_id);
@ -787,7 +813,6 @@ if (!empty($selectCat)) {
$cat = new Category();
$course_id = CourseManager::get_course_by_category($selectCat);
$show_message = $cat->show_message_resource_delete($course_id);
if ($show_message == '') {
// Student
if (!api_is_allowed_to_edit() && !api_is_excluded_user_type()) {
@ -819,7 +844,7 @@ if (!api_is_allowed_to_edit(null, true)) {
if ($allowButton) {
$actionsLeft .= Display::url(
Display::returnFontAwesomeIcon('file-pdf-o').get_lang('DownloadReportPdf'),
api_get_self().'?action=export_table&'.api_get_cidreq(),
api_get_self().'?action=export_table&'.api_get_cidreq().'&category_id='.$selectCat,
['class' => 'btn btn-default']
);
}
@ -960,7 +985,7 @@ if (isset($first_time) && $first_time == 1 && api_is_allowed_to_edit(null, true)
if (api_is_allowed_to_edit(null, true) &&
api_get_setting('gradebook_enable_grade_model') === 'true'
) {
//Showing the grading system
// Showing the grading system
if (!empty($grade_models[$grade_model_id])) {
echo Display::return_message(
get_lang('GradeModel').': '.$grade_models[$grade_model_id]['name']
@ -1002,28 +1027,6 @@ if (isset($first_time) && $first_time == 1 && api_is_allowed_to_edit(null, true)
$gradebookTable->td_attributes = [
4 => 'class="text-center"',
];
} else {
/*if (empty($model)) {
$gradebookTable->td_attributes = [
3 => 'class="text-right"',
4 => 'class="text-center"',
];
if (!empty($loadStats)) {
for ($z = 5; $z < count($loadStats); $z++) {
$gradebookTable->td_attributes[$z] = 'class="text-center"';
}
}
} else {
$gradebookTable->td_attributes = [
3 => 'class="text-right"',
4 => 'class="text-center"',
];
}
if ($action == 'export_table') {
unset($gradebookTable->td_attributes[7]);
}*/
}
$table = $gradebookTable->return_table();
@ -1034,7 +1037,7 @@ if (isset($first_time) && $first_time == 1 && api_is_allowed_to_edit(null, true)
$graph = $gradebookTable->getGraph();
}
if ($action == 'export_table') {
if ($action === 'export_table') {
ob_clean();
$params = [
'pdf_title' => sprintf(get_lang('GradeFromX'), $courseInfo['name']),

@ -378,6 +378,7 @@ class GradebookUtils
ICON_SIZE_SMALL
).
'</a>';
if (api_is_allowed_to_edit(null, true)) {
$modify_icons .= '&nbsp;<a href="gradebook_showlog_eval.php?visiblelog='.$eval->get_id().'&selectcat='.$selectcat.' &'.$courseParams.'">'.
Display::return_icon(
@ -387,6 +388,14 @@ class GradebookUtils
ICON_SIZE_SMALL
).
'</a>';
$allowStats = api_get_configuration_value('allow_gradebook_stats');
if ($allowStats) {
$modify_icons .= Display::url(
Display::return_icon('reload.png', get_lang('GenerateStats')),
api_get_self().'?itemId='.$eval->get_id().'&action=generate_eval_stats&selectcat='.$selectcat.'&'.$courseParams
);
}
}
if ($is_locked && !api_is_platform_admin()) {
@ -466,6 +475,7 @@ class GradebookUtils
ICON_SIZE_SMALL
).
'</a>';
$modify_icons .= '&nbsp;<a href="gradebook_showlog_link.php?visiblelink='.$link->get_id().'&selectcat='.$selectcat.'&'.$courseParams.'">'.
Display::return_icon(
'history.png',
@ -475,6 +485,14 @@ class GradebookUtils
).
'</a>';
$allowStats = api_get_configuration_value('allow_gradebook_stats');
if ($allowStats && $link->get_type() == LINK_EXERCISE) {
$modify_icons .= Display::url(
Display::return_icon('reload.png', get_lang('GenerateStats')),
api_get_self().'?itemId='.$link->get_id().'&action=generate_link_stats&selectcat='.$selectcat.'&'.$courseParams
);
}
//If a work is added in a gradebook you can only delete the link in the work tool
if ($is_locked && !api_is_platform_admin()) {
$modify_icons .= '&nbsp;'.
@ -1523,16 +1541,19 @@ class GradebookUtils
}
/**
* @param int $userId
* @param array $cats
* @param bool $saveToFile
* @param bool $saveToHtmlFile
* @param array $studentList
* @param PDF $pdf
* @param GradebookTable $gradebooktable
* @param array $courseInfo
* @param int $userId
* @param array $cats
* @param bool $saveToFile
* @param bool $saveToHtmlFile
* @param array $studentList
* @param PDF $pdf
*
* @return string
*/
public static function generateTable(
$courseInfo,
$userId,
$cats,
$saveToFile = false,
@ -1540,9 +1561,7 @@ class GradebookUtils
$studentList = [],
$pdf = null
) {
$courseInfo = api_get_course_info();
$userInfo = api_get_user_info($userId);
$cat = $cats[0];
$allcat = $cats[0]->get_subcategories(
$userId,
@ -1577,9 +1596,6 @@ class GradebookUtils
$gradebooktable->userId = $userId;
if (api_is_allowed_to_edit(null, true)) {
/*$gradebooktable->td_attributes = [
4 => 'class=centered',
];*/
} else {
$gradebooktable->td_attributes = [
3 => 'class=centered',
@ -1589,7 +1605,6 @@ class GradebookUtils
7 => 'class=centered',
];
}
$table = $gradebooktable->return_table();
$graph = $gradebooktable->getGraph();
@ -1626,6 +1641,7 @@ class GradebookUtils
);
if ($saveToHtmlFile) {
return $result;
file_put_contents($file, $result);
return $file;

@ -28,6 +28,8 @@ abstract class AbstractLink implements GradebookItem
protected $weight;
protected $visible;
protected $session_id;
/** @var \Chamilo\CoreBundle\Entity\GradebookLink */
public $entity;
/**
* Constructor.
@ -699,6 +701,12 @@ abstract class AbstractLink implements GradebookItem
private static function create_objects_from_sql_result($result)
{
$links = [];
$allow = api_get_configuration_value('allow_gradebook_stats');
if ($allow) {
$em = Database::getManager();
$repo = $em->getRepository('ChamiloCoreBundle:GradebookLink');
}
while ($data = Database::fetch_array($result)) {
$link = LinkFactory::create($data['type']);
$link->set_id($data['id']);
@ -714,8 +722,11 @@ abstract class AbstractLink implements GradebookItem
//session id should depend of the category --> $data['category_id']
$session_id = api_get_session_id();
$link->set_session_id($session_id);
if ($allow) {
$link->entity = $repo->find($data['id']);
}
$links[] = $link;
}
@ -745,4 +756,43 @@ abstract class AbstractLink implements GradebookItem
return $targets;
}
/**
* @param int $itemId
* @param int $linkType
* @param string $courseCode
* @param int $sessionId
*
* @return array|bool|\Doctrine\DBAL\Driver\Statement
*/
public static function getGradebookLinksFromItem($itemId, $linkType, $courseCode, $sessionId = 0)
{
if (empty($courseCode) || empty($itemId) || empty($linkType)) {
return false;
}
$table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
$tableCategory = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
$itemId = (int) $itemId;
$linkType = (int) $linkType;
$sessionId = (int) $sessionId;
$courseCode = Database::escape_string($courseCode);
$sql = "SELECT DISTINCT l.*
FROM $table l INNER JOIN $tableCategory c
ON (c.course_code = l.course_code AND c.id = l.category_id)
WHERE
ref_id = $itemId AND
type = $linkType AND
l.course_code = '$courseCode' AND
c.session_id = $sessionId";
$result = Database::query($sql);
if (Database::num_rows($result)) {
$result = Database::store_result($result);
return $result;
}
return false;
}
}

@ -34,6 +34,8 @@ class Category implements GradebookItem
private $minimumToValidate;
/** @var int */
private $gradeBooksToValidateInDependence;
/** @var GradebookCategory */
public $entity;
/**
* Consctructor.
@ -2571,6 +2573,12 @@ class Category implements GradebookItem
private static function create_category_objects_from_sql_result($result)
{
$categories = [];
$allow = api_get_configuration_value('allow_gradebook_stats');
if ($allow) {
$em = Database::getManager();
$repo = $em->getRepository('ChamiloCoreBundle:GradebookCategory');
}
while ($data = Database::fetch_array($result)) {
$cat = new Category();
$cat->set_id($data['id']);
@ -2591,6 +2599,10 @@ class Category implements GradebookItem
$cat->setMinimumToValidate(isset($data['minimum_to_validate']) ? $data['minimum_to_validate'] : null);
$cat->setGradeBooksToValidateInDependence(isset($data['gradebooks_to_validate_in_dependence']) ? $data['gradebooks_to_validate_in_dependence'] : null);
if ($allow) {
$cat->entity = $repo->find($data['id']);
}
$categories[] = $cat;
}

@ -23,6 +23,8 @@ class Evaluation implements GradebookItem
private $eval_max;
private $visible;
private $sessionId;
/** @var \Chamilo\CoreBundle\Entity\GradebookEvaluation */
public $entity;
/**
* Construct.
@ -534,6 +536,55 @@ class Evaluation implements GradebookItem
*/
public function calc_score($stud_id = null, $type = null)
{
$allowStats = api_get_configuration_value('allow_gradebook_stats');
if ($allowStats) {
$evaluation = $this->entity;
if (!empty($evaluation)) {
$weight = $evaluation->getMax();
switch ($type) {
case 'best':
$bestResult = $evaluation->getBestScore();
$result = [$bestResult, $weight];
return $result;
break;
case 'average':
$count = count($evaluation->getUserScoreList());
if (empty($count)) {
$result = [0, $weight];
return $result;
}
$sumResult = array_sum($evaluation->getUserScoreList());
$result = [$sumResult / $count, $weight];
return $result;
break;
case 'ranking':
$ranking = AbstractLink::getCurrentUserRanking($stud_id, $evaluation->getUserScoreList());
return $ranking;
break;
default:
$weight = $evaluation->getMax();
if (!empty($stud_id)) {
$scoreList = $evaluation->getUserScoreList();
$result = [0, $weight];
if (isset($scoreList[$stud_id])) {
$result = [$scoreList[$stud_id], $weight];
}
return $result;
} else {
$studentCount = count($evaluation->getUserScoreList());
$sumResult = array_sum($evaluation->getUserScoreList());
$result = [$sumResult, $studentCount];
}
return $result;
break;
}
}
}
$useSession = true;
if (isset($stud_id) && empty($type)) {
$key = 'result_score_student_list_'.api_get_course_int_id().'_'.api_get_session_id().'_'.$this->id.'_'.$stud_id;
@ -803,16 +854,66 @@ class Evaluation implements GradebookItem
{
}
/**
* @return mixed
*/
public function getStudentList()
{
return $this->studentList;
}
/**
* @param $list
*/
public function setStudentList($list)
{
$this->studentList = $list;
}
/**
* @param int $evaluationId
*
*/
public static function generateStats($evaluationId)
{
$allowStats = api_get_configuration_value('allow_gradebook_stats');
if ($allowStats) {
$evaluation = self::load($evaluationId);
$results = Result::load(null, null, $evaluationId);
$sumResult = 0;
$bestResult = 0;
$average = 0;
$scoreList = [];
if (!empty($results)) {
/** @var Result $result */
foreach ($results as $result) {
$score = $result->get_score();
$scoreList[$result->get_user_id()] = $score;
$sumResult += $score;
if ($score > $bestResult) {
$bestResult = $score;
}
}
$average = $sumResult / count($results);
}
/** @var Evaluation $evaluation */
$evaluation = $evaluation[0];
$evaluation = $evaluation->entity;
$evaluation
->setBestScore($bestResult)
->setAverageScore($average)
->setUserScoreList($scoreList)
;
$em = Database::getManager();
$em->persist($evaluation);
$em->flush();
}
}
/**
* @param array $result
*
@ -821,6 +922,12 @@ class Evaluation implements GradebookItem
private static function create_evaluation_objects_from_sql_result($result)
{
$alleval = [];
$allow = api_get_configuration_value('allow_gradebook_stats');
if ($allow) {
$em = Database::getManager();
$repo = $em->getRepository('ChamiloCoreBundle:GradebookEvaluation');
}
if (Database::num_rows($result)) {
while ($data = Database::fetch_array($result)) {
$eval = new Evaluation();
@ -838,6 +945,10 @@ class Evaluation implements GradebookItem
$eval->set_locked($data['locked']);
$eval->setSessionId(api_get_session_id());
if ($allow) {
$eval->entity = $repo->find($data['id']);
}
$alleval[] = $eval;
}
}

@ -1,6 +1,8 @@
<?php
/* For licensing terms, see /license.txt */
use ChamiloSession as Session;
/**
* Class ExerciseLink
* Defines a gradebook ExerciseLink object.
@ -176,6 +178,61 @@ class ExerciseLink extends AbstractLink
*/
public function calc_score($stud_id = null, $type = null)
{
$allowStats = api_get_configuration_value('allow_gradebook_stats');
if ($allowStats) {
$link = $this->entity;
if (!empty($link)) {
$weight = $link->getScoreWeight();
switch ($type) {
case 'best':
$bestResult = $link->getBestScore();
$result = [$bestResult, $weight];
return $result;
break;
case 'average':
$count = count($this->getStudentList());
if (empty($count)) {
$result = [0, $weight];
return $result;
}
$sumResult = array_sum($link->getUserScoreList());
$result = [$sumResult / $count, $weight];
return $result;
break;
case 'ranking':
return '';
$newList = [];
var_dump($this->getStudentList());exit;
foreach ($this->getStudentList() as $userId) {
}
$ranking = AbstractLink::getCurrentUserRanking($stud_id, $link->getUserScoreList());
return $ranking;
break;
default:
if (!empty($stud_id)) {
$scoreList = $link->getUserScoreList();
$result = [0, $weight];
if (isset($scoreList[$stud_id])) {
$result = [$scoreList[$stud_id], $weight];
}
return $result;
} else {
$studentCount = count($this->getStudentList());
$sumResult = array_sum($link->getUserScoreList());
$result = [$sumResult, $studentCount];
}
return $result;
break;
}
}
}
$tblStats = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
$tblHp = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTPOTATOES);
$tblDoc = Database::get_course_table(TABLE_DOCUMENT);
@ -185,6 +242,7 @@ class ExerciseLink extends AbstractLink
$sessionId = $this->get_session_id();
$courseId = $this->getCourseId();
$exerciseData = $this->get_exercise_data();
$exerciseId = isset($exerciseData['id']) ? $exerciseData['id'] : 0;
$stud_id = (int) $stud_id;
@ -559,4 +617,18 @@ class ExerciseLink extends AbstractLink
return $this->exercise_data;
}
public function getBestScore()
{
return $this->getStats('best');
}
public function getStats($type)
{
switch ($type) {
case 'best':
break;
}
}
}

@ -74,11 +74,11 @@ class LinkFactory
/**
* Static method to create specific link objects.
*
* @param $type link type
* @param int $type link type
*/
public static function create($type)
{
$type = intval($type);
$type = (int) $type;
switch ($type) {
case LINK_EXERCISE:
return new ExerciseLink();

@ -179,7 +179,6 @@ class Result
$sql .= ' WHERE';
}
$sql .= ' gr.evaluation_id = '.intval($evaluation_id);
$paramcount++;
}
$sql .= ' ORDER BY u.lastname, u.firstname';
@ -273,6 +272,8 @@ class Result
$sql .= ' WHERE id = '.$this->id;
// no need to update creation date
Database::query($sql);
Evaluation::generateStats($this->get_evaluation_id());
}
/**
@ -289,6 +290,8 @@ class Result
$sql = "DELETE FROM $table WHERE result_id = ".$this->id;
Database::query($sql);
}
Evaluation::generateStats($this->get_evaluation_id());
}
/**

@ -4,6 +4,7 @@
use CpChart\Cache as pCache;
use CpChart\Data as pData;
use CpChart\Image as pImage;
use ChamiloSession as Session;
/**
* GradebookTable Class
@ -63,6 +64,7 @@ class GradebookTable extends SortableTable
$this->teacherView = is_null($showTeacherView) ? api_is_allowed_to_edit(null, true) : $showTeacherView;
$this->userId = is_null($userId) ? api_get_user_id() : $userId;
$this->exportToPdf = $exportToPdf;
$this->studentList = $studentList;
parent::__construct(
'gradebooklist',
@ -79,6 +81,8 @@ class GradebookTable extends SortableTable
$this->cats = $cats;
$this->loadStats = $loadStats;
$this->datagen = new GradebookDataGenerator($cats, $evals, $links);
$this->datagen->preLoadDataKey = $this->getPreloadDataKey();
$this->datagen->hidePercentage = api_get_configuration_value('hide_gradebook_percentage_user_result');
if (!empty($userId)) {
@ -182,6 +186,105 @@ class GradebookTable extends SortableTable
return $this->datagen->get_total_items_count();
}
/**
* @return string
*/
public function getPreloadDataKey()
{
return 'default_data_'.api_get_course_id().'_'.api_get_session_id();
}
public function preloadData()
{
$allitems = $this->datagen->items;
usort($allitems, ['GradebookDataGenerator', 'sort_by_name']);
//$visibleItems = $this->datagen->items;
$visibleItems = array_merge($this->datagen->items, $this->evals_links);
//Session::erase($this->getPreloadDataKey());
$defaultDataFromSession = Session::read($this->getPreloadDataKey());
if (empty($defaultDataFromSession)) {
$defaultData = [];
/** @var GradebookItem $item */
foreach ($visibleItems as $item) {
$item->setStudentList($this->studentList);
$itemType = get_class($item);
switch ($itemType) {
case 'Evaluation':
// Best
$best = $this->datagen->buildBestResultColumn($item);
$defaultData[$item->get_id()]['best'] = $best;
// Average
$average = $this->datagen->buildAverageResultColumn($item);
$defaultData[$item->get_id()]['average'] = $average;
break;
case 'ExerciseLink':
/** @var ExerciseLink $item */
// Best
$best = $this->datagen->buildBestResultColumn($item);
$defaultData[$item->get_id()]['best'] = $best;
// Average
$average = $this->datagen->buildAverageResultColumn($item);
$defaultData[$item->get_id()]['average'] = $average;
// Ranking
/*if (!empty($this->studentList)) {
$invalidateRanking = true;
foreach ($this->studentList as $user) {
$score = $this->datagen->build_result_column(
$user['user_id'],
$item,
false,
true
);
if (!empty($score['score'])) {
$invalidateRanking = false;
}
$rankingStudentList[$user['user_id']] = $score['score'][0];
$defaultData[$item->get_id()]['ranking'] = $rankingStudentList;
$defaultData[$item->get_id()]['ranking_invalidate'] = $invalidateRanking;
}
}*/
break;
default:
// Best
$best = $this->datagen->buildBestResultColumn($item);
$defaultData[$item->get_id()]['best'] = $best;
// Average
$average = $this->datagen->buildAverageResultColumn($item);
$defaultData[$item->get_id()]['average'] = $average;
// Ranking
if (!empty($this->studentList)) {
$invalidateRanking = true;
foreach ($this->studentList as $user) {
$score = $this->datagen->build_result_column(
$user['user_id'],
$item,
false,
true
);
if (!empty($score['score'])) {
$invalidateRanking = false;
}
$rankingStudentList[$user['user_id']] = $score['score'][0];
$defaultData[$item->get_id()]['ranking'] = $rankingStudentList;
$defaultData[$item->get_id()]['ranking_invalidate'] = $invalidateRanking;
}
//exit;
}
break;
}
}
Session::write($this->getPreloadDataKey(), $defaultData);
} else {
$defaultData = $defaultDataFromSession;
}
return $defaultData;
}
/**
* Function used by SortableTable to generate the data to display.
*
@ -292,12 +395,16 @@ class GradebookTable extends SortableTable
$userExerciseScoreInCategory = api_get_configuration_value(
'gradebook_use_exercise_score_settings_in_categories'
);
$course_code = api_get_course_id();
$session_id = api_get_session_id();
$defaultData = Session::read($this->getPreloadDataKey());
// Categories.
if (!empty($data_array)) {
foreach ($data_array as $data) {
// list of items inside the gradebook (exercises, lps, forums, etc)
$row = [];
/** @var AbstractLink $item */
$item = $mainCategory = $data[0];
@ -316,7 +423,7 @@ class GradebookTable extends SortableTable
$row[] = $this->build_type_column($item);
// Name.
if (get_class($item) == 'Category') {
if (get_class($item) === 'Category') {
$row[] = $invisibility_span_open.'<strong>'.$item->get_name().'</strong>'.$invisibility_span_close;
$main_categories[$item->get_id()]['name'] = $item->get_name();
} else {
@ -383,94 +490,86 @@ class GradebookTable extends SortableTable
}
// Students get the results and certificates columns
if (1) {
$value_data = isset($data[4]) ? $data[4] : null;
$best = isset($data['best']) ? $data['best'] : null;
$average = isset($data['average']) ? $data['average'] : null;
$ranking = isset($data['ranking']) ? $data['ranking'] : null;
$totalResult = [
$data['result_score'][0],
$data['result_score'][1],
];
$totalUserResult[0] += $totalResult[0] / ($totalResult[1] ?: 1) * $data[3];
$totalUserResult[1] += $data[3];
$totalBest = [
$scoredisplay->format_score($totalBest[0] + $data['best_score'][0]),
$scoredisplay->format_score($totalBest[1] + $data['best_score'][1]),
];
$totalAverage = [
$data['average_score'][0],
$data['average_score'][1],
];
// Student result
if (empty($model)) {
$row[] = $value_data;
} else {
$row[] = ExerciseLib::show_score(
$data['result_score'][0],
$data['result_score'][1]
);
}
$value_data = isset($data[4]) ? $data[4] : null;
$best = isset($data['best']) ? $data['best'] : null;
$average = isset($data['average']) ? $data['average'] : null;
$ranking = isset($data['ranking']) ? $data['ranking'] : null;
$mode = SCORE_AVERAGE;
if ($userExerciseScoreInCategory) {
$mode = SCORE_SIMPLE;
$totalResult = [
$data['result_score'][0],
$data['result_score'][1],
];
$result = ExerciseLib::convertScoreToPlatformSetting($totalAverage[0], $totalAverage[1]);
$totalAverage[0] = $result['score'];
$totalAverage[1] = $result['weight'];
$totalUserResult[0] += $totalResult[0] / ($totalResult[1] ?: 1) * $data[3];
$totalUserResult[1] += $data[3];
$result = ExerciseLib::convertScoreToPlatformSetting($totalResult[0], $totalResult[1]);
$totalResult[0] = $result['score'];
$totalResult[1] = $result['weight'];
$totalBest = [
$scoredisplay->format_score($totalBest[0] + $data['best_score'][0]),
$scoredisplay->format_score($totalBest[1] + $data['best_score'][1]),
];
$result = ExerciseLib::convertScoreToPlatformSetting(
$data['result_score'][0],
$data['result_score'][1]
);
$data['my_result_no_float'][0] = $result['score'];
}
$totalAverage = [
$data['average_score'][0],
$data['average_score'][1],
];
$totalResultAverageValue = strip_tags($scoredisplay->display_score($totalResult, $mode));
$totalAverageValue = strip_tags($scoredisplay->display_score($totalAverage, $mode));
// Student result
if (empty($model)) {
$row[] = $value_data;
} else {
$row[] = ExerciseLib::show_score(
$data['result_score'][0],
$data['result_score'][1]
);
}
$this->dataForGraph['my_result'][] = floatval($totalResultAverageValue);
$this->dataForGraph['average'][] = floatval($totalAverageValue);
$this->dataForGraph['my_result_no_float'][] = $data['result_score'][0];
$mode = SCORE_AVERAGE;
if ($userExerciseScoreInCategory) {
$mode = SCORE_SIMPLE;
if (empty($model)) {
// Ranking
if (in_array(1, $this->loadStats)) {
$row[] = $ranking;
}
$result = ExerciseLib::convertScoreToPlatformSetting($totalAverage[0], $totalAverage[1]);
$totalAverage[0] = $result['score'];
$totalAverage[1] = $result['weight'];
// Best
if (in_array(2, $this->loadStats)) {
$row[] = $best;
}
$result = ExerciseLib::convertScoreToPlatformSetting($totalResult[0], $totalResult[1]);
$totalResult[0] = $result['score'];
$totalResult[1] = $result['weight'];
// Average
if (in_array(3, $this->loadStats)) {
$row[] = $average;
}
$result = ExerciseLib::convertScoreToPlatformSetting(
$data['result_score'][0],
$data['result_score'][1]
);
$data['my_result_no_float'][0] = $result['score'];
}
$totalResultAverageValue = strip_tags($scoredisplay->display_score($totalResult, $mode));
$totalAverageValue = strip_tags($scoredisplay->display_score($totalAverage, $mode));
$this->dataForGraph['my_result'][] = floatval($totalResultAverageValue);
$this->dataForGraph['average'][] = floatval($totalAverageValue);
$this->dataForGraph['my_result_no_float'][] = $data['result_score'][0];
if (empty($model)) {
// Ranking
if (in_array(1, $this->loadStats)) {
$row[] = $ranking;
}
if (get_class($item) == 'Category') {
if ($this->exportToPdf == false) {
$row[] = $this->build_edit_column($item);
}
// Best
if (in_array(2, $this->loadStats)) {
$row[] = $best;
}
} else {
$row[] = $scoreToDisplay;
if (!empty($this->cats)) {
if ($this->exportToPdf == false) {
$row[] = $this->build_edit_column($item);
}
// Average
if (in_array(3, $this->loadStats)) {
$row[] = $average;
}
}
if (get_class($item) === 'Category') {
if ($this->exportToPdf == false) {
$row[] = $this->build_edit_column($item);
}
}
}
@ -479,10 +578,9 @@ class GradebookTable extends SortableTable
$sortable_data[] = $row;
// Loading children
if (get_class($item) == 'Category') {
$course_code = api_get_course_id();
$session_id = api_get_session_id();
if (get_class($item) === 'Category') {
$parent_id = $item->get_id();
$cats = Category::load(
$parent_id,
null,
@ -500,6 +598,7 @@ class GradebookTable extends SortableTable
$alllink = $subCategory->get_links($this->userId);
$sub_cat_info = new GradebookDataGenerator($allcat, $alleval, $alllink);
$sub_cat_info->preLoadDataKey = $this->getPreloadDataKey();
$sub_cat_info->userId = $user_id;
$data_array2 = $sub_cat_info->get_data(
$sorting,
@ -534,7 +633,7 @@ class GradebookTable extends SortableTable
$row[] = $this->build_type_column($item, ['style' => 'padding-left:5px']);
// Name.
$row[] = $invisibility_span_open."&nbsp;&nbsp;&nbsp; ".
$row[] = $invisibility_span_open.'&nbsp;&nbsp;&nbsp; '.
$this->build_name_link($item, $type).$invisibility_span_close;
// Description.
@ -652,7 +751,7 @@ class GradebookTable extends SortableTable
foreach ($main_cat as $myCat) {
$myParentId = $myCat->get_parent_id();
if ($myParentId == 0) {
$main_weight = intval($myCat->get_weight());
$main_weight = (int) $myCat->get_weight();
}
}
}
@ -682,22 +781,18 @@ class GradebookTable extends SortableTable
} else {
// Total for student.
if (count($main_cat) > 1) {
$weights = [];
foreach ($main_categories as $cat) {
$weights[] = $cat['weight'];
}
$main_weight = intval($main_cat[0]->get_weight());
$main_weight = (int) $main_cat[0]->get_weight();
$global = null;
$average = null;
$myTotal = 0;
foreach ($this->dataForGraph['my_result_no_float'] as $result) {
$myTotal += $scoredisplay->format_score($result);
$myTotal += $result;
}
$totalResult[0] = $myTotal;
// Overwrite main weight
$totalResult[1] = $main_weight;
$totalResult = $scoredisplay->display_score(
$totalResult,
SCORE_DIV
@ -714,24 +809,33 @@ class GradebookTable extends SortableTable
$row[] = $main_weight;
$row[] = $totalResult;
$categoryId = $main_cat[0]->get_id();
if (in_array(1, $this->loadStats)) {
$totalRanking = [];
$invalidateRanking = true;
$average = 0;
$main_cat[0]->setStudentList($this->studentList);
foreach ($this->studentList as $student) {
$score = $main_cat[0]->calc_score(
$student['user_id'],
null,
api_get_course_id(),
api_get_session_id()
);
if (!empty($score[0])) {
$invalidateRanking = false;
if (isset($defaultData[$categoryId]) && isset($defaultData[$categoryId]['ranking'])) {
$totalRanking = $defaultData[$categoryId]['ranking'];
$invalidateRanking = $defaultData[$categoryId]['ranking_invalidate'];
} else {
$totalRanking = [];
$invalidateRanking = true;
$average = 0;
$main_cat[0]->setStudentList($this->studentList);
foreach ($this->studentList as $student) {
$score = $main_cat[0]->calc_score(
$student['user_id'],
null,
api_get_course_id(),
api_get_session_id()
);
if (!empty($score[0])) {
$invalidateRanking = false;
}
$totalRanking[$student['user_id']] = $score[0];
$average += $score[0];
}
$totalRanking[$student['user_id']] = $score[0];
$average += $score[0];
$defaultData[$categoryId]['ranking'] = $totalRanking;
$defaultData[$categoryId]['ranking_invalidate'] = $invalidateRanking;
Session::write($this->getPreloadDataKey(), $defaultData);
}
$totalRanking = AbstractLink::getCurrentUserRanking($user_id, $totalRanking);
@ -750,8 +854,13 @@ class GradebookTable extends SortableTable
}
if (in_array(2, $this->loadStats)) {
// Overwrite main weight
$totalBest[1] = $main_weight;
if (isset($defaultData[$categoryId]) && isset($defaultData[$categoryId]['best'])) {
$totalBest = $defaultData[$categoryId]['best'];
} else {
// Overwrite main weight
$totalBest[1] = $main_weight;
$defaultData[$categoryId]['best'] = $totalBest;
}
$totalBest = $scoredisplay->display_score(
$totalBest,
SCORE_DIV,
@ -760,10 +869,17 @@ class GradebookTable extends SortableTable
);
$row[] = $totalBest;
}
if (in_array(3, $this->loadStats)) {
// Overwrite main weight
$totalAverage[0] = $average / count($this->studentList);
$totalAverage[1] = $main_weight;
if (isset($defaultData[$categoryId]) && isset($defaultData[$categoryId]['average'])) {
$totalAverage = $defaultData[$categoryId]['average'];
} else {
// Overwrite main weight
$totalAverage[0] = $average / count($this->studentList);
$totalAverage[1] = $main_weight;
$defaultData[$categoryId]['average'] = $totalBest;
}
$totalAverage = $scoredisplay->display_score(
$totalAverage,
SCORE_DIV,
@ -773,23 +889,25 @@ class GradebookTable extends SortableTable
$row[] = $totalAverage;
}
if (!empty($row)) {
$sortable_data[] = $row;
}
}
}
Session::write('default_data', $defaultData);
// Warning messages
$view = isset($_GET['view']) ? $_GET['view'] : null;
if ($this->teacherView) {
if (isset($_GET['selectcat']) &&
$_GET['selectcat'] > 0 &&
$view != 'presence'
) {
$id_cat = intval($_GET['selectcat']);
$id_cat = (int) $_GET['selectcat'];
$category = Category::load($id_cat);
$weight_category = intval($this->build_weight($category[0]));
$weight_category = (int) $this->build_weight($category[0]);
$course_code = $this->build_course_code($category[0]);
$weight_total_links = round($weight_total_links);
@ -798,7 +916,8 @@ class GradebookTable extends SortableTable
$weight_total_links > $weight_category
) {
$warning_message = sprintf(get_lang('TotalWeightMustBeX'), $weight_category);
$modify_icons = '<a href="gradebook_edit_cat.php?editcat='.$id_cat.'&cidReq='.$course_code.'&id_session='.api_get_session_id().'">'.
$modify_icons =
'<a href="gradebook_edit_cat.php?editcat='.$id_cat.'&cidReq='.$course_code.'&id_session='.api_get_session_id().'">'.
Display::return_icon('edit.png', $warning_message, [], ICON_SIZE_SMALL).'</a>';
$warning_message .= $modify_icons;
echo Display::return_message($warning_message, 'warning', false);
@ -853,7 +972,9 @@ class GradebookTable extends SortableTable
if (empty($certificate_min_score) ||
($certificate_min_score > $weight_category)
) {
$warning_message .= $course_code.'&nbsp;-&nbsp;'.get_lang('CertificateMinimunScoreIsRequiredAndMustNotBeMoreThan').'&nbsp;'.$weight_category.'<br />';
$warning_message .= $course_code.
'&nbsp;-&nbsp;'.get_lang('CertificateMinimunScoreIsRequiredAndMustNotBeMoreThan').
'&nbsp;'.$weight_category.'<br />';
}
}
@ -864,27 +985,6 @@ class GradebookTable extends SortableTable
}
}
if (!$this->teacherView) {
/*$rowTotal = [];
$rowTotal[] = ' ';
$rowTotal[] = '<strong>'.get_lang('FinalScore').'</strong>';
if (!$this->exportToPdf) {
$rowTotal[] = ' ';
}
$rowTotal[] = ' ';
$rowTotal[] = $scoredisplay->display_score(
$totalUserResult,
SCORE_DIV_PERCENT_WITH_CUSTOM
);
foreach ($this->loadStats as $col) {
$rowTotal[] = ' ';
}
$sortable_data[] = $rowTotal;*/
}
return $sortable_data;
}

@ -1,6 +1,8 @@
<?php
/* For licensing terms, see /license.txt */
use ChamiloSession as Session;
/**
* Class GradebookDataGenerator
* Class to select, sort and transform object data into array data,
@ -24,8 +26,9 @@ class GradebookDataGenerator
public $userId;
public $hidePercentage = false;
private $items;
public $items;
private $evals_links;
public $preLoadDataKey;
/**
* @param array $cats
@ -113,7 +116,9 @@ class GradebookDataGenerator
// Generate the data to display
$data = [];
$totalWeight = 0;
$allowStats = api_get_configuration_value('allow_gradebook_stats');
$scoreDisplay = ScoreDisplay::instance();
$defaultData = Session::read($this->preLoadDataKey);
/** @var GradebookItem $item */
foreach ($visibleItems as $item) {
@ -124,91 +129,214 @@ class GradebookDataGenerator
// on mouseover (https://support.chamilo.org/issues/6588)
$row[] = '<span title="'.api_remove_tags_with_space($item->get_description()).'">'.
api_get_short_text_from_html($item->get_description(), 160).'</span>';
$totalWeight += $item->get_weight();
$row[] = $item->get_weight();
$item->setStudentList($studentList);
$itemType = get_class($item);
switch ($itemType) {
case 'Evaluation':
// Items inside a category.
$resultColumn = $this->build_result_column(
$userId,
$item,
$ignore_score_color
);
if (get_class($item) == 'Evaluation') {
// Items inside a category.
$resultColumn = $this->build_result_column(
$userId,
$item,
$ignore_score_color
);
$row[] = $resultColumn['display'];
$row['result_score'] = $resultColumn['score'];
$row['result_score_weight'] = $resultColumn['score_weight'];
// Best
$best = $this->buildBestResultColumn($item);
$row['best'] = $best['display'];
$row['best_score'] = $best['score'];
// Average
$average = $this->buildAverageResultColumn($item);
$row['average'] = $average['display'];
$row['average_score'] = $average['score'];
// Ranking
$ranking = $this->buildRankingColumn($item, $userId, $userCount);
$row['ranking'] = $ranking['display'];
$row['ranking_score'] = $ranking['score'];
$row[] = $item;
} else {
// Category.
$result = $this->build_result_column(
$userId,
$item,
$ignore_score_color,
true
);
$row[] = $result['display'];
$row['result_score'] = $result['score'];
$row['result_score_weight'] = $result['score'];
// Best
$best = $this->buildBestResultColumn($item);
$row['best'] = $best['display'];
$row['best_score'] = $best['score'];
$rankingStudentList = [];
$invalidateResults = true;
// Average
$average = $this->buildAverageResultColumn($item);
$row['average'] = $average['display'];
$row['average_score'] = $average['score'];
// Ranking
if (!empty($studentList)) {
foreach ($studentList as $user) {
$score = $this->build_result_column(
$user['user_id'],
$item,
$ignore_score_color,
true
);
if (!empty($score['score'][0])) {
$invalidateResults = false;
$row[] = $resultColumn['display'];
$row['result_score'] = $resultColumn['score'];
$row['result_score_weight'] = $resultColumn['score_weight'];
// Best
if (isset($defaultData[$item->get_id()]) && isset($defaultData[$item->get_id()]['best'])) {
$best = $defaultData[$item->get_id()]['best'];
} else {
$best = $this->buildBestResultColumn($item);
}
$row['best'] = $best['display'];
$row['best_score'] = $best['score'];
// Average
if (isset($defaultData[$item->get_id()]) && isset($defaultData[$item->get_id()]['average'])) {
$average = $defaultData[$item->get_id()]['average'];
} else {
$average = $this->buildBestResultColumn($item);
}
$row['average'] = $average['display'];
$row['average_score'] = $average['score'];
// Ranking
$ranking = $this->buildRankingColumn($item, $userId, $userCount);
$row['ranking'] = $ranking['display'];
$row['ranking_score'] = $ranking['score'];
$row[] = $item;
break;
case 'ExerciseLink':
/** @var ExerciseLink $item */
// Category.
$result = $this->build_result_column(
$userId,
$item,
$ignore_score_color,
true
);
$row[] = $result['display'];
$row['result_score'] = $result['score'];
$row['result_score_weight'] = $result['score'];
// Best
if (isset($defaultData[$item->get_id()]) && isset($defaultData[$item->get_id()]['best'])) {
$best = $defaultData[$item->get_id()]['best'];
} else {
$best = $this->buildBestResultColumn($item);
}
$row['best'] = $best['display'];
$row['best_score'] = $best['score'];
$rankingStudentList = [];
$invalidateResults = false;
$debug = $item->get_id() == 1177;
// Average
if (isset($defaultData[$item->get_id()]) && isset($defaultData[$item->get_id()]['average'])) {
$average = $defaultData[$item->get_id()]['average'];
} else {
$average = $this->buildAverageResultColumn($item);
}
$row['average'] = $average['display'];
$row['average_score'] = $average['score'];
// Ranking
if ($allowStats) {
// Ranking
if (isset($defaultData[$item->get_id()]) && isset($defaultData[$item->get_id()]['ranking'])) {
$rankingStudentList = $defaultData[$item->get_id()]['ranking'];
$invalidateResults = $defaultData[$item->get_id()]['ranking_invalidate'];
$score = AbstractLink::getCurrentUserRanking($userId, $rankingStudentList);
} else {
if (!empty($studentList)) {
foreach ($studentList as $user) {
$score = $this->build_result_column(
$user['user_id'],
$item,
$ignore_score_color,
true
);
if (!empty($score['score'][0])) {
$invalidateResults = false;
}
$rankingStudentList[$user['user_id']] = $score['score'][0];
}
$defaultData[$item->get_id()]['ranking'] = $rankingStudentList;
$defaultData[$item->get_id()]['ranking_invalidate'] = $invalidateResults;
Session::write($this->preLoadDataKey, $defaultData);
}
$score = AbstractLink::getCurrentUserRanking($userId, $rankingStudentList);
}
$rankingStudentList[$user['user_id']] = $score['score'][0];
} else {
if (!empty($studentList)) {
foreach ($studentList as $user) {
$score = $this->build_result_column(
$user['user_id'],
$item,
$ignore_score_color,
true
);
if (!empty($score['score'][0])) {
$invalidateResults = false;
}
$rankingStudentList[$user['user_id']] = $score['score'][0];
}
}
$score = AbstractLink::getCurrentUserRanking($userId, $rankingStudentList);
}
}
$scoreDisplay = ScoreDisplay::instance();
$score = AbstractLink::getCurrentUserRanking($userId, $rankingStudentList);
$row['ranking'] = $scoreDisplay->display_score(
$score,
SCORE_DIV,
SCORE_BOTH,
true,
true
);
if ($invalidateResults) {
$row['ranking'] = null;
}
$row['ranking'] = $scoreDisplay->display_score(
$score,
SCORE_DIV,
SCORE_BOTH,
true,
true
);
if ($invalidateResults) {
$row['ranking'] = null;
}
break;
default:
// Category.
$result = $this->build_result_column(
$userId,
$item,
$ignore_score_color,
true
);
$row[] = $result['display'];
$row['result_score'] = $result['score'];
$row['result_score_weight'] = $result['score'];
// Best
if (isset($defaultData[$item->get_id()]) && isset($defaultData[$item->get_id()]['best'])) {
$best = $defaultData[$item->get_id()]['best'];
} else {
$best = $this->buildBestResultColumn($item);
}
$row['best'] = $best['display'];
$row['best_score'] = $best['score'];
$rankingStudentList = [];
$invalidateResults = true;
// Average
if (isset($defaultData[$item->get_id()]) && isset($defaultData[$item->get_id()]['average'])) {
$average = $defaultData[$item->get_id()]['average'];
} else {
$average = $this->buildAverageResultColumn($item);
}
$row['average'] = $average['display'];
$row['average_score'] = $average['score'];
// Ranking
if (isset($defaultData[$item->get_id()]) && isset($defaultData[$item->get_id()]['ranking'])) {
$rankingStudentList = $defaultData[$item->get_id()]['ranking'];
$invalidateResults = $defaultData[$item->get_id()]['ranking_invalidate'];
$invalidateResults = false;
$score = AbstractLink::getCurrentUserRanking($userId, $rankingStudentList);
} else {
if (!empty($studentList)) {
foreach ($studentList as $user) {
$score = $this->build_result_column(
$user['user_id'],
$item,
$ignore_score_color,
true
);
if (!empty($score['score'][0])) {
$invalidateResults = false;
}
$rankingStudentList[$user['user_id']] = $score['score'][0];
}
}
error_log('loading not cACHE');
$score = AbstractLink::getCurrentUserRanking($userId, $rankingStudentList);
}
$row['ranking'] = $scoreDisplay->display_score(
$score,
SCORE_DIV,
SCORE_BOTH,
true,
true
);
if ($invalidateResults) {
$row['ranking'] = null;
}
break;
}
$data[] = $row;
}
@ -244,7 +372,7 @@ class GradebookDataGenerator
*
* @return int
*/
public function sort_by_name($item1, $item2)
public static function sort_by_name($item1, $item2)
{
return api_strnatcmp($item1->get_name(), $item2->get_name());
}
@ -345,7 +473,7 @@ class GradebookDataGenerator
*
* @return array
*/
private function buildBestResultColumn(GradebookItem $item)
public function buildBestResultColumn(GradebookItem $item)
{
$score = $item->calc_score(
null,
@ -382,7 +510,7 @@ class GradebookDataGenerator
*
* @return array
*/
private function buildAverageResultColumn(GradebookItem $item)
public function buildAverageResultColumn(GradebookItem $item)
{
$score = $item->calc_score(null, 'average');
$scoreDisplay = ScoreDisplay::instance();
@ -421,7 +549,7 @@ class GradebookDataGenerator
*
* @return array
*/
private function buildRankingColumn(GradebookItem $item, $userId = null, $userCount = 0)
public function buildRankingColumn(GradebookItem $item, $userId = null, $userCount = 0)
{
$score = $item->calc_score($userId, 'ranking');
$score[1] = $userCount;
@ -448,9 +576,9 @@ class GradebookDataGenerator
* @param GradebookItem $item
* @param bool $ignore_score_color
*
* @return string|null
* @return null|string
*/
private function build_result_column(
public function build_result_column(
$userId,
$item,
$ignore_score_color,
@ -491,7 +619,6 @@ class GradebookDataGenerator
];
}
break;
// evaluation and link
case 'E':
case 'L':
//if ($parentId == 0) {

@ -2847,6 +2847,7 @@ HOTSPOT;
$weight = float_format($weight, 1, $decimalSeparator, $thousandSeparator);
}
$html = '';
if ($show_percentage) {
$percentageSign = '%';
if ($hidePercentageSign) {
@ -4828,6 +4829,15 @@ EOT;
$exercise_stat_info['exe_duration'],
$question_list
);
$allowStats = api_get_configuration_value('allow_gradebook_stats');
if ($allowStats) {
$objExercise->generateStats(
$objExercise->selectId(),
api_get_course_info(),
api_get_session_id()
);
}
}
}

@ -147,12 +147,17 @@ class PDF
$html = $tpl->fetch($tableTemplate);
$html = api_utf8_encode($html);
if ($returnHtml) {
return $html;
}
$css_file = api_get_path(SYS_CSS_PATH).'themes/'.$tpl->theme.'/print.css';
if (!file_exists($css_file)) {
$css_file = api_get_path(SYS_CSS_PATH).'print.css';
}
$css = file_get_contents($css_file);
$html = self::content_to_pdf(
self::content_to_pdf(
$html,
$css,
$this->params['filename'],
@ -163,10 +168,6 @@ class PDF
$returnHtml,
$addDefaultCss
);
if ($returnHtml) {
return $html;
}
}
/**
@ -382,7 +383,7 @@ class PDF
return false;
}
//clean styles and javascript document
// clean styles and javascript document
$clean_search = [
'@<script[^>]*?>.*?</script>@si',
'@<style[^>]*?>.*?</style>@siU',

@ -73,12 +73,21 @@ function api_set_encoding_html(&$string, $encoding)
}
} else {
$count = 1;
$string = str_ireplace(
'</head>',
'<meta http-equiv="Content-Type" content="text/html; charset='.$encoding.'"/></head>',
$string,
$count
);
if (strpos('</head>', strtolower($string)) !== false) {
$string = str_ireplace(
'</head>',
'<meta http-equiv="Content-Type" content="text/html; charset='.$encoding.'"/></head>',
$string,
$count
);
} else {
$string = str_ireplace(
'<body>',
'<head><meta http-equiv="Content-Type" content="text/html; charset='.$encoding.'"/></head><body>',
$string,
$count
);
}
}
$string = api_convert_encoding($string, $encoding, $old_encoding);
}

@ -1082,6 +1082,13 @@ VALUES (2, 13, 'session_courses_read_only_mode', 'Lock Course In Session', 1, 1,
// Allow to session admins login as teachers
//$_configuration['allow_session_admin_login_as_teacher'] = false;
// Allow gradebook stats
// Requires to edit the GradebookLink.php And GradebookEvaluation.php files adding the "@" in the ORM phpdoc block
/* ALTER TABLE gradebook_link ADD score_weight DOUBLE PRECISION DEFAULT NULL, ADD average_score DOUBLE PRECISION DEFAULT NULL, ADD best_score DOUBLE PRECISION DEFAULT NULL, ADD user_score_list LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)' ;
ALTER TABLE gradebook_evaluation ADD average_score DOUBLE PRECISION DEFAULT NULL, ADD best_score DOUBLE PRECISION DEFAULT NULL, ADD user_score_list LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)' ;
//$_configuration['allow_gradebook_stats'] = false;
// Hide social media links
//$_configuration['hide_social_media_links'] = false;

@ -99,6 +99,34 @@ class GradebookEvaluation
*/
protected $locked;
/**
* @var float
*
* ORM\Column(name="best_score", type="float", precision=6, scale=2, nullable=true)
*/
protected $bestScore;
/**
* @var float
*
* ORM\Column(name="average_score", type="float", precision=6, scale=2, nullable=true)
*/
protected $averageScore;
/**
* @var float
*
* ORM\Column(name="score_weight", type="float", precision=6, scale=2, nullable=true)
*/
protected $scoreWeight;
/**
* @var array
*
* ORM\Column(name="user_score_list", type="array", nullable=true)
*/
protected $userScoreList;
/**
* Set name.
*
@ -372,4 +400,88 @@ class GradebookEvaluation
{
return $this->id;
}
/**
* @return float
*/
public function getBestScore()
{
return $this->bestScore;
}
/**
* @param float $bestScore
*
* @return GradebookEvaluation
*/
public function setBestScore($bestScore)
{
$this->bestScore = $bestScore;
return $this;
}
/**
* @return float
*/
public function getAverageScore()
{
return $this->averageScore;
}
/**
* @param float $averageScore
*
* @return GradebookEvaluation
*/
public function setAverageScore($averageScore)
{
$this->averageScore = $averageScore;
return $this;
}
/**
* @return array
*/
public function getUserScoreList()
{
if (empty($this->userScoreList)) {
return [];
}
return $this->userScoreList;
}
/**
* @param array $userScoreList
*
* @return GradebookEvaluation
*/
public function setUserScoreList($userScoreList)
{
$this->userScoreList = $userScoreList;
return $this;
}
/**
* @return float
*/
public function getScoreWeight()
{
return $this->scoreWeight;
}
/**
* @param float $scoreWeight
*
* @return GradebookEvaluation
*/
public function setScoreWeight($scoreWeight)
{
$this->scoreWeight = $scoreWeight;
return $this;
}
}

@ -85,6 +85,34 @@ class GradebookLink
*/
protected $locked;
/**
* @var float
*
* ORM\Column(name="best_score", type="float", precision=6, scale=2, nullable=true)
*/
protected $bestScore;
/**
* @var float
*
* ORM\Column(name="average_score", type="float", precision=6, scale=2, nullable=true)
*/
protected $averageScore;
/**
* @var float
*
* ORM\Column(name="score_weight", type="float", precision=6, scale=2, nullable=true)
*/
protected $scoreWeight;
/**
* @var array
*
* ORM\Column(name="user_score_list", type="array", nullable=true)
*/
protected $userScoreList;
/**
* Set type.
*
@ -310,4 +338,88 @@ class GradebookLink
{
return $this->id;
}
/**
* @return float
*/
public function getBestScore()
{
return $this->bestScore;
}
/**
* @param float $bestScore
*
* @return GradebookLink
*/
public function setBestScore($bestScore)
{
$this->bestScore = $bestScore;
return $this;
}
/**
* @return float
*/
public function getAverageScore()
{
return $this->averageScore;
}
/**
* @param float $averageScore
*
* @return GradebookLink
*/
public function setAverageScore($averageScore)
{
$this->averageScore = $averageScore;
return $this;
}
/**
* @return array
*/
public function getUserScoreList()
{
if (empty($this->userScoreList)) {
return [];
}
return $this->userScoreList;
}
/**
* @param array $userScoreList
*
* @return GradebookLink
*/
public function setUserScoreList($userScoreList)
{
$this->userScoreList = $userScoreList;
return $this;
}
/**
* @return float
*/
public function getScoreWeight()
{
return $this->scoreWeight;
}
/**
* @param float $scoreWeight
*
* @return GradebookLink
*/
public function setScoreWeight($scoreWeight)
{
$this->scoreWeight = $scoreWeight;
return $this;
}
}

Loading…
Cancel
Save