1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
14:
15: 16: 17: 18:
19: class ClipitQuiz extends UBItem {
20: 21: 22:
23: const SUBTYPE = "ClipitQuiz";
24: const REL_QUIZ_TRICKYTOPIC = "ClipitQuiz-ClipitTrickyTopic";
25: const REL_QUIZ_QUIZQUESTION = "ClipitQuiz-ClipitQuizQuestion";
26: const REL_QUIZ_USER = "ClipitQuiz-ClipitUser";
27: const VIEW_MODE_LIST = "list";
28: const VIEW_MODE_PAGED = "paged";
29: const TARGET_CLIPIT = "clipit";
30: const TARGET_LARGEDISPLAY = "large_display";
31: 32: 33:
34: public $target = "";
35: 36: 37:
38: public $public = false;
39: 40: 41:
42: public $quiz_question_array = array();
43: 44: 45:
46: public $tricky_topic = 0;
47: 48: 49:
50: public $view_mode = "";
51: 52: 53:
54: public $max_time = 0;
55: 56: 57: 58: 59:
60: protected function copy_from_elgg($elgg_entity) {
61: parent::copy_from_elgg($elgg_entity);
62: $this->quiz_question_array = static::get_quiz_questions($this->id);
63: $this->public = (bool)$elgg_entity->get("public");
64: $this->tricky_topic = (int)static::get_tricky_topic($this->id);
65: $this->target = (string)$elgg_entity->get("target");
66: $this->view_mode = (string)$elgg_entity->get("view_mode");
67: $this->max_time = (int)$elgg_entity->get("max_time");
68: }
69:
70: 71: 72: 73: 74:
75: protected function copy_to_elgg($elgg_entity) {
76: parent::copy_to_elgg($elgg_entity);
77: $elgg_entity->set("public", (bool)$this->public);
78: $elgg_entity->set("target", (string)$this->target);
79: if((string)$this->view_mode == ""){
80: $elgg_entity->set("view_mode", static::VIEW_MODE_LIST);
81: }else{
82: $elgg_entity->set("view_mode", (string)$this->view_mode);
83: }
84: $elgg_entity->set("max_time", (int)$this->max_time);
85: }
86:
87: 88: 89: 90: 91:
92: protected function save($double_save=false) {
93: parent::save($double_save);
94: static::set_tricky_topic($this->id, (int)$this->tricky_topic);
95: static::set_quiz_questions($this->id, $this->quiz_question_array);
96: return $this->id;
97: }
98:
99: 100: 101: 102: 103: 104: 105: 106: 107:
108: static function create_clone($id, $linked = true, $keep_owner = false) {
109: $prop_value_array = static::get_properties($id);
110: if($keep_owner === false){
111: $prop_value_array["owner_id"] = elgg_get_logged_in_user_guid();
112: }
113: $quiz_question_array = $prop_value_array["quiz_question_array"];
114: if(!empty($quiz_question_array)){
115: $new_quiz_question_array = array();
116: foreach($quiz_question_array as $quiz_question_id){
117: $new_quiz_question_array[] = ClipitQuizQuestion::create_clone($quiz_question_id);
118: }
119: $prop_value_array["quiz_question_array"] = $new_quiz_question_array;
120: }
121: $clone_id = static::create($prop_value_array);
122: if($linked) {
123: static::link_parent_clone($id, $clone_id);
124: }
125: return $clone_id;
126: }
127:
128: 129: 130: 131: 132: 133: 134: 135: 136:
137: static function set_properties($id, $prop_value_array) {
138: $new_prop_value_array = array();
139: foreach($prop_value_array as $prop => $value) {
140: if($prop == "public") {
141: if($value == "true") {
142: $new_prop_value_array["public"] = true;
143: } elseif($value == "false") {
144: $new_prop_value_array["public"] = false;
145: } else {
146: $new_prop_value_array["public"] = (bool)$value;
147: }
148: } else {
149: $new_prop_value_array[$prop] = $value;
150: }
151: }
152: return parent::set_properties($id, $new_prop_value_array);
153: }
154:
155: static function get_task($id){
156: $task_array = UBCollection::get_items((int)$id, ClipitTask::REL_TASK_QUIZ, true);
157: if(empty($task_array)){
158: return 0;
159: }
160: return (int)array_pop($task_array);
161: }
162:
163: static function get_tricky_topic($id) {
164: $ret_array = UBCollection::get_items($id, static::REL_QUIZ_TRICKYTOPIC);
165: if(!empty($ret_array)){
166: return array_pop($ret_array);
167: }
168: return 0;
169: }
170:
171: static function set_tricky_topic($id, $tricky_topic) {
172: return UBCollection::set_items($id, array($tricky_topic), static::REL_QUIZ_TRICKYTOPIC);
173: }
174:
175: static function get_from_tricky_topic($tricky_topic_id) {
176: $id_array = UBCollection::get_items($tricky_topic_id, static::REL_QUIZ_TRICKYTOPIC, true);
177: $quiz_array = array();
178: foreach($id_array as $quiz_id) {
179: $quiz_array[] = new static($quiz_id);
180: }
181: return $quiz_array;
182: }
183:
184: static function set_quiz_as_finished($id, $user_id){
185: $start_timestamp = (int)static::get_quiz_start($id, $user_id);
186: if(empty($start_timestamp)) {
187: return null;
188: }
189: $prop_value_array = static::get_properties($id, array("max_time"));
190: $max_time = (int)$prop_value_array["max_time"];
191: $new_start_timestamp = (int)$start_timestamp - $max_time;
192: return UBCollection::set_timestamp($id, $user_id, static::REL_QUIZ_USER, $new_start_timestamp);
193: }
194:
195: static function set_quiz_start($id, $user_id){
196: return UBCollection::add_items($id, array($user_id), static::REL_QUIZ_USER);
197: }
198:
199: static function remove_quiz_start($id, $user_array){
200: return UBCollection::remove_items($id, $user_array, static::REL_QUIZ_USER);
201: }
202:
203: 204: 205: 206: 207:
208: static function get_quiz_start($id, $user_id){
209: return UBCollection::get_timestamp($id, $user_id, static::REL_QUIZ_USER);
210: }
211:
212: 213: 214: 215: 216: 217: 218:
219: static function has_finished_quiz($id, $user_id){
220:
221: $start_time = (int)static::get_quiz_start($id, $user_id);
222: if(empty($start_time)){
223: return false;
224: }
225:
226: $task_id = static::get_task($id);
227: if(ClipitTask::get_status($task_id) !== ClipitTask::STATUS_ACTIVE){
228: return true;
229: }
230: $prop_value_array = (array)static::get_properties($id, array("max_time"));
231: $max_time = (int)$prop_value_array["max_time"];
232: if($max_time == 0){
233: return false;
234: }
235: $current_time = (int)time();
236: if($start_time + $max_time <= $current_time){
237: return true;
238: }
239:
240: return false;
241: }
242:
243: static function questions_answered_by_user($id, $user_id){
244: if(empty($id) || empty($user_id)){
245: return null;
246: }
247: $quiz = new static($id);
248: $answered_questions = 0;
249: $user_results = ClipitQuizResult::get_by_owner(array($user_id));
250: $user_results = $user_results[$user_id];
251: if(empty($user_results)){
252: return $answered_questions;
253: }
254: foreach($user_results as $result){
255: if(array_search($result->quiz_question, $quiz->quiz_question_array) !== false){
256: $answered_questions++;
257: }
258: }
259: return $answered_questions;
260: }
261:
262: 263: 264: 265: 266: 267: 268: 269:
270: static function get_user_results_by_question($id, $user_id){
271: $result_array = array();
272: if(!static::has_finished_quiz($id, $user_id)){
273: return $result_array;
274: }
275: $quiz_question_array = static::get_quiz_questions($id);
276: if(empty($quiz_question_array)){
277: return $result_array;
278: }
279: foreach($quiz_question_array as $quiz_question_id){
280: $quiz_question = new ClipitQuizQuestion($quiz_question_id);
281: $quiz_result = ClipitQuizResult::get_from_question_user($quiz_question_id, $user_id);
282: if(!empty($quiz_result)) {
283: if ($quiz_result->correct) {
284: $result_array[$quiz_question->id]=1;
285: } else {
286: $result_array[$quiz_question->id]=0;
287: }
288: }
289: }
290: return $result_array;
291: }
292:
293: 294: 295: 296: 297: 298: 299: 300: 301: 302:
303: static function get_group_results_by_question($id, $group_id){
304: $group = new ClipitGroup($group_id);
305: $user_results = array();
306: foreach($group->user_array as $user_id){
307: $user_results[$user_id] = static::get_user_results_by_question($id, $user_id);
308: }
309: $group_results = array();
310: $user_count = 0;
311: foreach($user_results as $user=>$results){
312: if(empty($results)){
313: continue;
314: }
315: $user_count++;
316: foreach($results as $question_id=>$result){
317: if(!isset($group_results[$question_id])){
318: $group_results[$question_id] = (float)0.0;
319: }
320: $group_results[$question_id] += $result;
321: }
322: }
323: foreach($group_results as $question_id=>$result){
324: $group_results[$question_id] = (float)$result/$user_count;
325: }
326: return $group_results;
327: }
328:
329:
330: 331: 332: 333: 334: 335: 336: 337:
338: static function get_user_results_by_tag($id, $user_id){
339: $result_array = array();
340: if(!static::has_finished_quiz($id, $user_id)){
341: return $result_array;
342: }
343: $tag_count_array = array();
344: $quiz_question_array = static::get_quiz_questions($id);
345: if(empty($quiz_question_array)){
346: return $result_array;
347: }
348: foreach($quiz_question_array as $quiz_question_id){
349: $quiz_question = new ClipitQuizQuestion($quiz_question_id);
350: $quiz_result = ClipitQuizResult::get_from_question_user($quiz_question_id, $user_id);
351: foreach($quiz_question->tag_array as $tag_id){
352: if(!isset($tag_count_array[$tag_id])){
353: $tag_count_array[$tag_id] = (int)1;
354: $result_array[$tag_id] = (int)0;
355: } else {
356: $tag_count_array[$tag_id]++;
357: }
358: if(!empty($quiz_result)) {
359: if ($quiz_result->correct) {
360: $result_array[$tag_id]++;
361: }
362: }
363: }
364: }
365:
366: foreach($result_array as $tag_id=>$correct_answers){
367: $result_array[$tag_id] = (float)$correct_answers/$tag_count_array[$tag_id];
368: }
369: ksort($result_array);
370: return $result_array;
371: }
372:
373: 374: 375: 376: 377: 378: 379: 380: 381: 382:
383: static function get_group_results_by_tag($id, $group_id){
384: $group = new ClipitGroup($group_id);
385: $user_results = array();
386: foreach($group->user_array as $user_id){
387: $user_results[$user_id] = static::get_user_results_by_tag($id, $user_id);
388: }
389: $group_results = array();
390: $user_count = 0;
391: foreach($user_results as $user=>$results){
392: if(empty($results)){
393: continue;
394: }
395: $user_count++;
396: foreach($results as $tag_id=>$result){
397: if(!isset($group_results[$tag_id])){
398: $group_results[$tag_id] = (float)0.0;
399: }
400: $group_results[$tag_id] += $result;
401: }
402: }
403: foreach($group_results as $tag_id=>$result){
404: $group_results[$tag_id] = (float)$result/$user_count;
405: }
406: ksort($group_results);
407: return $group_results;
408: }
409:
410: 411: 412: 413: 414: 415: 416: 417:
418: static function get_quiz_results_by_tag($id){
419: $task_id = static::get_task($id);
420: $activity_id = ClipitTask::get_activity($task_id);
421: $user_array = ClipitActivity::get_students($activity_id);
422: $user_results = array();
423: foreach($user_array as $user_id){
424: $user_results[$user_id] = static::get_user_results_by_tag($id, $user_id);
425: }
426: $quiz_results = array();
427: $user_count = 0;
428: foreach($user_results as $user=>$results){
429: if(empty($results)){
430: continue;
431: }
432: $user_count++;
433: foreach($results as $tag_id=>$result){
434: if(!isset($quiz_results[$tag_id])){
435: $quiz_results[$tag_id] = (float)0.0;
436: }
437: $quiz_results[$tag_id] += $result;
438: }
439: }
440: foreach($quiz_results as $tag_id=>$result){
441: $quiz_results[$tag_id] = (float)$result/$user_count;
442: }
443: ksort($quiz_results);
444: return $quiz_results;
445: }
446:
447: static function evaluate_results($id){
448: $quiz_questions = static::get_quiz_questions($id);
449: foreach($quiz_questions as $quiz_question) {
450: ClipitQuizQuestion::evaluate_results($quiz_question);
451: }
452: return true;
453: }
454:
455: 456: 457: 458: 459: 460: 461: 462:
463: static function add_quiz_questions($id, $question_array) {
464: return UBCollection::add_items($id, $question_array, static::REL_QUIZ_QUIZQUESTION, true);
465: }
466:
467: 468: 469: 470: 471: 472: 473: 474:
475: static function set_quiz_questions($id, $question_array) {
476: return UBCollection::set_items($id, $question_array, static::REL_QUIZ_QUIZQUESTION, true);
477: }
478:
479: 480: 481: 482: 483: 484: 485: 486:
487: static function remove_quiz_questions($id, $question_array) {
488: return UBCollection::remove_items($id, $question_array, static::REL_QUIZ_QUIZQUESTION);
489: }
490:
491: 492: 493: 494: 495: 496: 497:
498: static function get_quiz_questions($id) {
499: return UBCollection::get_items($id, static::REL_QUIZ_QUIZQUESTION);
500: }
501:
502: static function export_to_excel($id){
503: $quiz = static::get_by_id(array($id));
504: if(empty($quiz)){
505: return null;
506: }
507: $quiz = array_pop($quiz);
508: $task_id = ClipitQuiz::get_task($id);
509: $activity_id = ClipitTask::get_activity($task_id);
510: $user_ids = ClipitActivity::get_students($activity_id);
511: $quiz_question_array = ClipitQuizQuestion::get_by_id(ClipitQuiz::get_quiz_questions($id));
512:
513:
514: $php_excel = new PHPExcel();
515:
516:
517: $php_excel->getProperties()->setCreator("ClipIt")
518: ->setTitle("ClipIt export of Quiz " . $quiz->name)
519: ->setKeywords("clipit export quiz");
520:
521:
522: $sheet_0 = $php_excel->getSheet(0);
523: $sheet_0->setTitle("Scores");
524: $sheet_0->getDefaultStyle()->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);
525: $sheet_0->getDefaultStyle()->getAlignment()->setWrapText(true);
526: $sheet_0->getDefaultRowDimension()->setRowHeight(-1);
527: $sheet_0->getDefaultColumnDimension()->setWidth(30);
528:
529:
530:
531:
532: $col_0 = 0; $row_0 = 1;
533: $sheet_0->getStyle($row_0)->getFont()->setBold(true);
534: $sheet_0->setCellValueByColumnAndRow($col_0++, $row_0, "QUIZ NAME");
535: $sheet_0->setCellValueByColumnAndRow($col_0++, $row_0, "TRICKY TOPIC");
536: $sheet_0->setCellValueByColumnAndRow($col_0++, $row_0, "MAX TIME (mins)");
537:
538:
539: $col_0=0; $row_0++;
540: $sheet_0->setCellValueByColumnAndRow($col_0++, $row_0, $quiz->name);
541: $tricky_topic = ClipitTrickyTopic::get_by_id(array($quiz->tricky_topic));
542: if(!empty($tricky_topic)) {
543: $tricky_topic = array_pop($tricky_topic);
544: $sheet_0->setCellValueByColumnAndRow($col_0++, $row_0, $tricky_topic->name);
545: } else{
546: $col_0++;
547: }
548: $sheet_0->setCellValueByColumnAndRow($col_0++, $row_0, ((int)$quiz->max_time)/60);
549:
550:
551: $col_0 = 0; $row_0 += 2;
552: $sheet_0->getStyle($row_0)->getFont()->setBold(true);
553: $sheet_0->setCellValueByColumnAndRow($col_0++, $row_0, "STUDENT NAME");
554: $sheet_0->setCellValueByColumnAndRow($col_0++, $row_0, "CORRECT ANSWERS");
555: $sheet_0->setCellValueByColumnAndRow($col_0++, $row_0, "SCORE");
556:
557:
558: $col_0 = 0; $row_0++;
559: foreach($user_ids as $user_id){
560: $pv_array = ClipitUser::get_properties($user_id, array("name"));
561: $user_names[$user_id] = $pv_array["name"];
562: $sheet_0->setCellValueByColumnAndRow($col_0++, $row_0, $pv_array["name"]);
563: $results_by_question = ClipitQuiz::get_user_results_by_question($id, $user_id);
564: $total_correct = 0;
565: foreach($results_by_question as $question_id => $result){
566: $total_correct += (int)$result;
567: }
568: $sheet_0->setCellValueByColumnAndRow($col_0++, $row_0, $total_correct." out of ".count($quiz->quiz_question_array));
569: $percentage = (float)($total_correct / count($quiz->quiz_question_array) * 100.0);
570: $percentage = round($percentage, 2);
571: $sheet_0->setCellValueByColumnAndRow($col_0++, $row_0, $percentage."%");
572: $col_0 = 0; $row_0++;
573: }
574:
575:
576: $sheet_1 = $php_excel->createSheet(1);
577: $sheet_1->setTitle("Answers");
578: $sheet_1->getDefaultStyle()->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);
579: $sheet_1->getDefaultStyle()->getAlignment()->setWrapText(true);
580: $sheet_1->getDefaultRowDimension()->setRowHeight(-1);
581: $sheet_1->getDefaultColumnDimension()->setWidth(30);
582:
583:
584: $col_1 = 0; $row_1 = 1;
585: $sheet_1->getColumnDimension('A')->setWidth(30);
586: $sheet_1->getStyleByColumnAndRow($col_1, $row_1)->getFont()->setBold(true);
587: $sheet_1->setCellValueByColumnAndRow($col_1, $row_1++, "QUESTION NUMBER");
588: $sheet_1->getStyleByColumnAndRow($col_1, $row_1)->getFont()->setBold(true);
589: $sheet_1->setCellValueByColumnAndRow($col_1, $row_1++, "QUESTION TITLE");
590: $sheet_1->getStyleByColumnAndRow($col_1, $row_1)->getFont()->setBold(true);
591: $sheet_1->setCellValueByColumnAndRow($col_1, $row_1++, "CORRECT ANSWER(S)");
592: $sheet_1->getStyleByColumnAndRow($col_1, $row_1)->getFont()->setBold(true);
593: $sheet_1->setCellValueByColumnAndRow($col_1, $row_1++, "STUDENT NAME");
594:
595:
596: $col_1 = 1; $row_1 = 1;
597: foreach($quiz_question_array as $quiz_question){
598: $sheet_1->setCellValueByColumnAndRow($col_1, $row_1++, (int)$quiz_question->order);
599: $sheet_1->setCellValueByColumnAndRow($col_1, $row_1++, (string)$quiz_question->name);
600: $correct_answer = "";
601: switch($quiz_question->option_type){
602: case ClipitQuizQuestion::TYPE_SELECT_MULTI:
603: case ClipitQuizQuestion::TYPE_SELECT_ONE:
604: $i = 0;
605: $multi = false;
606: $num_options = count($quiz_question->option_array);
607: while($i < $num_options){
608: if($quiz_question->validation_array[$i]){
609: if($multi) {
610: $correct_answer = $correct_answer."\n ";
611: } else{
612: $multi = true;
613: }
614: $correct_answer = $correct_answer.$quiz_question->option_array[$i];
615: }
616: $i++;
617: }
618: break;
619: case ClipitQuizQuestion::TYPE_TRUE_FALSE:
620: if($quiz_question->validation_array[0]){
621: $correct_answer = "TRUE";
622: } elseif($quiz_question->validation_array[1]){
623: $correct_answer = "FALSE";
624: }
625: break;
626: case ClipitQuizQuestion::TYPE_NUMBER:
627: $correct_answer = $quiz_question->validation_array[0];
628: break;
629: default:
630: $correct_answer = ("<ERROR>");
631: }
632: $fill_color = new PHPExcel_Style_Color(PHPExcel_Style_Color::COLOR_GREEN);
633: $sheet_1->getStyleByColumnAndRow($col_1, $row_1)->getFill()->setFillType(PHPExcel_Style_Fill::FILL_SOLID);
634: $sheet_1->getStyleByColumnAndRow($col_1, $row_1)->getFill()->setStartColor($fill_color);
635: $sheet_1->setCellValueByColumnAndRow($col_1, $row_1++, (string)$correct_answer);
636: $sheet_1->setCellValueByColumnAndRow($col_1, $row_1, "STUDENT ANSWER");
637: $sheet_1->getStyleByColumnAndRow($col_1, $row_1)->getFont()->setBold(true);
638: $col_1++; $row_1 = 1;
639: }
640:
641:
642: $col_1 = 0; $row_1 = 5;
643: foreach($user_ids as $user_id){
644: $sheet_1->setCellValueByColumnAndRow($col_1, $row_1++, $user_names[$user_id]);
645: }
646:
647:
648: $col_1 = 1; $row_1 = 5;
649: foreach($user_ids as $user_id){
650: foreach($quiz_question_array as $quiz_question){
651: $quiz_result = ClipitQuizResult::get_from_question_user($quiz_question->id, $user_id);
652: if(empty($quiz_result)){
653: $sheet_1->setCellValueByColumnAndRow($col_1, $row_1, "<not answered>");
654: $col_1++;
655: continue;
656: }
657: $answer = "";
658: switch($quiz_question->option_type){
659: case ClipitQuizQuestion::TYPE_SELECT_MULTI:
660: case ClipitQuizQuestion::TYPE_SELECT_ONE:
661: $i = 0;
662: $multi = false;
663: $num_options = count($quiz_question->option_array);
664: while($i < $num_options){
665: if($quiz_result->answer[$i]){
666: if($multi) {
667: $answer = $answer."\n ";
668: } else{
669: $multi = true;
670: }
671: $answer = $answer.$quiz_question->option_array[$i];
672: }
673: $i++;
674: }
675: break;
676: case ClipitQuizQuestion::TYPE_TRUE_FALSE:
677: if($quiz_result->answer[0]){
678: $answer = "TRUE";
679: } elseif($quiz_result->answer[1]){
680: $answer = "FALSE";
681: }
682: break;
683: case ClipitQuizQuestion::TYPE_NUMBER:
684: $answer = $quiz_result->answer;
685: break;
686: default:
687: $answer = ("<ERROR>");
688: }
689: $sheet_1->setCellValueByColumnAndRow($col_1, $row_1, (string)$answer);
690: if($quiz_result->correct){
691: $fill_color = new PHPExcel_Style_Color(PHPExcel_Style_Color::COLOR_GREEN);
692: } else{
693: $fill_color = new PHPExcel_Style_Color(PHPExcel_Style_Color::COLOR_RED);
694: }
695: $sheet_1->getStyleByColumnAndRow($col_1, $row_1)->getFill()->setFillType(PHPExcel_Style_Fill::FILL_SOLID);
696: $sheet_1->getStyleByColumnAndRow($col_1, $row_1)->getFill()->setStartColor($fill_color);
697: $col_1++;
698: }
699: $col_1 = 1; $row_1++;
700: }
701:
702:
703: $php_excel->setActiveSheetIndex(0);
704: $date_obj = new DateTime();
705: $timestamp = $date_obj->getTimestamp();
706: $objWriter = PHPExcel_IOFactory::createWriter($php_excel, 'Excel2007');
707: $filename = (string)"/tmp/".$timestamp."_quiz_export.xlsx";
708: $objWriter->save($filename);
709: return $filename;
710: }
711: }