upload_exercise.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * Upload quiz: This script shows the upload quiz feature
  5. * Initial work by Isaac flores on Nov 4 of 2010
  6. * Encoding fixes Julio Montoya
  7. * @package chamilo.exercise
  8. */
  9. use \ChamiloSession as Session;
  10. $language_file[] = 'learnpath';
  11. $language_file[] = 'exercice';
  12. // setting the help
  13. $help_content = 'exercise_upload';
  14. // including the global Dokeos file
  15. require_once '../inc/global.inc.php';
  16. require_once api_get_path(LIBRARY_PATH) . 'pear/excelreader/reader.php';
  17. // Security check
  18. $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
  19. if (!$is_allowed_to_edit) {
  20. api_not_allowed(true);
  21. }
  22. // setting the tabs
  23. $this_section = SECTION_COURSES;
  24. $htmlHeadXtra[] = "<script>
  25. $(document).ready( function(){
  26. $('#user_custom_score').click(function() {
  27. $('#options').toggle();
  28. });
  29. });
  30. </script>";
  31. // Action handling
  32. lp_upload_quiz_action_handling();
  33. $interbreadcrumb[]= array ("url"=>"exercice.php", "name"=> get_lang('Exercices'));
  34. // Display the header
  35. Display :: display_header(get_lang('ImportExcelQuiz'), 'Exercises');
  36. if (isset($_GET['message'])) {
  37. if (in_array($_GET['message'], array('ExerciseEdited'))) {
  38. Display :: display_confirmation_message(get_lang($_GET['message']));
  39. }
  40. }
  41. // display the actions
  42. echo '<div class="actions">';
  43. echo lp_upload_quiz_actions();
  44. echo '</div>';
  45. // the main content
  46. lp_upload_quiz_main();
  47. function lp_upload_quiz_actions() {
  48. $return = '<a href="exercice.php?'.api_get_cidReq().'">'.
  49. Display::return_icon('back.png', get_lang('BackToExercisesList'),'',ICON_SIZE_MEDIUM).'</a>';
  50. return $return;
  51. }
  52. function lp_upload_quiz_secondary_actions() {
  53. $lp_id = Security::remove_XSS($_GET['lp_id']);
  54. $return = '';
  55. $return .= '<a href="exercise_report.php?' . api_get_cidreq() . '">' .
  56. Display :: return_icon('reporting32.png', get_lang('Tracking')) . get_lang('Tracking') . '</a>';
  57. return $return;
  58. }
  59. function lp_upload_quiz_main() {
  60. // variable initialisation
  61. $lp_id = isset($_GET['lp_id']) ? Security::remove_XSS($_GET['lp_id']) : null;
  62. $form = new FormValidator('upload', 'POST', api_get_self() . '?' . api_get_cidreq() . '&lp_id='.$lp_id, '', array('enctype' => 'multipart/form-data'));
  63. $form->addElement('header', get_lang('ImportExcelQuiz'));
  64. $form->addElement('file', 'user_upload_quiz', get_lang('FileUpload'));
  65. $link = '<a href="../exercice/quiz_template.xls">'.
  66. Display::return_icon('export_excel.png', get_lang('DownloadExcelTemplate')).get_lang('DownloadExcelTemplate').'</a>';
  67. $form->addElement('advanced_settings', $link);
  68. $form->addElement('checkbox', 'user_custom_score', null, get_lang('UseCustomScoreForAllQuestions'), array('id'=> 'user_custom_score'));
  69. $form->addElement('html', '<div id="options" style="display:none">');
  70. $form->addElement('text', 'correct_score', get_lang('CorrectScore'));
  71. $form->addElement('text', 'incorrect_score', get_lang('IncorrectScore'));
  72. $form->addElement('html', '</div>');
  73. $form->addRule('user_upload_quiz', get_lang('ThisFieldIsRequired'), 'required');
  74. $form->add_progress_bar();
  75. $form->addElement('style_submit_button', 'submit_upload_quiz', get_lang('Send'), 'class="upload"');
  76. // Display the upload field
  77. $form->display();
  78. }
  79. /**
  80. * Handles a given Excel spreadsheets as in the template provided
  81. */
  82. function lp_upload_quiz_action_handling() {
  83. global $debug;
  84. $_course = api_get_course_info();
  85. $courseId = $_course['real_id'];
  86. if (!isset($_POST['submit_upload_quiz'])) {
  87. return;
  88. }
  89. // Get the extension of the document.
  90. $path_info = pathinfo($_FILES['user_upload_quiz']['name']);
  91. // Check if the document is an Excel document
  92. if ($path_info['extension'] != 'xls') {
  93. return;
  94. }
  95. // Read the Excel document
  96. $data = new Spreadsheet_Excel_Reader();
  97. // Set output Encoding.
  98. $data->setOutputEncoding(api_get_system_encoding());
  99. // Reading the xls document.
  100. $data->read($_FILES['user_upload_quiz']['tmp_name']);
  101. $correctScore = isset($_POST['correct_score']) ? $_POST['correct_score'] : null;
  102. $incorrectScore = isset($_POST['incorrect_score']) ? $_POST['incorrect_score'] : null;
  103. $useCustomScore = isset($_POST['user_custom_score']) ? true : false;
  104. $propagateNegative = 0;
  105. if ($useCustomScore && !empty($incorrectScore)) {
  106. if ($incorrectScore < 0) {
  107. $propagateNegative = 1;
  108. }
  109. }
  110. // Variables
  111. $quiz_index = 0;
  112. $question_title_index = array();
  113. $question_name_index_init = array();
  114. $question_name_index_end = array();
  115. $score_index = array();
  116. $feedback_true_index = array();
  117. $feedback_false_index = array();
  118. $number_questions = 0;
  119. $question_description_index = array();
  120. // Reading all the first column items sequentially to create breakpoints
  121. for ($i = 1; $i <= $data->sheets[0]['numRows']; $i++) {
  122. if ($data->sheets[0]['cells'][$i][1] == 'Quiz' && $i == 1) {
  123. $quiz_index = $i; // Quiz title position, only occurs once
  124. } elseif ($data->sheets[0]['cells'][$i][1] == 'Question') {
  125. $question_title_index[] = $i; // Question title position line
  126. $question_name_index_init[] = $i + 1; // Questions name 1st position line
  127. $number_questions++;
  128. } elseif ($data->sheets[0]['cells'][$i][1] == 'Score') {
  129. $question_name_index_end[] = $i - 1; // Question name position
  130. $score_index[] = $i; // Question score position
  131. } elseif ($data->sheets[0]['cells'][$i][1] == 'FeedbackTrue') {
  132. $feedback_true_index[] = $i; // FeedbackTrue position (line)
  133. } elseif ($data->sheets[0]['cells'][$i][1] == 'FeedbackFalse') {
  134. $feedback_false_index[] = $i; // FeedbackFalse position (line)
  135. } elseif ($data->sheets[0]['cells'][$i][1] == 'EnrichQuestion') {
  136. $question_description_index[] = $i;
  137. }
  138. }
  139. // Variables
  140. $quiz = array();
  141. $question = array();
  142. $new_answer = array();
  143. $score_list = array();
  144. $feedback_true_list = array();
  145. $feedback_false_list = array();
  146. $question_description = array();
  147. // Getting questions.
  148. $k = $z = $q = $l = $m = 0;
  149. for ($i = 1; $i <= $data->sheets[0]['numRows']; $i++) {
  150. if (is_array($data->sheets[0]['cells'][$i])) {
  151. $column_data = $data->sheets[0]['cells'][$i];
  152. // Fill all column with data to have a full array
  153. for ($x = 1; $x <= $data->sheets[0]['numCols']; $x++) {
  154. if (empty($column_data[$x])) {
  155. $data->sheets[0]['cells'][$i][$x] = '';
  156. }
  157. }
  158. // Array filled with data
  159. $column_data = $data->sheets[0]['cells'][$i];
  160. } else {
  161. $column_data = '';
  162. }
  163. // Fill quiz data
  164. if ($quiz_index == $i) {
  165. // The title always in the first position
  166. $quiz = $column_data;
  167. } elseif (in_array($i, $question_title_index)) {
  168. //a complete line where 1st column is 'Question'
  169. $question[$k] = $column_data;
  170. $k++;
  171. } elseif (in_array($i, $score_index)) {
  172. //a complete line where 1st column is 'Score'
  173. $score_list[$z] = $column_data;
  174. $z++;
  175. } elseif (in_array($i, $feedback_true_index)) {
  176. //a complete line where 1st column is 'FeedbackTrue'
  177. $feedback_true_list[$q] = $column_data;
  178. $q++;
  179. } elseif (in_array($i, $feedback_false_index)) {
  180. //a complete line where 1st column is 'FeedbackFalse' for wrong answers
  181. $feedback_false_list[$l] = $column_data;
  182. $l++;
  183. } elseif (in_array($i, $question_description_index)) {
  184. //a complete line where 1st column is 'EnrichQuestion'
  185. $question_description[$m] = $column_data;
  186. $m++;
  187. }
  188. }
  189. // Get answers
  190. for ($i = 0; $i < count($question_name_index_init); $i++) {
  191. for ($j = $question_name_index_init[$i]; $j <= $question_name_index_end[$i]; $j++) {
  192. if (is_array($data->sheets[0]['cells'][$j])) {
  193. $column_data = $data->sheets[0]['cells'][$j];
  194. // Fill all column with data
  195. for ($x = 1; $x <= $data->sheets[0]['numCols']; $x++) {
  196. if (empty($column_data[$x])) {
  197. $data->sheets[0]['cells'][$j][$x] = '';
  198. }
  199. }
  200. $column_data = $data->sheets[0]['cells'][$j];
  201. // Array filled of data
  202. if (is_array($data->sheets[0]['cells'][$j]) && count($data->sheets[0]['cells'][$j]) > 0) {
  203. $new_answer[$i][$j] = $data->sheets[0]['cells'][$j];
  204. }
  205. }
  206. }
  207. }
  208. // Quiz title.
  209. $quiz_title = $quiz[2];
  210. if ($quiz_title != '') {
  211. // Variables
  212. $type = 2;
  213. $random = $active = $results = $max_attempt = $expired_time = 0;
  214. // Make sure feedback is enabled (3 to disable), otherwise the fields
  215. // added to the XLS are not shown, which is confusing
  216. $feedback = 0;
  217. // Quiz object
  218. $exercise = new Exercise();
  219. //
  220. $quiz_id = $exercise->createExercise(
  221. $quiz_title,
  222. $expired_time,
  223. $type,
  224. $random,
  225. $active,
  226. $results,
  227. $max_attempt,
  228. $feedback,
  229. $propagateNegative
  230. );
  231. if ($quiz_id) {
  232. // insert into the item_property table
  233. api_item_property_update($_course, TOOL_QUIZ, $quiz_id, 'QuizAdded', api_get_user_id());
  234. // Import questions.
  235. for ($i = 0; $i < $number_questions; $i++) {
  236. // Question name
  237. $question_title = $question[$i][2];
  238. $question_description_text = "<p></p>";
  239. if (isset($question_description[$i][2])) {
  240. // Question description.
  241. $question_description_text = "<p>".$question_description[$i][2]."</p>";
  242. }
  243. // Unique answers are the only question types available for now
  244. // through xls-format import
  245. $question_id = null;
  246. $detectQuestionType = detectQuestionType(
  247. $new_answer[$i],
  248. $score_list
  249. );
  250. /** @var Question $answer */
  251. switch ($detectQuestionType) {
  252. case FREE_ANSWER:
  253. $answer = new FreeAnswer();
  254. break;
  255. case GLOBAL_MULTIPLE_ANSWER:
  256. $answer = new GlobalMultipleAnswer();
  257. break;
  258. case MULTIPLE_ANSWER:
  259. $answer = new MultipleAnswer();
  260. break;
  261. case UNIQUE_ANSWER:
  262. default:
  263. $answer = new UniqueAnswer();
  264. break;
  265. }
  266. if ($question_title != '') {
  267. $question_id = $answer->create_question(
  268. $quiz_id,
  269. $question_title,
  270. $question_description_text,
  271. 0, // max score
  272. $answer->type
  273. );
  274. }
  275. $total = 0;
  276. if (is_array($new_answer[$i]) && !empty($question_id)) {
  277. $id = 1;
  278. $answers_data = $new_answer[$i];
  279. $globalScore = null;
  280. $objAnswer = new Answer($question_id, $courseId);
  281. $globalScore = $score_list[$i][3];
  282. // Calculate the number of correct answers to divide the
  283. // score between them when importing from CSV
  284. $numberRightAnswers = 0;
  285. foreach ($answers_data as $answer_data) {
  286. if (strtolower($answer_data[3]) == 'x') {
  287. $numberRightAnswers++;
  288. }
  289. }
  290. foreach ($answers_data as $answer_data) {
  291. $answerValue = $answer_data[2];
  292. $correct = 0;
  293. $score = 0;
  294. if (strtolower($answer_data[3]) == 'x') {
  295. $correct = 1;
  296. $score = $score_list[$i][3];
  297. $comment = $feedback_true_list[$i][2];
  298. } else {
  299. $comment = $feedback_false_list[$i][2];
  300. $floatVal = (float)$answer_data[3];
  301. if (is_numeric($floatVal)) {
  302. $score = $answer_data[3];
  303. }
  304. }
  305. //var_dump($answerValue);
  306. if ($useCustomScore) {
  307. if ($correct) {
  308. $score = $correctScore;
  309. } else {
  310. $score = $incorrectScore;
  311. }
  312. }
  313. // Fixing scores:
  314. switch ($detectQuestionType) {
  315. case GLOBAL_MULTIPLE_ANSWER:
  316. $score /= $numberRightAnswers;
  317. break;
  318. case UNIQUE_ANSWER:
  319. break;
  320. case MULTIPLE_ANSWER:
  321. if (!$correct) {
  322. //$total = $total - $score;
  323. }
  324. break;
  325. }
  326. $objAnswer->createAnswer(
  327. $answerValue,
  328. $correct,
  329. $comment,
  330. $score,
  331. $id
  332. );
  333. $total += $score;
  334. $id++;
  335. //var_dump($score);
  336. }
  337. $objAnswer->save();
  338. $questionObj = Question::read($question_id, $courseId);
  339. switch ($detectQuestionType) {
  340. case GLOBAL_MULTIPLE_ANSWER:
  341. $questionObj->updateWeighting($globalScore);
  342. break;
  343. case UNIQUE_ANSWER:
  344. case MULTIPLE_ANSWER:
  345. default:
  346. $questionObj->updateWeighting($total);
  347. break;
  348. }
  349. //var_dump($total);
  350. $questionObj->save();
  351. } else if ($detectQuestionType === FREE_ANSWER) {
  352. $questionObj = Question::read($question_id, $courseId);
  353. $globalScore = $score_list[$i][3];
  354. $questionObj->updateWeighting($globalScore);
  355. $questionObj->save();
  356. }
  357. }
  358. }
  359. // exit;
  360. if (isset($_SESSION['lpobject'])) {
  361. if ($debug > 0) {
  362. error_log('New LP - SESSION[lpobject] is defined', 0);
  363. }
  364. $oLP = unserialize($_SESSION['lpobject']);
  365. if (is_object($oLP)) {
  366. if ($debug > 0) {
  367. error_log('New LP - oLP is object', 0);
  368. }
  369. if ((empty($oLP->cc)) OR $oLP->cc != api_get_course_id()) {
  370. if ($debug > 0) {
  371. error_log('New LP - Course has changed, discard lp object', 0);
  372. }
  373. $oLP = null;
  374. Session::erase('oLP');
  375. Session::erase('lpobject');
  376. } else {
  377. $_SESSION['oLP'] = $oLP;
  378. }
  379. }
  380. }
  381. if (isset($_SESSION['oLP']) && isset($_GET['lp_id'])) {
  382. $previous = $_SESSION['oLP']->select_previous_item_id();
  383. $parent = 0;
  384. // Add a Quiz as Lp Item
  385. $_SESSION['oLP']->add_item($parent, $previous, TOOL_QUIZ, $quiz_id, $quiz_title, '');
  386. // Redirect to home page for add more content
  387. header('location: ../newscorm/lp_controller.php?'.api_get_cidreq().'&action=add_item&type=step&lp_id='.Security::remove_XSS($_GET['lp_id']));
  388. exit;
  389. } else {
  390. // header('location: exercice.php?' . api_get_cidreq());
  391. echo '<script>window.location.href = "'.api_get_path(WEB_CODE_PATH).'exercice/admin.php?'.api_get_cidReq().'&exerciseId='.$quiz_id.'&session_id='.api_get_session_id().'"</script>';
  392. }
  393. }
  394. }
  395. /**
  396. * @param array $answers_data
  397. * @return int
  398. */
  399. function detectQuestionType($answers_data) {
  400. $correct = 0;
  401. $isNumeric = false;
  402. foreach ($answers_data as $answer_data) {
  403. if (strtolower($answer_data[3]) == 'x') {
  404. $correct++;
  405. } else {
  406. if (is_numeric($answer_data[3])) {
  407. $isNumeric = true;
  408. }
  409. }
  410. }
  411. $type = '';
  412. if ($correct == 1) {
  413. $type = UNIQUE_ANSWER;
  414. } else if ($correct > 1) {
  415. $type = MULTIPLE_ANSWER;
  416. } else {
  417. $type = FREE_ANSWER;
  418. }
  419. if ($type == MULTIPLE_ANSWER) {
  420. if ($isNumeric) {
  421. $type = MULTIPLE_ANSWER;
  422. } else {
  423. $type = GLOBAL_MULTIPLE_ANSWER;
  424. }
  425. }
  426. return $type;
  427. }
  428. if (!isset($origin) || isset($origin) && $origin != 'learnpath') {
  429. //so we are not in learnpath tool
  430. Display :: display_footer();
  431. }