flatviewtable.class.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. set_time_limit(0);
  4. use CpChart\Cache as pCache;
  5. use CpChart\Data as pData;
  6. use CpChart\Image as pImage;
  7. /**
  8. * Class FlatViewTable
  9. * Table to display flat view (all evaluations and links for all students).
  10. *
  11. * @author Stijn Konings
  12. * @author Bert Steppé - (refactored, optimised)
  13. * @author Julio Montoya Armas - Gradebook Graphics
  14. *
  15. * @package chamilo.gradebook
  16. */
  17. class FlatViewTable extends SortableTable
  18. {
  19. public $datagen;
  20. private $selectcat;
  21. private $limit_enabled;
  22. private $offset;
  23. private $mainCourseCategory;
  24. /**
  25. * @param Category $selectcat
  26. * @param array $users
  27. * @param array $evals
  28. * @param array $links
  29. * @param bool $limit_enabled
  30. * @param int $offset
  31. * @param null $addparams
  32. * @param Category $mainCourseCategory
  33. */
  34. public function __construct(
  35. $selectcat,
  36. $users = [],
  37. $evals = [],
  38. $links = [],
  39. $limit_enabled = false,
  40. $offset = 0,
  41. $addparams = null,
  42. $mainCourseCategory = null
  43. ) {
  44. parent:: __construct(
  45. 'flatviewlist',
  46. null,
  47. null,
  48. api_is_western_name_order() ? 1 : 0
  49. );
  50. $this->selectcat = $selectcat;
  51. $this->datagen = new FlatViewDataGenerator(
  52. $users,
  53. $evals,
  54. $links,
  55. ['only_subcat' => $this->selectcat->get_id()],
  56. $mainCourseCategory
  57. );
  58. $this->limit_enabled = $limit_enabled;
  59. $this->offset = $offset;
  60. if (isset($addparams)) {
  61. $this->set_additional_parameters($addparams);
  62. }
  63. // step 2: generate rows: students
  64. $this->datagen->category = $this->selectcat;
  65. $this->mainCourseCategory = $mainCourseCategory;
  66. }
  67. /**
  68. * @param bool $value
  69. */
  70. public function setLimitEnabled($value)
  71. {
  72. $this->limit_enabled = (bool) $value;
  73. }
  74. /**
  75. * @return Category
  76. */
  77. public function getMainCourseCategory()
  78. {
  79. return $this->mainCourseCategory;
  80. }
  81. /**
  82. * Display gradebook graphs.
  83. */
  84. public function display_graph_by_resource()
  85. {
  86. $headerName = $this->datagen->get_header_names();
  87. $total_users = $this->datagen->get_total_users_count();
  88. $displayscore = ScoreDisplay::instance();
  89. $customdisplays = $displayscore->get_custom_score_display_settings();
  90. if (empty($customdisplays)) {
  91. echo get_lang('ToViewGraphScoreRuleMustBeEnabled');
  92. return '';
  93. }
  94. $user_results = $this->datagen->get_data_to_graph2(false);
  95. if (empty($user_results) || empty($total_users)) {
  96. echo get_lang('NoResults');
  97. return '';
  98. }
  99. // Removing first name
  100. array_shift($headerName);
  101. // Removing last name
  102. array_shift($headerName);
  103. // Removing username
  104. array_shift($headerName);
  105. $pre_result = $new_result = [];
  106. foreach ($user_results as $result) {
  107. for ($i = 0; $i < count($headerName); $i++) {
  108. if (isset($result[$i + 1])) {
  109. $pre_result[$i + 3][] = $result[$i + 1];
  110. }
  111. }
  112. }
  113. $i = 0;
  114. $resource_list = [];
  115. $pre_result2 = [];
  116. foreach ($pre_result as $key => $res_array) {
  117. rsort($res_array);
  118. $pre_result2[] = $res_array;
  119. }
  120. //@todo when a display custom does not exist the order of the color does not match
  121. //filling all the answer that are not responded with 0
  122. rsort($customdisplays);
  123. if ($total_users > 0) {
  124. foreach ($pre_result2 as $key => $res_array) {
  125. $key_list = [];
  126. foreach ($res_array as $user_result) {
  127. $userResult = isset($user_result[1]) ? $user_result[1] : null;
  128. if (!isset($resource_list[$key][$userResult])) {
  129. $resource_list[$key][$userResult] = 0;
  130. }
  131. $resource_list[$key][$userResult]++;
  132. $key_list[] = $userResult;
  133. }
  134. foreach ($customdisplays as $display) {
  135. if (!in_array($display['display'], $key_list)) {
  136. $resource_list[$key][$display['display']] = 0;
  137. }
  138. }
  139. $i++;
  140. }
  141. }
  142. //fixing $resource_list
  143. $max = 0;
  144. $new_list = [];
  145. foreach ($resource_list as $key => $value) {
  146. $new_value = [];
  147. foreach ($customdisplays as $item) {
  148. if ($value[$item['display']] > $max) {
  149. $max = $value[$item['display']];
  150. }
  151. $new_value[$item['display']] = strip_tags($value[$item['display']]);
  152. }
  153. $new_list[] = $new_value;
  154. }
  155. $resource_list = $new_list;
  156. $i = 1;
  157. foreach ($resource_list as $key => $resource) {
  158. // Reverse array, otherwise we get highest values first
  159. $resource = array_reverse($resource, true);
  160. $dataSet = new pData();
  161. $dataSet->addPoints($resource, 'Serie');
  162. $dataSet->addPoints(array_keys($resource), 'Labels');
  163. $header = $headerName[$i - 1];
  164. if (is_array($header) && isset($header['header'])) {
  165. $header = $header['header'];
  166. }
  167. $dataSet->setSerieDescription('Labels', strip_tags($header));
  168. $dataSet->setAbscissa('Labels');
  169. $dataSet->setAbscissaName(get_lang('GradebookSkillsRanking'));
  170. $dataSet->setAxisName(0, get_lang('Students'));
  171. $palette = [
  172. '0' => ['R' => 186, 'G' => 206, 'B' => 151, 'Alpha' => 100],
  173. '1' => ['R' => 210, 'G' => 148, 'B' => 147, 'Alpha' => 100],
  174. '2' => ['R' => 148, 'G' => 170, 'B' => 208, 'Alpha' => 100],
  175. '3' => ['R' => 221, 'G' => 133, 'B' => 61, 'Alpha' => 100],
  176. '4' => ['R' => 65, 'G' => 153, 'B' => 176, 'Alpha' => 100],
  177. '5' => ['R' => 114, 'G' => 88, 'B' => 144, 'Alpha' => 100],
  178. '6' => ['R' => 138, 'G' => 166, 'B' => 78, 'Alpha' => 100],
  179. '7' => ['R' => 171, 'G' => 70, 'B' => 67, 'Alpha' => 100],
  180. '8' => ['R' => 69, 'G' => 115, 'B' => 168, 'Alpha' => 100],
  181. ];
  182. // Cache definition
  183. $cachePath = api_get_path(SYS_ARCHIVE_PATH);
  184. $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
  185. $chartHash = $myCache->getHash($dataSet);
  186. if ($myCache->isInCache($chartHash)) {
  187. $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
  188. $myCache->saveFromCache($chartHash, $imgPath);
  189. $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
  190. } else {
  191. /* Create the pChart object */
  192. $widthSize = 480;
  193. $heightSize = 250;
  194. $myPicture = new pImage($widthSize, $heightSize, $dataSet);
  195. /* Turn of Antialiasing */
  196. $myPicture->Antialias = false;
  197. /* Add a border to the picture */
  198. $myPicture->drawRectangle(
  199. 0,
  200. 0,
  201. $widthSize - 1,
  202. $heightSize - 1,
  203. [
  204. 'R' => 0,
  205. 'G' => 0,
  206. 'B' => 0,
  207. ]
  208. );
  209. /* Set the default font */
  210. $myPicture->setFontProperties(
  211. [
  212. 'FontName' => api_get_path(SYS_FONTS_PATH).'opensans/OpenSans-Regular.ttf',
  213. 'FontSize' => 10,
  214. ]
  215. );
  216. /* Write the chart title */
  217. $myPicture->drawText(
  218. 250,
  219. 30,
  220. strip_tags($header),
  221. [
  222. 'FontSize' => 12,
  223. 'Align' => TEXT_ALIGN_BOTTOMMIDDLE,
  224. ]
  225. );
  226. /* Define the chart area */
  227. $myPicture->setGraphArea(50, 40, $widthSize - 20, $heightSize - 50);
  228. /* Draw the scale */
  229. $scaleSettings = [
  230. 'GridR' => 200,
  231. 'GridG' => 200,
  232. 'GridB' => 200,
  233. 'DrawSubTicks' => true,
  234. 'CycleBackground' => true,
  235. 'Mode' => SCALE_MODE_START0,
  236. ];
  237. $myPicture->drawScale($scaleSettings);
  238. /* Turn on shadow computing */
  239. $myPicture->setShadow(
  240. true,
  241. [
  242. 'X' => 1,
  243. 'Y' => 1,
  244. 'R' => 0,
  245. 'G' => 0,
  246. 'B' => 0,
  247. 'Alpha' => 10,
  248. ]
  249. );
  250. /* Draw the chart */
  251. $myPicture->setShadow(
  252. true,
  253. [
  254. 'X' => 1,
  255. 'Y' => 1,
  256. 'R' => 0,
  257. 'G' => 0,
  258. 'B' => 0,
  259. 'Alpha' => 10,
  260. ]
  261. );
  262. $settings = [
  263. 'OverrideColors' => $palette,
  264. 'Gradient' => false,
  265. 'GradientMode' => GRADIENT_SIMPLE,
  266. 'DisplayPos' => LABEL_POS_TOP,
  267. 'DisplayValues' => true,
  268. 'DisplayR' => 0,
  269. 'DisplayG' => 0,
  270. 'DisplayB' => 0,
  271. 'DisplayShadow' => true,
  272. 'Surrounding' => 10,
  273. ];
  274. $myPicture->drawBarChart($settings);
  275. /* Render the picture (choose the best way) */
  276. $myCache->writeToCache($chartHash, $myPicture);
  277. $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
  278. $myCache->saveFromCache($chartHash, $imgPath);
  279. $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
  280. }
  281. echo '<img src="'.$imgPath.'" >';
  282. if ($i % 2 == 0 && $i != 0) {
  283. echo '<br /><br />';
  284. } else {
  285. echo '&nbsp;&nbsp;&nbsp;';
  286. }
  287. $i++;
  288. }
  289. }
  290. /**
  291. * Function used by SortableTable to get total number of items in the table.
  292. */
  293. public function get_total_number_of_items()
  294. {
  295. return $this->datagen->get_total_users_count();
  296. }
  297. /**
  298. * Function used by SortableTable to generate the data to display.
  299. */
  300. public function get_table_data(
  301. $from = 1,
  302. $per_page = null,
  303. $column = null,
  304. $direction = null,
  305. $sort = null
  306. ) {
  307. $is_western_name_order = api_is_western_name_order();
  308. // create page navigation if needed
  309. $totalitems = $this->datagen->get_total_items_count();
  310. if ($this->limit_enabled && $totalitems > GRADEBOOK_ITEM_LIMIT) {
  311. $selectlimit = GRADEBOOK_ITEM_LIMIT;
  312. } else {
  313. $selectlimit = $totalitems;
  314. }
  315. $header = null;
  316. if ($this->limit_enabled && $totalitems > GRADEBOOK_ITEM_LIMIT) {
  317. $header .= '<table style="width: 100%; text-align: right; margin-left: auto; margin-right: auto;" border="0" cellpadding="2">'
  318. .'<tbody>'
  319. .'<tr>';
  320. // previous X
  321. $header .= '<td style="width:100%;">';
  322. if ($this->offset >= GRADEBOOK_ITEM_LIMIT) {
  323. $header .= '<a href="'.api_get_self()
  324. .'?selectcat='.Security::remove_XSS($_GET['selectcat'])
  325. .'&offset='.(($this->offset) - GRADEBOOK_ITEM_LIMIT)
  326. .(isset($_GET['search']) ? '&search='.Security::remove_XSS($_GET['search']) : '').'">'
  327. .Display::return_icon(
  328. 'action_prev.png',
  329. get_lang('PreviousPage'),
  330. [],
  331. ICON_SIZE_MEDIUM
  332. )
  333. .'</a>';
  334. } else {
  335. $header .= Display::return_icon(
  336. 'action_prev_na.png',
  337. get_lang('PreviousPage'),
  338. [],
  339. ICON_SIZE_MEDIUM
  340. );
  341. }
  342. $header .= ' ';
  343. // next X
  344. $calcnext = (($this->offset + (2 * GRADEBOOK_ITEM_LIMIT)) > $totalitems) ?
  345. ($totalitems - (GRADEBOOK_ITEM_LIMIT + $this->offset)) : GRADEBOOK_ITEM_LIMIT;
  346. if ($calcnext > 0) {
  347. $header .= '<a href="'.api_get_self()
  348. .'?selectcat='.Security::remove_XSS($_GET['selectcat'])
  349. .'&offset='.($this->offset + GRADEBOOK_ITEM_LIMIT)
  350. .(isset($_GET['search']) ? '&search='.Security::remove_XSS($_GET['search']) : '').'">'
  351. .Display::return_icon('action_next.png', get_lang('NextPage'), [], ICON_SIZE_MEDIUM)
  352. .'</a>';
  353. } else {
  354. $header .= Display::return_icon(
  355. 'action_next_na.png',
  356. get_lang('NextPage'),
  357. [],
  358. ICON_SIZE_MEDIUM
  359. );
  360. }
  361. $header .= '</td>';
  362. $header .= '</tbody></table>';
  363. echo $header;
  364. }
  365. // retrieve sorting type
  366. if ($is_western_name_order) {
  367. $users_sorting = ($this->column == 0 ? FlatViewDataGenerator::FVDG_SORT_FIRSTNAME : FlatViewDataGenerator::FVDG_SORT_LASTNAME);
  368. } else {
  369. $users_sorting = ($this->column == 0 ? FlatViewDataGenerator::FVDG_SORT_LASTNAME : FlatViewDataGenerator::FVDG_SORT_FIRSTNAME);
  370. }
  371. if ($this->direction == 'DESC') {
  372. $users_sorting |= FlatViewDataGenerator::FVDG_SORT_DESC;
  373. } else {
  374. $users_sorting |= FlatViewDataGenerator::FVDG_SORT_ASC;
  375. }
  376. // step 1: generate columns: evaluations and links
  377. $header_names = $this->datagen->get_header_names($this->offset, $selectlimit);
  378. $userRowSpan = false;
  379. foreach ($header_names as $item) {
  380. if (is_array($item)) {
  381. $userRowSpan = true;
  382. break;
  383. }
  384. }
  385. $thAttributes = '';
  386. if ($userRowSpan) {
  387. $thAttributes = 'rowspan=2';
  388. }
  389. $this->set_header(0, $header_names[0], true, $thAttributes);
  390. $this->set_header(1, $header_names[1], true, $thAttributes);
  391. $column = 2;
  392. $firstHeader = [];
  393. while ($column < count($header_names)) {
  394. $headerData = $header_names[$column];
  395. if (is_array($headerData)) {
  396. $countItems = count($headerData['items']);
  397. $this->set_header(
  398. $column,
  399. $headerData['header'],
  400. false,
  401. 'colspan="'.$countItems.'"'
  402. );
  403. foreach ($headerData['items'] as $item) {
  404. $firstHeader[] = '<span class="text-center">'.$item.'</span>';
  405. }
  406. } else {
  407. $this->set_header($column, $headerData, false, $thAttributes);
  408. }
  409. $column++;
  410. }
  411. $data_array = $this->datagen->get_data(
  412. $users_sorting,
  413. $from,
  414. $this->per_page,
  415. $this->offset,
  416. $selectlimit
  417. );
  418. $table_data = [];
  419. if (!empty($firstHeader)) {
  420. $table_data[] = $firstHeader;
  421. }
  422. $columnOffset = empty($this->datagen->params['show_official_code']) ? 0 : 1;
  423. foreach ($data_array as $user_row) {
  424. $user_id = $user_row[0];
  425. unset($user_row[0]);
  426. $userInfo = api_get_user_info($user_id);
  427. if ($is_western_name_order) {
  428. $user_row[1 + $columnOffset] = $this->build_name_link(
  429. $user_id,
  430. $userInfo['firstname']
  431. );
  432. $user_row[2 + $columnOffset] = $this->build_name_link(
  433. $user_id,
  434. $userInfo['lastname']
  435. );
  436. } else {
  437. $user_row[1 + $columnOffset] = $this->build_name_link(
  438. $user_id,
  439. $userInfo['lastname']
  440. );
  441. $user_row[2 + $columnOffset] = $this->build_name_link(
  442. $user_id,
  443. $userInfo['firstname']
  444. );
  445. }
  446. $user_row = array_values($user_row);
  447. $table_data[] = $user_row;
  448. }
  449. return $table_data;
  450. }
  451. /**
  452. * @param $userId
  453. * @param $name
  454. *
  455. * @return string
  456. */
  457. private function build_name_link($userId, $name)
  458. {
  459. return '<a href="user_stats.php?userid='.$userId.'&selectcat='.$this->selectcat->get_id().'&'.api_get_cidreq().'">'.$name.'</a>';
  460. }
  461. }