scoredisplay.class.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * Script
  5. * @package chamilo.gradebook
  6. */
  7. /**
  8. * Init
  9. */
  10. // Score display types constants
  11. define('SCORE_DIV', 1); // X / Y
  12. define('SCORE_PERCENT', 2); // XX %
  13. define('SCORE_DIV_PERCENT', 3); // X / Y (XX %)
  14. define('SCORE_AVERAGE', 4); // XX %
  15. define('SCORE_DECIMAL', 5); // 0.50 (X/Y)
  16. //@todo where is number 6?
  17. define('SCORE_IGNORE_SPLIT', 8); // ??
  18. define('SCORE_DIV_PERCENT_WITH_CUSTOM', 9); // X / Y (XX %) - Good!
  19. define('SCORE_CUSTOM', 10); // Good!
  20. define('SCORE_DIV_SIMPLE_WITH_CUSTOM', 11); // X - Good!
  21. define('SCORE_BOTH',1);
  22. define('SCORE_ONLY_DEFAULT',2);
  23. define('SCORE_ONLY_CUSTOM',3);
  24. /**
  25. * Class to display scores according to the settings made by the platform admin.
  26. * This class works as a singleton: call instance() to retrieve an object.
  27. * @author Bert Steppé
  28. * @package chamilo.gradebook
  29. */
  30. class ScoreDisplay
  31. {
  32. // Singleton stuff
  33. /**
  34. * Get the instance of this class
  35. */
  36. public static function instance($category_id = 0) {
  37. static $instance;
  38. if (!isset ($instance)) {
  39. $instance = new ScoreDisplay($category_id);
  40. }
  41. return $instance;
  42. }
  43. // Static methods
  44. /**
  45. * Compare the custom display of 2 scores, can be useful in sorting
  46. */
  47. public static function compare_scores_by_custom_display ($score1, $score2)
  48. {
  49. if (!isset($score1)) {
  50. return (isset($score2) ? 1 : 0);
  51. } elseif (!isset($score2)) {
  52. return -1;
  53. } else {
  54. $scoredisplay = ScoreDisplay :: instance();
  55. $custom1 = $scoredisplay->display_custom($score1);
  56. $custom2 = $scoredisplay->display_custom($score2);
  57. if ($custom1 == $custom2) {
  58. return 0;
  59. } else {
  60. return (($score1[0]/$score1[1]) < ($score2[0]/$score2[1]) ? -1 : 1);
  61. }
  62. }
  63. }
  64. private $coloring_enabled;
  65. private $color_split_value;
  66. private $custom_enabled;
  67. private $upperlimit_included;
  68. private $custom_display;
  69. private $custom_display_conv;
  70. /**
  71. * Protected constructor - call instance() to instantiate
  72. */
  73. protected function ScoreDisplay($category_id = 0) {
  74. if (!empty($category_id)) {
  75. $this->category_id = $category_id;
  76. }
  77. //Loading portal settings + using standard functions
  78. $value = api_get_setting('gradebook_score_display_coloring');
  79. $value = $value['my_display_coloring'];
  80. //Settting coloring
  81. $this->coloring_enabled = $value == 'true' ? true : false;
  82. if ($this->coloring_enabled) {
  83. $value = api_get_setting('gradebook_score_display_colorsplit');
  84. if (isset($value)) {
  85. $this->color_split_value = $value;
  86. }
  87. }
  88. //Setting custom enabled
  89. $value = api_get_setting('gradebook_score_display_custom');
  90. $value = $value['my_display_custom'];
  91. $this->custom_enabled = $value;
  92. if ($this->custom_enabled) {
  93. $params = array('category = ? AND subkey = ?' => array('Gradebook', 'ranking'));
  94. $displays = api_get_settings_params($params);
  95. $portal_displays = array();
  96. if (!empty($displays)) {
  97. foreach ($displays as $display) {
  98. $data = explode('::', $display['selected_value']);
  99. $portal_displays[$data[0]] = array('score' => $data[0], 'display' =>$data[1]);
  100. }
  101. sort($portal_displays);
  102. }
  103. $this->custom_display = $portal_displays;
  104. if (count($this->custom_display)>0) {
  105. $value = api_get_setting('gradebook_score_display_upperlimit');
  106. $value = $value['my_display_upperlimit'];
  107. $this->upperlimit_included = $value == 'true' ? true : false;
  108. $this->custom_display_conv = $this->convert_displays($this->custom_display);
  109. }
  110. }
  111. //If teachers can override the portal parameters
  112. if (api_get_setting('teachers_can_change_score_settings') == 'true') {
  113. //Load course settings
  114. if ($this->custom_enabled) {
  115. $this->custom_display = $this->get_custom_displays();
  116. if (count($this->custom_display)> 0) {
  117. $this->custom_display_conv = $this->convert_displays($this->custom_display);
  118. }
  119. }
  120. if ($this->coloring_enabled) {
  121. $this->color_split_value = $this->get_score_color_percent();
  122. }
  123. }
  124. }
  125. /**
  126. * Is coloring enabled ?
  127. */
  128. public function is_coloring_enabled () {
  129. return $this->coloring_enabled;
  130. }
  131. /**
  132. * Is custom score display enabled ?
  133. */
  134. public function is_custom() {
  135. return $this->custom_enabled;
  136. }
  137. /**
  138. * Is upperlimit included ?
  139. */
  140. public function is_upperlimit_included ()
  141. {
  142. return $this->upperlimit_included;
  143. }
  144. /**
  145. * If custom score display is enabled, this will return the current settings.
  146. * See also update_custom_score_display_settings
  147. * @return array current settings (or null if feature not enabled)
  148. */
  149. public function get_custom_score_display_settings() {
  150. return $this->custom_display;
  151. }
  152. /**
  153. * If coloring is enabled, scores below this value will be displayed in red.
  154. * @return int color split value, in percent (or null if feature not enabled)
  155. */
  156. public function get_color_split_value() {
  157. return $this->color_split_value;
  158. }
  159. /**
  160. * Get current gradebook category id
  161. * @return int Category id
  162. */
  163. private function get_current_gradebook_category_id() {
  164. $tbl_gradebook_category = Database :: get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
  165. $curr_course_code = api_get_course_id();
  166. $curr_session_id = api_get_session_id();
  167. $session_condition = '';
  168. if (empty($curr_session_id)) {
  169. $session_condition = ' AND session_id is null ';
  170. } else {
  171. $session_condition = ' AND session_id = '.$curr_session_id;
  172. }
  173. $sql = 'SELECT id FROM '.$tbl_gradebook_category.' WHERE course_code = "'.$curr_course_code.'" '. $session_condition;
  174. $rs = Database::query($sql);
  175. $category_id = 0;
  176. if (Database::num_rows($rs) > 0) {
  177. $row = Database::fetch_row($rs);
  178. $category_id = $row[0];
  179. }
  180. return $category_id;
  181. }
  182. /**
  183. * Update custom score display settings
  184. * @param array $displays 2-dimensional array - every subarray must have keys (score, display)
  185. * @param int score color percent (optional)
  186. * @param int gradebook category id (optional)
  187. */
  188. public function update_custom_score_display_settings ($displays, $scorecolpercent = 0, $category_id = null) {
  189. $this->custom_display = $displays;
  190. $this->custom_display_conv = $this->convert_displays($this->custom_display);
  191. if (isset($category_id)) {
  192. $category_id = intval($category_id);
  193. } else {
  194. $category_id = $this->get_current_gradebook_category_id();
  195. }
  196. // remove previous settings
  197. $tbl_display = Database :: get_main_table(TABLE_MAIN_GRADEBOOK_SCORE_DISPLAY);
  198. $sql = 'DELETE FROM '.$tbl_display.' WHERE category_id = '.$category_id;
  199. Database::query($sql);
  200. // add new settings
  201. $sql = 'INSERT INTO '.$tbl_display.' (id, score, display, category_id, score_color_percent) VALUES ';
  202. $count = 0;
  203. foreach ($displays as $display) {
  204. if ($count > 0) {
  205. $sql .= ',';
  206. }
  207. $sql .= "(NULL, '".$display['score']."', '".Database::escape_string($display['display'])."', ".$category_id.", ".intval($scorecolpercent).")";
  208. $count++;
  209. }
  210. Database::query($sql);
  211. }
  212. /**
  213. * Display a score according to the current settings
  214. * @param array $score score data structure, as returned by the calc_score functions
  215. * @param int $type one of the following constants: SCORE_DIV, SCORE_PERCENT, SCORE_DIV_PERCENT, SCORE_AVERAGE
  216. * (ignored for student's view if custom score display is enabled)
  217. * @param int $what one of the following constants: SCORE_BOTH, SCORE_ONLY_DEFAULT, SCORE_ONLY_CUSTOM (default: SCORE_BOTH)
  218. * (only taken into account if custom score display is enabled and for course/platform admin)
  219. */
  220. public function display_score($score, $type = SCORE_DIV_PERCENT, $what = SCORE_BOTH, $no_color = false) {
  221. $my_score = ($score==0) ? 1 : $score;
  222. if ($this->custom_enabled && isset($this->custom_display_conv)) {
  223. $display = $this->display_default($my_score, $type);
  224. } else {
  225. // if no custom display set, use default display
  226. $display = $this->display_default($my_score, $type);
  227. }
  228. if ($this->coloring_enabled && $no_color == false) {
  229. $my_score_denom = ($score[1]==0)?1:$score[1];
  230. if (($score[0] / $my_score_denom) < ($this->color_split_value / 100)) {
  231. $display = Display::tag('font', $display, array('color'=>'red'));
  232. }
  233. }
  234. return $display;
  235. }
  236. // Internal functions
  237. private function display_default ($score, $type) {
  238. switch ($type) {
  239. case SCORE_DIV : // X / Y
  240. return $this->display_as_div($score);
  241. case SCORE_PERCENT : // XX %
  242. return $this->display_as_percent($score);
  243. case SCORE_DIV_PERCENT : // X / Y (XX %)
  244. return $this->display_as_div($score).' (' . $this->display_as_percent($score) . ')';
  245. case SCORE_AVERAGE : // XX %
  246. return $this->display_as_percent($score);
  247. case SCORE_DECIMAL : // 0.50 (X/Y)
  248. return $this->display_as_decimal($score);
  249. case SCORE_DIV_PERCENT_WITH_CUSTOM : // X / Y (XX %) - Good!
  250. $custom = $this->display_custom($score);
  251. if (!empty($custom)) {
  252. $custom = ' - '.$custom;
  253. }
  254. return $this->display_as_div($score).' (' . $this->display_as_percent($score) . ')'.$custom;
  255. case SCORE_DIV_SIMPLE_WITH_CUSTOM : // X - Good!
  256. $custom = $this->display_custom($score);
  257. if (!empty($custom)) {
  258. $custom = ' - '.$custom;
  259. }
  260. return $this->display_simple_score($score).$custom;
  261. case SCORE_CUSTOM: // Good!
  262. return $this->display_custom($score);
  263. }
  264. }
  265. private function display_simple_score($score) {
  266. if (isset($score[0])) {
  267. return $score[0];
  268. }
  269. return '';
  270. }
  271. /**
  272. * Returns "1" for array("100", "100");
  273. */
  274. private function display_as_decimal($score) {
  275. $score_denom = ($score[1]==0) ? 1 : $score[1];
  276. return round(($score[0]/ $score_denom),2);
  277. }
  278. /**
  279. * Returns "100 %" for array("100", "100");
  280. */
  281. private function display_as_percent($score) {
  282. $score_denom=($score[1]==0) ? 1 : $score[1];
  283. return round(($score[0] / $score_denom) * 100,2) . ' %';
  284. }
  285. /**
  286. *
  287. * Returns 10.00 / 10.00 for array("100", "100");
  288. * @param array $score
  289. */
  290. private function display_as_div($score) {
  291. if ($score == 1) {
  292. return '0/0';
  293. } else {
  294. return $score[0] . ' / ' . $score[1];
  295. }
  296. }
  297. /**
  298. *
  299. * Depends in the user selections [0 50] Bad [50:100] Good
  300. * @param array $score
  301. */
  302. private function display_custom ($score) {
  303. $my_score_denom= ($score[1]==0)?1:$score[1];
  304. $scaledscore = $score[0] / $my_score_denom;
  305. if ($this->upperlimit_included) {
  306. foreach ($this->custom_display_conv as $displayitem) {
  307. if ($scaledscore <= $displayitem['score']) {
  308. return $displayitem['display'];
  309. }
  310. }
  311. } else {
  312. if (!empty($this->custom_display_conv)) {
  313. foreach ($this->custom_display_conv as $displayitem) {
  314. if ($scaledscore < $displayitem['score'] || $displayitem['score'] == 1) {
  315. return $displayitem['display'];
  316. }
  317. }
  318. }
  319. }
  320. }
  321. /**
  322. * Get score color percent by category
  323. * @param int Gradebook category id
  324. * @return int Score
  325. */
  326. private function get_score_color_percent($category_id = null) {
  327. $tbl_display = Database :: get_main_table(TABLE_MAIN_GRADEBOOK_SCORE_DISPLAY);
  328. if (isset($category_id)) {
  329. $category_id = intval($category_id);
  330. } else {
  331. $category_id = $this->get_current_gradebook_category_id();
  332. }
  333. $sql = 'SELECT score_color_percent FROM '.$tbl_display.' WHERE category_id = '.$category_id.' LIMIT 1';
  334. $result = Database::query($sql);
  335. $score = 0;
  336. if (Database::num_rows($result) > 0) {
  337. $row = Database::fetch_row($result);
  338. $score = $row[0];
  339. }
  340. return $score;
  341. }
  342. /**
  343. * Get current custom score display settings
  344. * @param int Gradebook category id
  345. * @return array 2-dimensional array - every element contains 3 subelements (id, score, display)
  346. */
  347. private function get_custom_displays($category_id = null) {
  348. $tbl_display = Database :: get_main_table(TABLE_MAIN_GRADEBOOK_SCORE_DISPLAY);
  349. if (isset($category_id)) {
  350. $category_id = intval($category_id);
  351. } else {
  352. $category_id = $this->get_current_gradebook_category_id();
  353. }
  354. $sql = 'SELECT * FROM '.$tbl_display.' WHERE category_id = '.$category_id.' ORDER BY score';
  355. $result = Database::query($sql);
  356. return Database::store_result($result,'ASSOC');
  357. }
  358. /**
  359. * Convert display settings to internally used values
  360. */
  361. private function convert_displays($custom_display) {
  362. if (isset($custom_display)) {
  363. // get highest score entry, and copy each element to a new array
  364. $converted = array();
  365. $highest = 0;
  366. foreach ($custom_display as $element) {
  367. if ($element['score'] > $highest) {
  368. $highest = $element['score'];
  369. }
  370. $converted[] = $element;
  371. }
  372. // sort the new array (ascending)
  373. usort($converted, array('ScoreDisplay', 'sort_display'));
  374. // adjust each score in such a way that
  375. // each score is scaled between 0 and 1
  376. // the highest score in this array will be equal to 1
  377. $converted2 = array();
  378. foreach ($converted as $element) {
  379. $newelement = array();
  380. $newelement['score'] = $element['score'] / $highest;
  381. $newelement['display'] = $element['display'];
  382. $converted2[] = $newelement;
  383. }
  384. return $converted2;
  385. } else {
  386. return null;
  387. }
  388. }
  389. private function sort_display ($item1, $item2) {
  390. if ($item1['score'] == $item2['score']) {
  391. return 0;
  392. } else {
  393. return ($item1['score'] < $item2['score'] ? -1 : 1);
  394. }
  395. }
  396. }