PageRenderTime 26ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/exam/classes/examresult/csv.php

https://github.com/pratikdhaboo/kodelearn
PHP | 300 lines | 165 code | 26 blank | 109 comment | 14 complexity | 360d5a310dd8b385690e67333345f2b6 MD5 | raw file
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. class Examresult_Csv {
  3. /**
  4. * Array $_FILES['name']
  5. */
  6. private $_file;
  7. /**
  8. * Database_Mysql_Result
  9. */
  10. private $_exams;
  11. /**
  12. * Array array_keys = user_ids, array_values = Student Names
  13. */
  14. private $_students;
  15. /**
  16. * Array
  17. */
  18. private $_content;
  19. /**
  20. * Array of the form array('exam_id'=> int, 'user_id'=>int, 'marks' => float);
  21. */
  22. private $_datasets;
  23. /**
  24. * Array
  25. */
  26. private $_errors = array();
  27. /**
  28. * Array
  29. */
  30. private $_headings;
  31. /**
  32. * Array of students list in an examwise array
  33. * keys = exam_ids, values = array of user_ids applicable
  34. */
  35. private $_exam_wise_students = array();
  36. /**
  37. * Easy lookup array with exam_ids as the keys and total marks as the values
  38. */
  39. private $_exam_marks = array();
  40. /**
  41. * Easy look up array with exam_ids as the keys and the names as the values
  42. */
  43. private $_exam_names = array();
  44. /**
  45. * @param array $files_arr eg. $_FILES['csv']
  46. * @param Database_Mysql_Result $exams
  47. *
  48. */
  49. public function __construct($files_arr, $exams, $students) {
  50. $this->_file = $files_arr;
  51. $this->set_exams($exams);
  52. $this->_students = $students;
  53. $this->_exam_wise_students = $this->_get_exam_wise_students();
  54. try {
  55. $this->validate_filetype();
  56. $data = $this->csvcontent();
  57. $this->_headings = array_shift($data);
  58. $this->_content = $data;
  59. $this->_datasets = $this->csv_to_datasets();
  60. } catch (Examresult_Exception $e) {
  61. $this->_errors['warning'] = $e->getMessage();
  62. }
  63. }
  64. /**
  65. * Method to check whether the type of file uploaded and the data contained
  66. * by it are valid
  67. * @return boolean
  68. */
  69. public function validate() {
  70. if (!$this->_errors) {
  71. return true;
  72. }
  73. return false;
  74. }
  75. /**
  76. * Public getter for datasets
  77. * @return Array $_datasets
  78. */
  79. public function datasets() {
  80. return $this->_datasets;
  81. }
  82. /**
  83. * Method to set the exam instance variable and also for setting up
  84. * the easy lookup exam arrays
  85. * @param Database_Mysql_Result $exams
  86. */
  87. public function set_exams($exams) {
  88. $this->_exams = $exams;
  89. $this->_exam_marks = $this->_exams->as_array('id', 'total_marks');
  90. $this->_exam_names = $this->_exams->as_array('id', 'name');
  91. }
  92. /**
  93. * Public getter for errors
  94. * @param string $key optional default = null
  95. * @param mixed (Array|String)
  96. */
  97. public function errors($key=null) {
  98. if ($key !== null) {
  99. return Arr::get($this->_errors, $key, '');
  100. }
  101. return $this->_errors;
  102. }
  103. /**
  104. * Method to read the csv file uploaded and return the content
  105. * @return array $filedata
  106. */
  107. public function csvcontent() {
  108. $filename = $this->_file['tmp_name'];
  109. $handle = fopen($filename, "r");
  110. while (($data = fgetcsv($handle, 1000, ',')) !== FALSE){
  111. $filedata[] = $data;
  112. }
  113. return $filedata;
  114. }
  115. /**
  116. * Method to find the result sets for the exams from the csv data provided
  117. * @return array $datasets array_keys in order 'exam_id', 'user_id', 'marks'
  118. */
  119. private function csv_to_datasets() {
  120. $exams = $this->ordered_exams();
  121. $exam_marks = $this->_exams->as_array('id', 'total_marks');
  122. $datasets = array();
  123. foreach ($this->_content as $row) {
  124. $user_id = $row[0];
  125. $marks = array_slice($row, 2);
  126. foreach ($marks as $k=>$m) {
  127. if ($m === '-') continue;
  128. $exam_id = $exams[$k];
  129. $data = array(
  130. 'exam_id' => $exam_id,
  131. 'user_id' => $user_id,
  132. 'marks' => $m
  133. );
  134. if (!$this->validate_result($data)) {
  135. break;
  136. }
  137. $datasets[] = $data;
  138. }
  139. }
  140. return $datasets;
  141. }
  142. /**
  143. * Method to get the ordered list of exam_ids from the first line
  144. * of the csv
  145. * @return array of exam_ids in the order in which they appear in the
  146. */
  147. private function ordered_exams() {
  148. $exams = $this->_exams->as_array('name', 'id');
  149. // remove the first two headings and get only exam headings
  150. $exam_names = array_slice($this->_headings, 2);
  151. $ordered_exams = array();
  152. foreach ($exam_names as $exam_name) {
  153. if (!isset($exams[$exam_name])) {
  154. $e = 'Exam "%s" not found in the selected grading period. Make sure the correct csv is uploaded';
  155. throw new Examresult_Exception(sprintf($e, $exam_name));
  156. }
  157. $ordered_exams[] = $exams[$exam_name];
  158. }
  159. return $ordered_exams;
  160. }
  161. /**
  162. * Method to create the exam_wise_students and set it to the
  163. * $_exam_wise_students property of the instance
  164. */
  165. private function _get_exam_wise_students() {
  166. $arr = array();
  167. foreach ($this->_exams as $exam) {
  168. $users = $exam->course->users->find_all()->as_array('id');
  169. $arr[$exam->id] = array_keys($users);
  170. }
  171. return $arr;
  172. }
  173. private function validate_result($result) {
  174. return (
  175. $this->validate_marks($result) &&
  176. $this->validate_student($result['user_id']) &&
  177. $this->validate_student_marks($result['user_id'], $result['exam_id'], $result['marks'])
  178. );
  179. }
  180. /**
  181. * Method to validate the file uploaded.
  182. * @return boolean if its a csv file
  183. */
  184. private function validate_filetype() {
  185. $filename = $this->_file['name'];
  186. $extension = explode(".",$filename);
  187. if (isset($extension[1]) && strtolower($extension[1]) === "csv") {
  188. return true;
  189. } else {
  190. $error = 'Uploaded file not of type CSV';
  191. $this->_errors['invalid_extension'] = $error;
  192. throw new Examresult_Exception($error);
  193. }
  194. }
  195. /**
  196. * Method to validate that the marks entered for a result are less than the total
  197. * marks for the exam. If validation fails, append an error to the $_errors array
  198. * @param array result array having keys (exam_id, user_id, marks)
  199. * @return boolean
  200. */
  201. private function validate_marks($result) {
  202. $total_marks = $this->_exam_marks[$result['exam_id']];
  203. if ($result['marks'] > $total_marks) {
  204. $e = 'Marks entered for Student "%s" for "%s" are greater than total marks (%s)';
  205. $exam_name = Arr::get($this->_exam_names, $result['exam_id']);
  206. $student_name = Arr::get($this->_students, $result['user_id']);
  207. $error = sprintf($e, $student_name, $exam_name, $total_marks);
  208. throw new Examresult_Exception($error);
  209. }
  210. return true;
  211. }
  212. /**
  213. * Method to valdate that a student who;s marks are being uploaded is
  214. * valid ie the result for this examgroup is applicable to him
  215. */
  216. private function validate_student($user_id) {
  217. if (!isset($this->_students[$user_id])) {
  218. $e = 'This result is not applicable for Student "%s". Please recheck the csv file.';
  219. $student_name = Arr::get($this->_students, $user_id);
  220. $error = sprintf($e, $student_name);
  221. throw new Examresult_Exception($error);
  222. }
  223. return true;
  224. }
  225. /**
  226. * Method to validate that a marks entered for a student are correct. So that
  227. * even if the user replaces any of the '-' with a value it the upload fails &
  228. * record does not get stored in the db
  229. * is if student is eligible, marks cannot be '-'
  230. * whereas if student is not eligible, marks can only be '-'
  231. * @param int $user_id
  232. * @param int $exam_id
  233. * @param string $marks
  234. * @return boolean
  235. */
  236. private function validate_student_marks($user_id, $exam_id, $marks) {
  237. $student_eligible = in_array($user_id, $this->_exam_wise_students[$exam_id]);
  238. if (!$student_eligible && $marks !== '-') {
  239. $e = 'Exam "%s" is not applicable for Student "%s". So value must be "-" in the csv';
  240. $student_name = Arr::get($this->_students, $user_id);
  241. $exam_name = Arr::get($this->_exam_names, $exam_id);
  242. $error = sprintf($e, $exam_name, $student_name);
  243. throw new Examresult_Exception($error);
  244. }
  245. return true;
  246. }
  247. /*
  248. * Method to get the matrix which will be finally put into the csv
  249. * @param array $students keys = student_ids, values = Student Names
  250. * @param array $exams {keys = exam_ids, values = Exam Names}
  251. * @param array $results
  252. * eg. array(
  253. * 'student_id' => array('exam_id' => marks)
  254. * )
  255. */
  256. public static function matrix($students, $exams, $results) {
  257. $matrix = array();
  258. // the first header line
  259. $matrix[0] = array_values($exams);
  260. array_unshift($matrix[0], "Student Id", "Student Name");
  261. // print_r($matrix[0]); exit;
  262. foreach ($students as $student_id=>$student_name) {
  263. $line = array(
  264. $student_id,
  265. $student_name
  266. );
  267. foreach ($exams as $exam_id=>$exam_name) {
  268. $line[] = $results[$student_id][$exam_id];
  269. }
  270. $matrix[] = $line;
  271. }
  272. return $matrix;
  273. }
  274. }