scoredisplay.class.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. <?php
  2. /*
  3. ==============================================================================
  4. Dokeos - elearning and course management software
  5. Copyright (c) 2008 Dokeos Latinoamerica SAC
  6. Copyright (c) 2006 Dokeos SPRL
  7. Copyright (c) 2006 Ghent University (UGent)
  8. Copyright (c) various contributors
  9. For a full list of contributors, see "credits.txt".
  10. The full license can be read in "license.txt".
  11. This program is free software; you can redistribute it and/or
  12. modify it under the terms of the GNU General Public License
  13. as published by the Free Software Foundation; either version 2
  14. of the License, or (at your option) any later version.
  15. See the GNU General Public License for more details.
  16. Contact address: Dokeos, rue du Corbeau, 108, B-1030 Brussels, Belgium
  17. Mail: info@dokeos.com
  18. ==============================================================================
  19. */
  20. // Score display types constants
  21. define('SCORE_DIV',1);
  22. define('SCORE_PERCENT',2);
  23. define('SCORE_DIV_PERCENT',3);
  24. define('SCORE_AVERAGE',4);
  25. define('SCORE_IGNORE_SPLIT', 8);
  26. define('SCORE_BOTH',1);
  27. define('SCORE_ONLY_DEFAULT',2);
  28. define('SCORE_ONLY_CUSTOM',3);
  29. /**
  30. * Class to display scores according to the settings made by the platform admin.
  31. * This class works as a singleton: call instance() to retrieve an object.
  32. * @author Bert Stepp�
  33. * @package dokeos.gradebook
  34. */
  35. class ScoreDisplay
  36. {
  37. // Singleton stuff
  38. /**
  39. * Get the instance of this class
  40. */
  41. public static function instance() {
  42. static $instance;
  43. if (!isset ($instance)) {
  44. $instance = new ScoreDisplay();
  45. }
  46. return $instance;
  47. }
  48. // Static methods
  49. /**
  50. * Compare the custom display of 2 scores, can be useful in sorting
  51. */
  52. public static function compare_scores_by_custom_display ($score1, $score2)
  53. {
  54. if (!isset($score1)) {
  55. return (isset($score2) ? 1 : 0);
  56. } elseif (!isset($score2)) {
  57. return -1;
  58. } else {
  59. $scoredisplay = ScoreDisplay :: instance();
  60. $custom1 = $scoredisplay->display_custom($score1);
  61. $custom2 = $scoredisplay->display_custom($score2);
  62. if ($custom1 == $custom2) {
  63. return 0;
  64. } else {
  65. return (($score1[0]/$score1[1]) < ($score2[0]/$score2[1]) ? -1 : 1);
  66. }
  67. }
  68. }
  69. // As object
  70. private $coloring_enabled;
  71. private $color_split_value;
  72. private $custom_enabled;
  73. private $upperlimit_included;
  74. private $custom_display;
  75. private $custom_display_conv;
  76. /**
  77. * Protected constructor - call instance() to instantiate
  78. */
  79. protected function ScoreDisplay() {
  80. $this->coloring_enabled = $this->load_bool_setting('gradebook_score_display_coloring',0);
  81. if ($this->coloring_enabled) {
  82. $this->color_split_value = $this->load_int_setting('gradebook_score_display_colorsplit',50);
  83. }
  84. $this->custom_enabled = $this->load_bool_setting('gradebook_score_display_custom', 0);
  85. if ($this->custom_enabled) {
  86. $this->upperlimit_included = $this->load_bool_setting('gradebook_score_display_upperlimit', 0);
  87. $this->custom_display = $this->get_custom_displays();
  88. $this->custom_display_conv = $this->convert_displays($this->custom_display);
  89. }
  90. }
  91. /**
  92. * Is coloring enabled ?
  93. */
  94. public function is_coloring_enabled ()
  95. {
  96. return $this->coloring_enabled;
  97. }
  98. /**
  99. * Is custom score display enabled ?
  100. */
  101. public function is_custom ()
  102. {
  103. return $this->custom_enabled;
  104. }
  105. /**
  106. * Is upperlimit included ?
  107. */
  108. public function is_upperlimit_included ()
  109. {
  110. return $this->upperlimit_included;
  111. }
  112. /**
  113. * Update the 'coloring' setting
  114. * @param boolean $coloring coloring enabled or disabled
  115. */
  116. public function set_coloring_enabled ($coloring) {
  117. $this->coloring_enabled = $coloring;
  118. $this->save_bool_setting ('gradebook_score_display_coloring', $coloring);
  119. }
  120. /**
  121. * Update the 'colorsplit' setting
  122. * @param int $colorsplit color split value, in percent
  123. */
  124. public function set_color_split_value ($colorsplit) {
  125. $this->color_split_value = $colorsplit;
  126. $this->save_int_setting ('gradebook_score_display_colorsplit', $colorsplit);
  127. }
  128. /**
  129. * Update the 'custom' setting
  130. * @param boolean $custom custom enabled or disabled
  131. */
  132. public function set_custom ($custom) {
  133. $this->custom_enabled = $custom;
  134. $this->save_bool_setting ('gradebook_score_display_custom', $custom);
  135. }
  136. /**
  137. * Update the 'upperlimit' setting
  138. * @param boolean $upperlimit_included true if upper limit must be included, false otherwise
  139. */
  140. public function set_upperlimit_included ($upperlimit_included) {
  141. $this->upperlimit_incl = $upperlimit_included;
  142. $this->save_bool_setting ('gradebook_score_display_upperlimit', $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. * Update custom score display settings
  161. * @param array $displays 2-dimensional array - every subarray must have keys (score, display)
  162. */
  163. public function update_custom_score_display_settings ($displays) {
  164. $this->custom_display = $displays;
  165. $this->custom_display_conv = $this->convert_displays($this->custom_display);
  166. // remove previous settings
  167. $tbl_display = Database :: get_main_table(TABLE_MAIN_GRADEBOOK_SCORE_DISPLAY);
  168. $sql = 'TRUNCATE TABLE '.$tbl_display;
  169. api_sql_query($sql, __FILE__, __LINE__);
  170. // add new settings
  171. $sql = 'INSERT INTO '.$tbl_display.' (id, score, display) VALUES ';
  172. $count = 0;
  173. foreach ($displays as $display) {
  174. if ($count > 0) {
  175. $sql .= ',';
  176. }
  177. $sql .= "(NULL, '".$display['score']."', '".$display['display']."')";
  178. $count++;
  179. }
  180. api_sql_query($sql, __FILE__, __LINE__);
  181. }
  182. /**
  183. * Display a score according to the current settings
  184. * @param array $score score data structure, as returned by the calc_score functions
  185. * @param int $type one of the following constants: SCORE_DIV, SCORE_PERCENT, SCORE_DIV_PERCENT, SCORE_AVERAGE
  186. * (ignored for student's view if custom score display is enabled)
  187. * @param int $what one of the following constants: SCORE_BOTH, SCORE_ONLY_DEFAULT, SCORE_ONLY_CUSTOM (default: SCORE_BOTH)
  188. * (only taken into account if custom score display is enabled and for course/platform admin)
  189. */
  190. public function display_score($score,$type,$what = SCORE_BOTH) {
  191. $type2 = $type & 7; // removes the 'SCORE_IGNORE_SPLIT' bit
  192. $split_enabled = ($type2 == $type);
  193. if (!isset($score)) {
  194. return '';
  195. } elseif ($this->custom_enabled && isset($this->custom_display_conv)) {
  196. // students only see the custom display
  197. if (!api_is_allowed_to_create_course()) {
  198. $display = $this->display_custom($score);
  199. }
  200. // course/platform admins
  201. elseif ($what == SCORE_ONLY_DEFAULT) {
  202. $display = $this->display_default ($score, $type2);
  203. }
  204. elseif ($what == SCORE_ONLY_CUSTOM) {
  205. $display = $this->display_custom ($score);
  206. } else {
  207. $display = $this->display_default ($score, $type2)
  208. .' ('.$this->display_custom ($score).')';
  209. }
  210. } else {
  211. // if no custom display set, use default display
  212. $display = $this->display_default ($score, $type2);
  213. }
  214. return (($split_enabled ? $this->get_color_display_start_tag($score) : '')
  215. . $display
  216. . ($split_enabled ? $this->get_color_display_end_tag($score) : ''));
  217. }
  218. // Internal functions
  219. private function display_default ($score, $type) {
  220. switch ($type) {
  221. case SCORE_DIV : // X / Y
  222. return $this->display_as_div($score);
  223. case SCORE_PERCENT : // XX %
  224. return $this->display_as_percent($score);
  225. case SCORE_DIV_PERCENT : // X / Y (XX %)
  226. return $this->display_as_div($score)
  227. . ' (' . $this->display_as_percent($score) . ')';
  228. case SCORE_AVERAGE : // XX %
  229. return $this->display_as_percent($score);
  230. }
  231. }
  232. private function display_as_percent ($score) {
  233. return round(($score[0] / $score[1]) * 100) . ' %';
  234. }
  235. private function display_as_div ($score) {
  236. return $score[0] . ' / ' . $score[1];
  237. }
  238. private function display_custom ($score) {
  239. $scaledscore = $score[0] / $score[1];
  240. if ($this->upperlimit_included) {
  241. foreach ($this->custom_display_conv as $displayitem) {
  242. if ($scaledscore <= $displayitem['score']) {
  243. return $displayitem['display'];
  244. }
  245. }
  246. } else {
  247. foreach ($this->custom_display_conv as $displayitem) {
  248. if ($scaledscore < $displayitem['score'] || $displayitem['score'] == 1) {
  249. return $displayitem['display'];
  250. }
  251. }
  252. }
  253. }
  254. private function load_bool_setting ($property, $default = 0) {
  255. $value = $this->load_int_setting($property, $default);
  256. return ($value == 'true' ? true : false);
  257. }
  258. private function load_int_setting ($property, $default = 0) {
  259. $tbl_setting = Database :: get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
  260. $sql = "SELECT selected_value FROM ".$tbl_setting
  261. ." WHERE category = 'Gradebook' AND variable = '".$property."'";
  262. $result = api_sql_query($sql, __FILE__, __LINE__);
  263. if ($data = Database::fetch_row($result)) {
  264. return $data[0];
  265. }
  266. else {
  267. // if not present, add default setting into table...
  268. $sql = "INSERT INTO ".$tbl_setting
  269. ." (variable, selected_value, category)"
  270. ." VALUES ('".$property."', '".$default."','Gradebook')";
  271. api_sql_query($sql, __FILE__, __LINE__);
  272. // ...and return default value
  273. return $default;
  274. }
  275. }
  276. private function save_bool_setting ($property, $value) {
  277. $this->save_int_setting ($property, ($value ? 'true' : 'false') );
  278. }
  279. private function save_int_setting ($property, $value) {
  280. $tbl_setting = Database :: get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
  281. $sql = 'UPDATE '.$tbl_setting
  282. ." SET selected_value = '".$value."' "
  283. ." WHERE variable = '".$property."' AND category='Gradebook'";
  284. api_sql_query($sql, __FILE__, __LINE__);
  285. }
  286. /**
  287. * Get current custom score display settings
  288. * @return array 2-dimensional array - every element contains 3 subelements (id, score, display)
  289. */
  290. private function get_custom_displays() {
  291. $tbl_display = Database :: get_main_table(TABLE_MAIN_GRADEBOOK_SCORE_DISPLAY);
  292. $sql = 'SELECT * FROM '.$tbl_display.' ORDER BY score';
  293. //echo $sql;
  294. $result = api_sql_query($sql, __FILE__, __LINE__);
  295. return api_store_result($result);
  296. }
  297. /**
  298. * Convert display settings to internally used values
  299. */
  300. private function convert_displays($custom_display) {
  301. if (isset($custom_display)) {
  302. // get highest score entry, and copy each element to a new array
  303. $converted = array();
  304. $highest = 0;
  305. foreach ($custom_display as $element) {
  306. if ($element['score'] > $highest) {
  307. $highest = $element['score'];
  308. }
  309. $converted[] = $element;
  310. }
  311. // sort the new array (ascending)
  312. usort($converted, array('ScoreDisplay', 'sort_display'));
  313. // adjust each score in such a way that
  314. // each score is scaled between 0 and 1
  315. // the highest score in this array will be equal to 1
  316. $converted2 = array();
  317. foreach ($converted as $element) {
  318. $newelement = array();
  319. $newelement['score'] = $element['score'] / $highest;
  320. $newelement['display'] = $element['display'];
  321. $converted2[] = $newelement;
  322. }
  323. return $converted2;
  324. } else {
  325. return null;
  326. }
  327. }
  328. private function sort_display ($item1, $item2) {
  329. if ($item1['score'] == $item2['score']) {
  330. return 0;
  331. } else {
  332. return ($item1['score'] < $item2['score'] ? -1 : 1);
  333. }
  334. }
  335. private function get_color_display_start_tag($score) {
  336. if ($this->coloring_enabled && ($score[0]/$score[1]) < ($this->color_split_value / 100)) {
  337. return '<font color="red">';
  338. } else {
  339. return '';
  340. }
  341. }
  342. private function get_color_display_end_tag($score) {
  343. if ($this->coloring_enabled && ($score[0]/$score[1]) < ($this->color_split_value / 100)) {
  344. return '</font>';
  345. } else {
  346. return '';
  347. }
  348. }
  349. }