gradebooktable.class.php 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183
  1. <?php
  2. /* For licensing terms, see license.txt */
  3. use CpChart\Cache as pCache;
  4. use CpChart\Data as pData;
  5. use CpChart\Image as pImage;
  6. /**
  7. * GradebookTable Class
  8. * Table to display categories, evaluations and links.
  9. *
  10. * @author Stijn Konings
  11. * @author Bert Steppé (refactored, optimised)
  12. *
  13. * @package chamilo.gradebook
  14. */
  15. class GradebookTable extends SortableTable
  16. {
  17. public $cats;
  18. public $exportToPdf;
  19. public $teacherView;
  20. public $userId;
  21. public $studentList = [];
  22. private $currentcat;
  23. private $datagen;
  24. private $evals_links;
  25. private $dataForGraph;
  26. /**
  27. * @var array Indicates which columns should be shown in gradebook
  28. *
  29. * @example [1] For add Ranking column
  30. * [2] For add Best Score column
  31. * [3] For add Average column
  32. */
  33. private $loadStats = [];
  34. /**
  35. * GradebookTable constructor.
  36. *
  37. * @param Category $currentcat
  38. * @param array $cats
  39. * @param array $evals
  40. * @param array $links
  41. * @param null $addparams
  42. * @param bool $exportToPdf
  43. * @param null $showTeacherView
  44. * @param int $userId
  45. * @param array $studentList
  46. * @param array $loadStats
  47. */
  48. public function __construct(
  49. $currentcat,
  50. $cats = [],
  51. $evals = [],
  52. $links = [],
  53. $addparams = null,
  54. $exportToPdf = false,
  55. $showTeacherView = null,
  56. $userId = null,
  57. $studentList = [],
  58. array $loadStats = []
  59. ) {
  60. $this->teacherView = is_null($showTeacherView) ? api_is_allowed_to_edit(null, true) : $showTeacherView;
  61. $this->userId = is_null($userId) ? api_get_user_id() : $userId;
  62. $this->exportToPdf = $exportToPdf;
  63. parent::__construct(
  64. 'gradebooklist',
  65. null,
  66. null,
  67. api_is_allowed_to_edit() ? 1 : 0,
  68. 20,
  69. 'ASC',
  70. 'gradebook_list'
  71. );
  72. $this->evals_links = array_merge($evals, $links);
  73. $this->currentcat = $currentcat;
  74. $this->cats = $cats;
  75. $this->loadStats = $loadStats;
  76. $this->datagen = new GradebookDataGenerator($cats, $evals, $links);
  77. $this->datagen->hidePercentage = api_get_configuration_value('hide_gradebook_percentage_user_result');
  78. if (!empty($userId)) {
  79. $this->datagen->userId = $userId;
  80. }
  81. if (isset($addparams)) {
  82. $this->set_additional_parameters($addparams);
  83. }
  84. $column = 0;
  85. if ($this->teacherView) {
  86. if ($this->exportToPdf == false) {
  87. $this->set_header($column++, '', '', 'width="25px"');
  88. }
  89. }
  90. $this->set_header($column++, get_lang('Type'), '', 'width="35px"');
  91. $this->set_header($column++, get_lang('Name'), false);
  92. if ($this->exportToPdf == false) {
  93. $this->set_header($column++, get_lang('Description'), false);
  94. }
  95. $model = ExerciseLib::getCourseScoreModel();
  96. $this->set_header(
  97. $column++,
  98. get_lang('Weight'),
  99. '',
  100. 'width="100px"'
  101. );
  102. if (!$this->teacherView) {
  103. $this->set_header($column++, get_lang('Result'), false);
  104. }
  105. if (empty($model)) {
  106. if (in_array(1, $this->loadStats)) {
  107. $this->set_header($column++, get_lang('Ranking'), false);
  108. }
  109. if (in_array(2, $this->loadStats)) {
  110. $this->set_header($column++, get_lang('BestScore'), false);
  111. }
  112. if (in_array(3, $this->loadStats)) {
  113. $this->set_header($column++, get_lang('Average'), false);
  114. }
  115. }
  116. if ($this->teacherView) {
  117. } else {
  118. if (!empty($cats)) {
  119. if ($this->exportToPdf == false) {
  120. $this->set_header($column++, get_lang('Actions'), false);
  121. }
  122. }
  123. }
  124. // Deactivates the odd/even alt rows in order that the +/- buttons work see #4047
  125. $this->odd_even_rows_enabled = false;
  126. // Admins get an edit column.
  127. if ($this->teacherView) {
  128. $this->set_header($column++, get_lang('Modify'), false, 'width="195px"');
  129. // Actions on multiple selected documents.
  130. $this->set_form_actions(
  131. [
  132. 'setvisible' => get_lang('SetVisible'),
  133. 'setinvisible' => get_lang('SetInvisible'),
  134. 'deleted' => get_lang('DeleteSelected'),
  135. ]
  136. );
  137. } else {
  138. if (empty($_GET['selectcat']) && !$this->teacherView) {
  139. if ($this->exportToPdf == false) {
  140. $this->set_header(
  141. $column++,
  142. get_lang('Certificates'),
  143. false
  144. );
  145. }
  146. }
  147. }
  148. }
  149. /**
  150. * @return GradebookDataGenerator
  151. */
  152. public function get_data()
  153. {
  154. return $this->datagen;
  155. }
  156. /**
  157. * Function used by SortableTable to get total number of items in the table.
  158. *
  159. * @return int
  160. */
  161. public function get_total_number_of_items()
  162. {
  163. return $this->datagen->get_total_items_count();
  164. }
  165. /**
  166. * Function used by SortableTable to generate the data to display.
  167. *
  168. * @param int $from
  169. * @param int $per_page
  170. * @param int $column
  171. * @param string $direction
  172. * @param int $sort
  173. *
  174. * @return array|mixed
  175. */
  176. public function get_table_data($from = 1, $per_page = null, $column = null, $direction = null, $sort = null)
  177. {
  178. //variables load in index.php
  179. global $certificate_min_score;
  180. $isAllowedToEdit = api_is_allowed_to_edit();
  181. // determine sorting type
  182. $col_adjust = $isAllowedToEdit ? 1 : 0;
  183. // By id
  184. $this->column = 5;
  185. switch ($this->column) {
  186. // Type
  187. case 0 + $col_adjust:
  188. $sorting = GradebookDataGenerator::GDG_SORT_TYPE;
  189. break;
  190. case 1 + $col_adjust:
  191. $sorting = GradebookDataGenerator::GDG_SORT_NAME;
  192. break;
  193. case 2 + $col_adjust:
  194. $sorting = GradebookDataGenerator::GDG_SORT_DESCRIPTION;
  195. break;
  196. case 3 + $col_adjust:
  197. $sorting = GradebookDataGenerator::GDG_SORT_WEIGHT;
  198. break;
  199. case 4 + $col_adjust:
  200. $sorting = GradebookDataGenerator::GDG_SORT_DATE;
  201. break;
  202. case 5 + $col_adjust:
  203. $sorting = GradebookDataGenerator::GDG_SORT_ID;
  204. break;
  205. }
  206. if ($this->direction == 'DESC') {
  207. $sorting |= GradebookDataGenerator::GDG_SORT_DESC;
  208. } else {
  209. $sorting |= GradebookDataGenerator::GDG_SORT_ASC;
  210. }
  211. // Status of user in course.
  212. $user_id = $this->userId;
  213. $course_code = api_get_course_id();
  214. $session_id = api_get_session_id();
  215. $statusToFilter = 0;
  216. if (empty($session_id)) {
  217. $statusToFilter = STUDENT;
  218. }
  219. if (empty($this->studentList) && $this->loadStats) {
  220. $studentList = CourseManager::get_user_list_from_course_code(
  221. $course_code,
  222. $session_id,
  223. null,
  224. null,
  225. $statusToFilter
  226. );
  227. $this->studentList = $studentList;
  228. }
  229. $this->datagen->userId = $this->userId;
  230. $data_array = $this->datagen->get_data(
  231. $sorting,
  232. $from,
  233. $this->per_page,
  234. false,
  235. $this->studentList,
  236. $this->loadStats
  237. );
  238. // generate the data to display
  239. $sortable_data = [];
  240. $weight_total_links = 0;
  241. $main_cat = Category::load(
  242. null,
  243. null,
  244. $course_code,
  245. null,
  246. null,
  247. $session_id,
  248. 'ORDER BY id'
  249. );
  250. $total_categories_weight = 0;
  251. $scoredisplay = ScoreDisplay::instance();
  252. $totalUserResult = [0, 0];
  253. $totalBest = [0, 0];
  254. $totalAverage = [0, 0];
  255. $type = 'detail';
  256. if ($this->exportToPdf) {
  257. $type = 'simple';
  258. }
  259. $model = ExerciseLib::getCourseScoreModel();
  260. $userExerciseScoreInCategory = api_get_configuration_value(
  261. 'gradebook_use_exercise_score_settings_in_categories'
  262. );
  263. // Categories.
  264. if (!empty($data_array)) {
  265. foreach ($data_array as $data) {
  266. // list of items inside the gradebook (exercises, lps, forums, etc)
  267. $row = [];
  268. /** @var AbstractLink $item */
  269. $item = $mainCategory = $data[0];
  270. // If the item is invisible, wrap it in a span with class invisible
  271. $invisibility_span_open = $isAllowedToEdit && $item->is_visible() == '0' ? '<span class="text-muted">' : '';
  272. $invisibility_span_close = $isAllowedToEdit && $item->is_visible() == '0' ? '</span>' : '';
  273. // Id
  274. if ($this->teacherView) {
  275. if ($this->exportToPdf == false) {
  276. $row[] = $this->build_id_column($item);
  277. }
  278. }
  279. // Type.
  280. $row[] = $this->build_type_column($item);
  281. // Name.
  282. if (get_class($item) == 'Category') {
  283. $row[] = $invisibility_span_open.'<strong>'.$item->get_name().'</strong>'.$invisibility_span_close;
  284. $main_categories[$item->get_id()]['name'] = $item->get_name();
  285. } else {
  286. $name = $this->build_name_link($item, $type);
  287. $row[] = $invisibility_span_open.$name.$invisibility_span_close;
  288. $main_categories[$item->get_id()]['name'] = $name;
  289. }
  290. $this->dataForGraph['categories'][] = $item->get_name();
  291. $main_categories[$item->get_id()]['weight'] = $item->get_weight();
  292. $total_categories_weight += $item->get_weight();
  293. // Description.
  294. if ($this->exportToPdf == false) {
  295. $row[] = $invisibility_span_open.$data[2].$invisibility_span_close;
  296. }
  297. // Weight.
  298. $weight = $scoredisplay->display_score(
  299. [
  300. $data['3'],
  301. $this->currentcat->get_weight(),
  302. ],
  303. SCORE_SIMPLE,
  304. SCORE_BOTH,
  305. true
  306. );
  307. if ($this->teacherView) {
  308. $row[] = $invisibility_span_open.
  309. Display::tag('p', $weight, ['class' => 'score']).
  310. $invisibility_span_close;
  311. } else {
  312. $row[] = $invisibility_span_open.$weight.$invisibility_span_close;
  313. }
  314. $category_weight = $item->get_weight();
  315. $mainCategoryWeight = $main_cat[0]->get_weight();
  316. if ($this->teacherView) {
  317. $weight_total_links += $data[3];
  318. } else {
  319. $cattotal = Category::load($_GET['selectcat']);
  320. $scoretotal = $cattotal[0]->calc_score($this->userId);
  321. }
  322. // Edit (for admins).
  323. if ($this->teacherView) {
  324. $cat = new Category();
  325. $show_message = $cat->show_message_resource_delete($item->get_course_code());
  326. if ($show_message === false) {
  327. $row[] = $this->build_edit_column($item);
  328. }
  329. } else {
  330. $score = $item->calc_score($this->userId);
  331. $scoreToDisplay = '-';
  332. if (!empty($score[1])) {
  333. $completeScore = $scoredisplay->display_score($score, SCORE_DIV_PERCENT);
  334. $score = $score[0] / $score[1] * $item->get_weight();
  335. $score = $scoredisplay->display_score([$score, null], SCORE_SIMPLE);
  336. $scoreToDisplay = Display::tip($score, $completeScore);
  337. } else {
  338. $categoryScore = null;
  339. }
  340. // Students get the results and certificates columns
  341. if (1) {
  342. $value_data = isset($data[4]) ? $data[4] : null;
  343. $best = isset($data['best']) ? $data['best'] : null;
  344. $average = isset($data['average']) ? $data['average'] : null;
  345. $ranking = isset($data['ranking']) ? $data['ranking'] : null;
  346. $totalResult = [
  347. $data['result_score'][0],
  348. $data['result_score'][1],
  349. ];
  350. $totalUserResult[0] += $totalResult[0] / ($totalResult[1] ?: 1) * $data[3];
  351. $totalUserResult[1] += $data[3];
  352. $totalBest = [
  353. $scoredisplay->format_score($totalBest[0] + $data['best_score'][0]),
  354. $scoredisplay->format_score($totalBest[1] + $data['best_score'][1]),
  355. ];
  356. $totalAverage = [
  357. $data['average_score'][0],
  358. $data['average_score'][1],
  359. ];
  360. // Student result
  361. if (empty($model)) {
  362. $row[] = $value_data;
  363. } else {
  364. $row[] = ExerciseLib::show_score(
  365. $data['result_score'][0],
  366. $data['result_score'][1]
  367. );
  368. }
  369. $mode = SCORE_AVERAGE;
  370. if ($userExerciseScoreInCategory) {
  371. $mode = SCORE_SIMPLE;
  372. $result = ExerciseLib::convertScoreToPlatformSetting($totalAverage[0], $totalAverage[1]);
  373. $totalAverage[0] = $result['score'];
  374. $totalAverage[1] = $result['weight'];
  375. $result = ExerciseLib::convertScoreToPlatformSetting($totalResult[0], $totalResult[1]);
  376. $totalResult[0] = $result['score'];
  377. $totalResult[1] = $result['weight'];
  378. $result = ExerciseLib::convertScoreToPlatformSetting(
  379. $data['result_score'][0],
  380. $data['result_score'][1]
  381. );
  382. $data['my_result_no_float'][0] = $result['score'];
  383. }
  384. $totalResultAverageValue = strip_tags($scoredisplay->display_score($totalResult, $mode));
  385. $totalAverageValue = strip_tags($scoredisplay->display_score($totalAverage, $mode));
  386. $this->dataForGraph['my_result'][] = floatval($totalResultAverageValue);
  387. $this->dataForGraph['average'][] = floatval($totalAverageValue);
  388. $this->dataForGraph['my_result_no_float'][] = $data['result_score'][0];
  389. if (empty($model)) {
  390. // Ranking
  391. if (in_array(1, $this->loadStats)) {
  392. $row[] = $ranking;
  393. }
  394. // Best
  395. if (in_array(2, $this->loadStats)) {
  396. $row[] = $best;
  397. }
  398. // Average
  399. if (in_array(3, $this->loadStats)) {
  400. $row[] = $average;
  401. }
  402. }
  403. if (get_class($item) == 'Category') {
  404. if ($this->exportToPdf == false) {
  405. $row[] = $this->build_edit_column($item);
  406. }
  407. }
  408. } else {
  409. $row[] = $scoreToDisplay;
  410. if (!empty($this->cats)) {
  411. if ($this->exportToPdf == false) {
  412. $row[] = $this->build_edit_column($item);
  413. }
  414. }
  415. }
  416. }
  417. // Category added.
  418. $sortable_data[] = $row;
  419. // Loading children
  420. if (get_class($item) == 'Category') {
  421. $course_code = api_get_course_id();
  422. $session_id = api_get_session_id();
  423. $parent_id = $item->get_id();
  424. $cats = Category::load(
  425. $parent_id,
  426. null,
  427. null,
  428. null,
  429. null,
  430. null
  431. );
  432. if (isset($cats[0])) {
  433. /** @var Category $subCategory */
  434. $subCategory = $cats[0];
  435. $allcat = $subCategory->get_subcategories($this->userId, $course_code, $session_id);
  436. $alleval = $subCategory->get_evaluations($this->userId);
  437. $alllink = $subCategory->get_links($this->userId);
  438. $sub_cat_info = new GradebookDataGenerator($allcat, $alleval, $alllink);
  439. $sub_cat_info->userId = $user_id;
  440. $data_array2 = $sub_cat_info->get_data(
  441. $sorting,
  442. $from,
  443. $this->per_page,
  444. false,
  445. $this->studentList
  446. );
  447. $total_weight = 0;
  448. // Links.
  449. foreach ($data_array2 as $data) {
  450. $row = [];
  451. $item = $data[0];
  452. //if the item is invisible, wrap it in a span with class invisible
  453. $invisibility_span_open = $isAllowedToEdit && $item->is_visible() == '0' ? '<span class="text-muted">' : '';
  454. $invisibility_span_close = $isAllowedToEdit && $item->is_visible() == '0' ? '</span>' : '';
  455. if (isset($item)) {
  456. $main_categories[$parent_id]['children'][$item->get_id()]['name'] = $item->get_name();
  457. $main_categories[$parent_id]['children'][$item->get_id()]['weight'] = $item->get_weight();
  458. }
  459. if ($this->teacherView) {
  460. if ($this->exportToPdf == false) {
  461. $row[] = $this->build_id_column($item);
  462. }
  463. }
  464. // Type
  465. $row[] = $this->build_type_column($item, ['style' => 'padding-left:5px']);
  466. // Name.
  467. $row[] = $invisibility_span_open."&nbsp;&nbsp;&nbsp; ".
  468. $this->build_name_link($item, $type).$invisibility_span_close;
  469. // Description.
  470. if ($this->exportToPdf == false) {
  471. $row[] = $invisibility_span_open.$data[2].$invisibility_span_close;
  472. }
  473. $weight = $data[3];
  474. $total_weight += $weight;
  475. // Weight
  476. $row[] = $invisibility_span_open.$weight.$invisibility_span_close;
  477. if ($this->teacherView) {
  478. //$weight_total_links += intval($data[3]);
  479. } else {
  480. $cattotal = Category::load($_GET['selectcat']);
  481. $scoretotal = $cattotal[0]->calc_score($this->userId);
  482. }
  483. // Admins get an edit column.
  484. if (api_is_allowed_to_edit(null, true) &&
  485. isset($_GET['user_id']) == false &&
  486. (isset($_GET['action']) && $_GET['action'] != 'export_all' || !isset($_GET['action']))
  487. ) {
  488. $cat = new Category();
  489. $show_message = $cat->show_message_resource_delete($item->get_course_code());
  490. if ($show_message === false) {
  491. if ($this->exportToPdf == false) {
  492. $row[] = $this->build_edit_column($item);
  493. }
  494. }
  495. } else {
  496. // Students get the results and certificates columns
  497. $eval_n_links = array_merge($alleval, $alllink);
  498. if (count($eval_n_links) > 0) {
  499. $value_data = isset($data[4]) ? $data[4] : null;
  500. if (!is_null($value_data)) {
  501. // Result
  502. $row[] = $value_data;
  503. $best = isset($data['best']) ? $data['best'] : null;
  504. $average = isset($data['average']) ? $data['average'] : null;
  505. $ranking = isset($data['ranking']) ? $data['ranking'] : null;
  506. if (in_array(1, $this->loadStats)) {
  507. // Ranking
  508. $row[] = $ranking;
  509. }
  510. if (in_array(2, $this->loadStats)) {
  511. // Best
  512. $row[] = $best;
  513. }
  514. // Average
  515. if (in_array(3, $this->loadStats)) {
  516. $row[] = $average;
  517. }
  518. }
  519. }
  520. if (!empty($cats)) {
  521. if ($this->exportToPdf == false) {
  522. $row[] = null;
  523. }
  524. }
  525. }
  526. if ($this->exportToPdf == false) {
  527. $row['child_of'] = $parent_id;
  528. }
  529. $sortable_data[] = $row;
  530. }
  531. // "Warning row"
  532. if (!empty($data_array)) {
  533. if ($this->teacherView) {
  534. // Compare the category weight to the sum of all weights inside the category
  535. if (intval($total_weight) == $category_weight) {
  536. $label = null;
  537. $total = GradebookUtils::score_badges(
  538. [
  539. $total_weight.' / '.$category_weight,
  540. '100',
  541. ]
  542. );
  543. } else {
  544. $label = Display::return_icon(
  545. 'warning.png',
  546. sprintf(get_lang('TotalWeightMustBeX'), $category_weight)
  547. );
  548. $total = Display::badge($total_weight.' / '.$category_weight, 'warning');
  549. }
  550. $row = [
  551. null,
  552. null,
  553. "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<h5>".get_lang('SubTotal').'</h5>',
  554. null,
  555. $total.' '.$label,
  556. 'child_of' => $parent_id,
  557. ];
  558. $sortable_data[] = $row;
  559. }
  560. }
  561. }
  562. }
  563. }
  564. } //end looping categories
  565. $main_weight = 0;
  566. if (count($main_cat) > 1) {
  567. /** @var Category $myCat */
  568. foreach ($main_cat as $myCat) {
  569. $myParentId = $myCat->get_parent_id();
  570. if ($myParentId == 0) {
  571. $main_weight = intval($myCat->get_weight());
  572. }
  573. }
  574. }
  575. if ($this->teacherView) {
  576. // Total for teacher.
  577. if (count($main_cat) > 1) {
  578. if (intval($total_categories_weight) == $main_weight) {
  579. $total = GradebookUtils::score_badges(
  580. [
  581. $total_categories_weight.' / '.$main_weight,
  582. '100',
  583. ]
  584. );
  585. } else {
  586. $total = Display::badge($total_categories_weight.' / '.$main_weight, 'warning');
  587. }
  588. $row = [
  589. null,
  590. null,
  591. '<strong>'.get_lang('Total').'</strong>',
  592. null,
  593. $total,
  594. ];
  595. $sortable_data[] = $row;
  596. }
  597. } else {
  598. // Total for student.
  599. if (count($main_cat) > 1) {
  600. $weights = [];
  601. foreach ($main_categories as $cat) {
  602. $weights[] = $cat['weight'];
  603. }
  604. $main_weight = intval($main_cat[0]->get_weight());
  605. $global = null;
  606. $average = null;
  607. $myTotal = 0;
  608. foreach ($this->dataForGraph['my_result_no_float'] as $result) {
  609. $myTotal += $scoredisplay->format_score($result);
  610. }
  611. $totalResult[0] = $myTotal;
  612. // Overwrite main weight
  613. $totalResult[1] = $main_weight;
  614. $totalResult = $scoredisplay->display_score(
  615. $totalResult,
  616. SCORE_DIV
  617. );
  618. $row = [
  619. null,
  620. '<strong>'.get_lang('Total').'</strong>',
  621. ];
  622. if (!$this->exportToPdf) {
  623. $row[] = null;
  624. }
  625. $row[] = $main_weight;
  626. $row[] = $totalResult;
  627. if (in_array(1, $this->loadStats)) {
  628. $totalRanking = [];
  629. $invalidateRanking = true;
  630. $average = 0;
  631. $main_cat[0]->setStudentList($this->studentList);
  632. foreach ($this->studentList as $student) {
  633. $score = $main_cat[0]->calc_score(
  634. $student['user_id'],
  635. null,
  636. api_get_course_id(),
  637. api_get_session_id()
  638. );
  639. if (!empty($score[0])) {
  640. $invalidateRanking = false;
  641. }
  642. $totalRanking[$student['user_id']] = $score[0];
  643. $average += $score[0];
  644. }
  645. $totalRanking = AbstractLink::getCurrentUserRanking($user_id, $totalRanking);
  646. $totalRanking = $scoredisplay->display_score(
  647. $totalRanking,
  648. SCORE_DIV,
  649. SCORE_BOTH,
  650. true,
  651. true
  652. );
  653. if ($invalidateRanking) {
  654. $totalRanking = null;
  655. }
  656. $row[] = $totalRanking;
  657. }
  658. if (in_array(2, $this->loadStats)) {
  659. // Overwrite main weight
  660. $totalBest[1] = $main_weight;
  661. $totalBest = $scoredisplay->display_score(
  662. $totalBest,
  663. SCORE_DIV,
  664. SCORE_BOTH,
  665. true
  666. );
  667. $row[] = $totalBest;
  668. }
  669. if (in_array(3, $this->loadStats)) {
  670. // Overwrite main weight
  671. $totalAverage[0] = $average / count($this->studentList);
  672. $totalAverage[1] = $main_weight;
  673. $totalAverage = $scoredisplay->display_score(
  674. $totalAverage,
  675. SCORE_DIV,
  676. SCORE_BOTH,
  677. true
  678. );
  679. $row[] = $totalAverage;
  680. }
  681. if (!empty($row)) {
  682. $sortable_data[] = $row;
  683. }
  684. }
  685. }
  686. // Warning messages
  687. $view = isset($_GET['view']) ? $_GET['view'] : null;
  688. if ($this->teacherView) {
  689. if (isset($_GET['selectcat']) &&
  690. $_GET['selectcat'] > 0 &&
  691. $view != 'presence'
  692. ) {
  693. $id_cat = intval($_GET['selectcat']);
  694. $category = Category::load($id_cat);
  695. $weight_category = intval($this->build_weight($category[0]));
  696. $course_code = $this->build_course_code($category[0]);
  697. $weight_total_links = round($weight_total_links);
  698. if ($weight_total_links > $weight_category ||
  699. $weight_total_links < $weight_category ||
  700. $weight_total_links > $weight_category
  701. ) {
  702. $warning_message = sprintf(get_lang('TotalWeightMustBeX'), $weight_category);
  703. $modify_icons = '<a href="gradebook_edit_cat.php?editcat='.$id_cat.'&cidReq='.$course_code.'&id_session='.api_get_session_id().'">'.
  704. Display::return_icon('edit.png', $warning_message, [], ICON_SIZE_SMALL).'</a>';
  705. $warning_message .= $modify_icons;
  706. echo Display::return_message($warning_message, 'warning', false);
  707. }
  708. $content_html = DocumentManager::replace_user_info_into_html(
  709. api_get_user_id(),
  710. $course_code,
  711. api_get_session_id()
  712. );
  713. if (!empty($content_html)) {
  714. $new_content = explode('</head>', $content_html['content']);
  715. }
  716. if (empty($new_content[0])) {
  717. // Set default certificate
  718. $courseData = api_get_course_info($course_code);
  719. DocumentManager::generateDefaultCertificate($courseData);
  720. }
  721. }
  722. if (empty($_GET['selectcat'])) {
  723. $categories = Category::load();
  724. $weight_categories = $certificate_min_scores = $course_codes = [];
  725. foreach ($categories as $category) {
  726. $course_code_category = $this->build_course_code($category);
  727. if (!empty($course_code)) {
  728. if ($course_code_category == $course_code) {
  729. $weight_categories[] = intval($this->build_weight($category));
  730. $certificate_min_scores[] = intval($this->build_certificate_min_score($category));
  731. $course_codes[] = $course_code;
  732. break;
  733. }
  734. } else {
  735. $weight_categories[] = intval($this->build_weight($category));
  736. $certificate_min_scores[] = intval($this->build_certificate_min_score($category));
  737. $course_codes[] = $course_code_category;
  738. }
  739. }
  740. if (is_array($weight_categories) &&
  741. is_array($certificate_min_scores) &&
  742. is_array($course_codes)
  743. ) {
  744. $warning_message = '';
  745. for ($x = 0; $x < count($weight_categories); $x++) {
  746. $weight_category = intval($weight_categories[$x]);
  747. $certificate_min_score = intval($certificate_min_scores[$x]);
  748. $course_code = $course_codes[$x];
  749. if (empty($certificate_min_score) ||
  750. ($certificate_min_score > $weight_category)
  751. ) {
  752. $warning_message .= $course_code.'&nbsp;-&nbsp;'.get_lang('CertificateMinimunScoreIsRequiredAndMustNotBeMoreThan').'&nbsp;'.$weight_category.'<br />';
  753. }
  754. }
  755. if (!empty($warning_message)) {
  756. echo Display::return_message($warning_message, 'warning', false);
  757. }
  758. }
  759. }
  760. }
  761. if (!$this->teacherView) {
  762. /*$rowTotal = [];
  763. $rowTotal[] = ' ';
  764. $rowTotal[] = '<strong>'.get_lang('FinalScore').'</strong>';
  765. if (!$this->exportToPdf) {
  766. $rowTotal[] = ' ';
  767. }
  768. $rowTotal[] = ' ';
  769. $rowTotal[] = $scoredisplay->display_score(
  770. $totalUserResult,
  771. SCORE_DIV_PERCENT_WITH_CUSTOM
  772. );
  773. foreach ($this->loadStats as $col) {
  774. $rowTotal[] = ' ';
  775. }
  776. $sortable_data[] = $rowTotal;*/
  777. }
  778. return $sortable_data;
  779. }
  780. /**
  781. * @return string
  782. */
  783. public function getGraph()
  784. {
  785. $data = $this->getDataForGraph();
  786. if (!empty($data) &&
  787. isset($data['categories']) &&
  788. isset($data['my_result']) &&
  789. isset($data['average'])
  790. ) {
  791. $dataSet = new pData();
  792. $dataSet->addPoints($data['my_result'], get_lang('Me'));
  793. // In order to generate random values
  794. // $data['average'] = array(rand(0,50), rand(0,50));
  795. $dataSet->addPoints($data['average'], get_lang('Average'));
  796. $dataSet->addPoints($data['categories'], 'categories');
  797. $dataSet->setAbscissa('categories');
  798. $xSize = 600;
  799. $ySize = 400;
  800. $pChart = new pImage($xSize, $ySize, $dataSet);
  801. /* Turn of Antialiasing */
  802. $pChart->Antialias = false;
  803. /* Add a border to the picture */
  804. $pChart->drawRectangle(
  805. 0,
  806. 0,
  807. $xSize - 1,
  808. $ySize - 1,
  809. ["R" => 0, "G" => 0, "B" => 0]
  810. );
  811. $pChart->drawText(
  812. 80,
  813. 16,
  814. get_lang('Results'),
  815. ["FontSize" => 11, "Align" => TEXT_ALIGN_BOTTOMMIDDLE]
  816. );
  817. $pChart->setGraphArea(50, 30, $xSize - 50, $ySize - 70);
  818. $pChart->setFontProperties(
  819. [
  820. 'FontName' => api_get_path(SYS_FONTS_PATH).'opensans/OpenSans-Regular.ttf',
  821. 'FontSize' => 10,
  822. ]
  823. );
  824. /* Draw the scale */
  825. $scaleSettings = [
  826. "XMargin" => AUTO,
  827. "YMargin" => 10,
  828. "Floating" => true,
  829. "GridR" => 200,
  830. "GridG" => 200,
  831. "GridB" => 200,
  832. "DrawSubTicks" => true,
  833. "CycleBackground" => true,
  834. 'LabelRotation' => 10,
  835. ];
  836. $pChart->drawScale($scaleSettings);
  837. /* Draw the line chart */
  838. $pChart->drawLineChart();
  839. $pChart->drawPlotChart(
  840. [
  841. "DisplayValues" => true,
  842. "PlotBorder" => true,
  843. "BorderSize" => 2,
  844. "Surrounding" => -60,
  845. "BorderAlpha" => 80,
  846. ]
  847. );
  848. /* Write the chart legend */
  849. $pChart->drawLegend(
  850. $xSize - 180,
  851. 9,
  852. [
  853. "Style" => LEGEND_NOBORDER,
  854. "Mode" => LEGEND_HORIZONTAL,
  855. "FontR" => 0,
  856. "FontG" => 0,
  857. "FontB" => 0,
  858. ]
  859. );
  860. $cachePath = api_get_path(SYS_ARCHIVE_PATH);
  861. $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
  862. $chartHash = $myCache->getHash($dataSet);
  863. $myCache->writeToCache($chartHash, $pChart);
  864. $imgSysPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
  865. $myCache->saveFromCache($chartHash, $imgSysPath);
  866. $imgWebPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
  867. if (file_exists($imgSysPath)) {
  868. $result = '<div id="contentArea" style="text-align: center;" >';
  869. $result .= '<img src="'.$imgWebPath.'" >';
  870. $result .= '</div>';
  871. return $result;
  872. }
  873. }
  874. return '';
  875. }
  876. /**
  877. * @return array
  878. */
  879. private function getDataForGraph()
  880. {
  881. return $this->dataForGraph;
  882. }
  883. /**
  884. * @param $item
  885. *
  886. * @return mixed
  887. */
  888. private function build_certificate_min_score($item)
  889. {
  890. return $item->getCertificateMinScore();
  891. }
  892. /**
  893. * @param $item
  894. *
  895. * @return mixed
  896. */
  897. private function build_weight($item)
  898. {
  899. return $item->get_weight();
  900. }
  901. /**
  902. * @param $item
  903. *
  904. * @return mixed
  905. */
  906. private function build_course_code($item)
  907. {
  908. return $item->get_course_code();
  909. }
  910. /**
  911. * @param $item
  912. *
  913. * @return string
  914. */
  915. private function build_id_column($item)
  916. {
  917. switch ($item->get_item_type()) {
  918. // category
  919. case 'C':
  920. return 'CATE'.$item->get_id();
  921. // evaluation
  922. case 'E':
  923. return 'EVAL'.$item->get_id();
  924. // link
  925. case 'L':
  926. return 'LINK'.$item->get_id();
  927. }
  928. }
  929. /**
  930. * @param $item
  931. * @param array $attributes
  932. *
  933. * @return string
  934. */
  935. private function build_type_column($item, $attributes = [])
  936. {
  937. return GradebookUtils::build_type_icon_tag($item->get_icon_name(), $attributes);
  938. }
  939. /**
  940. * Generate name column.
  941. *
  942. * @param GradebookItem $item
  943. * @param string $type simple|detail
  944. *
  945. * @return string
  946. */
  947. private function build_name_link($item, $type = 'detail')
  948. {
  949. $view = isset($_GET['view']) ? Security::remove_XSS($_GET['view']) : null;
  950. $categoryId = $item->getCategory()->get_id();
  951. switch ($item->get_item_type()) {
  952. // category
  953. case 'C':
  954. $prms_uri = '?selectcat='.$item->get_id().'&view='.$view;
  955. $isStudentView = api_is_student_view_active();
  956. if (isset($is_student) || $isStudentView) {
  957. $prms_uri = $prms_uri.'&amp;isStudentView=studentview';
  958. }
  959. $cat = new Category();
  960. $show_message = $cat->show_message_resource_delete($item->get_course_code());
  961. return '&nbsp;<a href="'.Category::getUrl().$prms_uri.'">'
  962. .$item->get_name()
  963. .'</a>'
  964. .($item->is_course() ? ' &nbsp;['.$item->get_course_code().']'.$show_message : '');
  965. // evaluation
  966. case 'E':
  967. $cat = new Category();
  968. $course_id = CourseManager::get_course_by_category($categoryId);
  969. $show_message = $cat->show_message_resource_delete($course_id);
  970. // course/platform admin can go to the view_results page
  971. if (api_is_allowed_to_edit() && $show_message === false) {
  972. if ($item->get_type() == 'presence') {
  973. return '&nbsp;'
  974. .'<a href="gradebook_view_result.php?cidReq='.$course_id.'&amp;selecteval='.$item->get_id().'">'
  975. .$item->get_name()
  976. .'</a>';
  977. } else {
  978. $extra = Display::label(get_lang('Evaluation'));
  979. if ($type == 'simple') {
  980. $extra = '';
  981. }
  982. return '&nbsp;'
  983. .'<a href="gradebook_view_result.php?'.api_get_cidreq().'&selecteval='.$item->get_id().'">'
  984. .$item->get_name()
  985. .'</a>&nbsp;'.$extra;
  986. }
  987. } elseif (ScoreDisplay::instance()->is_custom() && $show_message === false) {
  988. // students can go to the statistics page (if custom display enabled)
  989. return '&nbsp;'
  990. .'<a href="gradebook_statistics.php?'.api_get_cidreq().'&selecteval='.$item->get_id().'">'
  991. .$item->get_name()
  992. .'</a>';
  993. } elseif ($show_message === false && !api_is_allowed_to_edit() && !ScoreDisplay::instance()->is_custom()) {
  994. return '&nbsp;'
  995. .'<a href="gradebook_statistics.php?'.api_get_cidreq().'&selecteval='.$item->get_id().'">'
  996. .$item->get_name()
  997. .'</a>';
  998. } else {
  999. return '['.get_lang('Evaluation').']&nbsp;&nbsp;'.$item->get_name().$show_message;
  1000. }
  1001. // no break because of return
  1002. case 'L':
  1003. // link
  1004. $cat = new Category();
  1005. $course_id = CourseManager::get_course_by_category($categoryId);
  1006. $show_message = $cat->show_message_resource_delete($course_id);
  1007. $url = $item->get_link();
  1008. $text = $item->get_name();
  1009. if (isset($url) && $show_message === false) {
  1010. $text = '&nbsp;<a href="'.$item->get_link().'">'
  1011. .$item->get_name()
  1012. .'</a>';
  1013. }
  1014. $extra = Display::label($item->get_type_name(), 'info');
  1015. if ($type == 'simple') {
  1016. $extra = '';
  1017. }
  1018. $extra .= $item->getSkillsFromItem();
  1019. $text .= "&nbsp;".$extra.$show_message;
  1020. $cc = $this->currentcat->get_course_code();
  1021. if (empty($cc)) {
  1022. $text .= '&nbsp;[<a href="'.api_get_path(REL_COURSE_PATH).$item->get_course_code().'/">'.$item->get_course_code().'</a>]';
  1023. }
  1024. return $text;
  1025. }
  1026. }
  1027. /**
  1028. * @param AbstractLink $item
  1029. *
  1030. * @return null|string
  1031. */
  1032. private function build_edit_column($item)
  1033. {
  1034. switch ($item->get_item_type()) {
  1035. // category
  1036. case 'C':
  1037. return GradebookUtils::build_edit_icons_cat($item, $this->currentcat);
  1038. // evaluation
  1039. case 'E':
  1040. return GradebookUtils::build_edit_icons_eval($item, $this->currentcat->get_id());
  1041. // link
  1042. case 'L':
  1043. return GradebookUtils::build_edit_icons_link($item, $this->currentcat->get_id());
  1044. }
  1045. }
  1046. }