gradebooktable.class.php 48 KB

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