ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
class.ilAssQuestionList.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4require_once 'Services/Taxonomy/interfaces/interface.ilTaxAssignedItemInfo.php';
5require_once 'Modules/TestQuestionPool/classes/questions/class.ilAssQuestionType.php';
6
17{
23 protected $db = null;
24
30 private $lng = null;
31
37 private $pluginAdmin = null;
38
44 private $parentObjIdsFilter = array();
45
51 private $parentObjId = null;
52
58 private $parentObjType = 'qpl';
59
65 private $availableTaxonomyIds = array();
66
72 private $fieldFilters = array();
73
79 private $taxFilters = array();
80
86 private $taxParentIds = array();
87
93 private $taxParentTypes = array();
94
100 private $answerStatusActiveId = null;
101
105 private $forcedQuestionIds = array();
106
111 protected $join_obj_data = true;
112
113
120
127
133 private $answerStatusFilter = null;
134
135 const QUESTION_INSTANCE_TYPE_ORIGINALS = 'QST_INSTANCE_TYPE_ORIGINALS';
136 const QUESTION_INSTANCE_TYPE_DUPLICATES = 'QST_INSTANCE_TYPE_DUPLICATES';
138
141
144 const QUESTION_COMPLETION_STATUS_BOTH = 'complete/incomplete';
146
152 protected $questions = array();
153
162 {
163 $this->db = $db;
164 $this->lng = $lng;
165 $this->pluginAdmin = $pluginAdmin;
166 }
167
168 public function getParentObjId()
169 {
170 return $this->parentObjId;
171 }
172
174 {
175 $this->parentObjId = $parentObjId;
176 }
177
178 public function getParentObjectType()
179 {
181 }
182
184 {
185 $this->parentObjType = $parentObjType;
186 }
187
191 public function getParentObjIdsFilter()
192 {
194 }
195
200 {
201 $this->parentObjIdsFilter = $parentObjIdsFilter;
202 }
203
205 {
206 $this->questionInstanceTypeFilter = $questionInstanceTypeFilter;
207 }
208
210 {
212 }
213
214 public function setIncludeQuestionIdsFilter($questionIdsFilter)
215 {
216 $this->includeQuestionIdsFilter = $questionIdsFilter;
217 }
218
220 {
222 }
223
225 {
227 }
228
230 {
231 $this->excludeQuestionIdsFilter = $excludeQuestionIdsFilter;
232 }
233
235 {
237 }
238
240 {
241 $this->questionCompletionStatusFilter = $questionCompletionStatusFilter;
242 }
243
244 public function addFieldFilter($fieldName, $fieldValue)
245 {
246 $this->fieldFilters[$fieldName] = $fieldValue;
247 }
248
249 public function addTaxonomyFilter($taxId, $taxNodes, $parentObjId, $parentObjType)
250 {
251 $this->taxFilters[$taxId] = $taxNodes;
252 $this->taxParentIds[$taxId] = $parentObjId;
253 $this->taxParentTypes[$taxId] = $parentObjType;
254 }
255
257 {
258 $this->availableTaxonomyIds = $availableTaxonomyIds;
259 }
260
261 public function getAvailableTaxonomyIds()
262 {
264 }
265
267 {
268 $this->answerStatusActiveId = $answerStatusActiveId;
269 }
270
271 public function getAnswerStatusActiveId()
272 {
274 }
275
277 {
278 $this->answerStatusFilter = $answerStatusFilter;
279 }
280
281 public function getAnswerStatusFilter()
282 {
284 }
285
291 public function setJoinObjectData($a_val)
292 {
293 $this->join_obj_data = $a_val;
294 }
295
301 public function getJoinObjectData()
302 {
304 }
305
310 {
311 $this->forcedQuestionIds = $forcedQuestionIds;
312 }
313
317 public function getForcedQuestionIds()
318 {
320 }
321
323 {
324 if( $this->getParentObjId() )
325 {
326 return 'qpl_questions.obj_fi = '.$this->db->quote($this->getParentObjId(), 'integer');
327 }
328
329 if( count($this->getParentObjIdsFilter()) )
330 {
331 return $this->db->in('qpl_questions.obj_fi', $this->getParentObjIdsFilter(), false, 'integer');
332 }
333
334 return null;
335 }
336
337 private function getFieldFilterExpressions()
338 {
339 $expressions = array();
340
341 foreach($this->fieldFilters as $fieldName => $fieldValue)
342 {
343 switch($fieldName)
344 {
345 case 'title':
346 case 'description':
347 case 'author':
348
349 $expressions[] = $this->db->like('qpl_questions.' . $fieldName, 'text', "%%$fieldValue%%");
350 break;
351
352 case 'type':
353
354 $expressions[] = "qpl_qst_type.type_tag = {$this->db->quote($fieldValue, 'text')}";
355 break;
356
357 case 'question_id':
358 if ($fieldValue != "" && !is_array($fieldValue))
359 {
360 $fieldValue = array($fieldValue);
361 }
362 $expressions[] = $this->db->in("qpl_questions.question_id", $fieldValue, false, "integer");
363 break;
364
365 case 'parent_title':
366 if ($this->join_obj_data)
367 {
368 $expressions[] = $this->db->like('object_data.title', 'text', "%%$fieldValue%%");
369 }
370 break;
371 }
372 }
373
374 return $expressions;
375 }
376
378 {
379 $expressions = array();
380
381 require_once 'Services/Taxonomy/classes/class.ilTaxonomyTree.php';
382 require_once 'Services/Taxonomy/classes/class.ilTaxNodeAssignment.php';
383
384 foreach($this->taxFilters as $taxId => $taxNodes)
385 {
386 $questionIds = array();
387
388 $forceBypass = true;
389
390 foreach($taxNodes as $taxNode)
391 {
392 $forceBypass = false;
393
394 $taxItemsByTaxParent = $this->getTaxItems(
395 $this->taxParentTypes[$taxId], $this->taxParentIds[$taxId],
396 $taxId, $taxNode
397 );
398
399 $taxItemsByParent = $this->getTaxItems(
400 $this->parentObjType, $this->parentObjId,
401 $taxId, $taxNode
402 );
403
404 $taxItems = array_merge($taxItemsByTaxParent, $taxItemsByParent);
405 foreach($taxItems as $taxItem)
406 {
407 $questionIds[$taxItem['item_id']] = $taxItem['item_id'];
408 }
409 }
410
411 if( !$forceBypass )
412 {
413 $expressions[] = $this->db->in('question_id', $questionIds, false, 'integer');
414 }
415 }
416
417 return $expressions;
418 }
419
427 protected function getTaxItems($parentType, $parentObjId, $taxId, $taxNode)
428 {
429 $taxTree = new ilTaxonomyTree($taxId);
430
431 $taxNodeAssignment = new ilTaxNodeAssignment(
432 $parentType, $parentObjId, 'quest', $taxId
433 );
434
435 $subNodes = $taxTree->getSubTreeIds($taxNode);
436 $subNodes[] = $taxNode;
437
438 return $taxNodeAssignment->getAssignmentsOfNode($subNodes);
439 }
440
442 {
443 switch( $this->getQuestionInstanceTypeFilter() )
444 {
446
447 return 'qpl_questions.original_id IS NULL';
448
450
451 return 'qpl_questions.original_id IS NOT NULL';
452 }
453
454 return null;
455 }
456
458 {
459 $expressions = array();
460
461 if( is_array($this->getIncludeQuestionIdsFilter()) )
462 {
463 $expressions[] = $this->db->in(
464 'qpl_questions.question_id', $this->getIncludeQuestionIdsFilter(), false, 'integer'
465 );
466 }
467
468 if( is_array($this->getExcludeQuestionIdsFilter()) )
469 {
470 $IN = $this->db->in(
471 'qpl_questions.question_id', $this->getExcludeQuestionIdsFilter(), true, 'integer'
472 );
473
474 if($IN == ' 1=2 ') { $IN = ' 1=1 '; } // required for ILIAS < 5.0
475
476 $expressions[] = $IN;
477 }
478
479 return $expressions;
480 }
481
483 {
484 if( $this->parentObjId )
485 {
486 return "qpl_questions.obj_fi = {$this->db->quote($this->parentObjId, 'integer')}";
487 }
488
489 return null;
490 }
491
493 {
494 $expressions = array();
495
496 switch( $this->getAnswerStatusFilter() )
497 {
499
500 $expressions[] = '
501 (tst_test_result.question_fi IS NULL OR tst_test_result.points < qpl_questions.points)
502 ';
503 break;
504
506
507 $expressions[] = 'tst_test_result.question_fi IS NULL';
508 break;
509
511
512 $expressions[] = 'tst_test_result.question_fi IS NOT NULL';
513 $expressions[] = 'tst_test_result.points < qpl_questions.points';
514 break;
515 }
516
517 return $expressions;
518 }
519
520 private function getTableJoinExpression()
521 {
522 $tableJoin = "
523 INNER JOIN qpl_qst_type
524 ON qpl_qst_type.question_type_id = qpl_questions.question_type_fi
525 ";
526
527 if ($this->join_obj_data)
528 {
529 $tableJoin .= "
530 INNER JOIN object_data
531 ON object_data.obj_id = qpl_questions.obj_fi
532 ";
533 }
534
535 if( $this->getAnswerStatusActiveId() )
536 {
537 $tableJoin .= "
538 LEFT JOIN tst_test_result
539 ON tst_test_result.question_fi = qpl_questions.question_id
540 AND tst_test_result.active_fi = {$this->db->quote($this->getAnswerStatusActiveId(), 'integer')}
541 ";
542 }
543
544 return $tableJoin;
545 }
546
548 {
549 $CONDITIONS = array();
550
551 if( $this->getQuestionInstanceTypeFilterExpression() !== null )
552 {
553 $CONDITIONS[] = $this->getQuestionInstanceTypeFilterExpression();
554 }
555
556 if( $this->getParentObjFilterExpression() !== null )
557 {
558 $CONDITIONS[] = $this->getParentObjFilterExpression();
559 }
560
561 if( $this->getParentObjectIdFilterExpression() !== null )
562 {
563 $CONDITIONS[] = $this->getParentObjectIdFilterExpression();
564 }
565
566 $CONDITIONS = array_merge($CONDITIONS,
571 );
572
573 $CONDITIONS = implode(' AND ', $CONDITIONS);
574
575 return strlen($CONDITIONS) ? 'AND '.$CONDITIONS : '';
576 }
577
578 private function getSelectFieldsExpression()
579 {
580 $selectFields = array(
581 'qpl_questions.*',
582 'qpl_qst_type.type_tag',
583 'qpl_qst_type.plugin',
584 'qpl_qst_type.plugin_name',
585 'qpl_questions.points max_points'
586 );
587
588 if ($this->join_obj_data)
589 {
590 $selectFields[] = 'object_data.title parent_title';
591 }
592
593 if( $this->getAnswerStatusActiveId() )
594 {
595 $selectFields[] = 'tst_test_result.points reached_points';
596 $selectFields[] = "CASE
597 WHEN tst_test_result.points IS NULL THEN '".self::QUESTION_ANSWER_STATUS_NON_ANSWERED."'
598 WHEN tst_test_result.points < qpl_questions.points THEN '".self::QUESTION_ANSWER_STATUS_WRONG_ANSWERED."'
599 ELSE '".self::QUESTION_ANSWER_STATUS_CORRECT_ANSWERED."'
600 END question_answer_status
601 ";
602 }
603
604 $selectFields = implode(",\n\t\t\t\t", $selectFields);
605
606 return "
607 SELECT {$selectFields}
608 ";
609 }
610
611 private function buildBasicQuery()
612 {
613 return "
614 {$this->getSelectFieldsExpression()}
615
616 FROM qpl_questions
617
618 {$this->getTableJoinExpression()}
619
620 WHERE qpl_questions.tstamp > 0
621 ";
622 }
623
624 private function buildQuery()
625 {
626 $query = $this->buildBasicQuery()."
627 {$this->getConditionalFilterExpression()}
628 ";
629
630 if( count($this->getForcedQuestionIds()) )
631 {
632 $query .= "
633 UNION {$this->buildBasicQuery()}
634 AND {$this->db->in('qpl_questions.question_id', $this->getForcedQuestionIds(), false, 'integer')}
635 ";
636 }
637
638 return $query;
639 }
640
641 public function load()
642 {
643 $this->checkFilters();
644
645 $query = $this->buildQuery();
646
647 #vd($query);
648
649 $res = $this->db->query($query);
650
651 //echo $this->db->db->last_query;
652
653 #vd($this->db->db->last_query);
654
655 while( $row = $this->db->fetchAssoc($res) )
656 {
658
659 if( !$this->isActiveQuestionType($row) )
660 {
661 continue;
662 }
663
664 $row['taxonomies'] = $this->loadTaxonomyAssignmentData($row['obj_fi'], $row['question_id']);
665
666 $row['ttype'] = $this->lng->txt($row['type_tag']);
667
668 $this->questions[ $row['question_id'] ] = $row;
669 }
670 }
671
672 private function loadTaxonomyAssignmentData($parentObjId, $questionId)
673 {
674 $taxAssignmentData = array();
675
676 foreach($this->getAvailableTaxonomyIds() as $taxId)
677 {
678 require_once 'Services/Taxonomy/classes/class.ilTaxonomyTree.php';
679 require_once 'Services/Taxonomy/classes/class.ilTaxNodeAssignment.php';
680
681 $taxTree = new ilTaxonomyTree($taxId);
682
683 $taxAssignment = new ilTaxNodeAssignment('qpl', $parentObjId, 'quest', $taxId);
684
685 $assignments = $taxAssignment->getAssignmentsOfItem($questionId);
686
687 foreach($assignments as $assData)
688 {
689 if( !isset($taxAssignmentData[ $assData['tax_id'] ]) )
690 {
691 $taxAssignmentData[ $assData['tax_id'] ] = array();
692 }
693
694 $nodeData = $taxTree->getNodeData($assData['node_id']);
695
696 $assData['node_lft'] = $nodeData['lft'];
697
698 $taxAssignmentData[ $assData['tax_id'] ][ $assData['node_id'] ] = $assData;
699 }
700 }
701
702 return $taxAssignmentData;
703 }
704
705 private function isActiveQuestionType($questionData)
706 {
707 if( !isset($questionData['plugin']) )
708 {
709 return false;
710 }
711
712 if( !$questionData['plugin'] )
713 {
714 return true;
715 }
716
717 return $this->pluginAdmin->isActive(IL_COMP_MODULE, 'TestQuestionPool', 'qst', $questionData['plugin_name']);
718 }
719
720 public function getDataArrayForQuestionId($questionId)
721 {
722 return $this->questions[$questionId];
723 }
724
725 public function getQuestionDataArray()
726 {
727 return $this->questions;
728 }
729
730 public function isInList($questionId)
731 {
732 return isset($this->questions[$questionId]);
733 }
734
744 public function getTitle($a_comp_id, $a_item_type, $a_item_id)
745 {
746 if( $a_comp_id != 'qpl' || $a_item_type != 'quest' || !(int)$a_item_id )
747 {
748 return '';
749 }
750
751 if( !isset($this->questions[$a_item_id]) )
752 {
753 return '';
754 }
755
756 return $this->questions[$a_item_id]['title'];
757 }
758
759 private function checkFilters()
760 {
761 if( strlen($this->getAnswerStatusFilter()) && !$this->getAnswerStatusActiveId() )
762 {
763 require_once 'Modules/TestQuestionPool/exceptions/class.ilTestQuestionPoolException.php';
764
766 'No active id given! You cannot use the answer status filter without giving an active id.'
767 );
768 }
769
770 }
771}
An exception for terminatinating execution or to throw for unit testing.
const IL_COMP_MODULE
setAnswerStatusActiveId($answerStatusActiveId)
const ANSWER_STATUS_FILTER_ALL_NON_CORRECT
answer status filter value domain
setExcludeQuestionIdsFilter($excludeQuestionIdsFilter)
getDataArrayForQuestionId($questionId)
setParentObjectType($parentObjType)
getTaxItems($parentType, $parentObjId, $taxId, $taxNode)
setIncludeQuestionIdsFilter($questionIdsFilter)
setQuestionCompletionStatusFilter($questionCompletionStatusFilter)
setForcedQuestionIds($forcedQuestionIds)
addTaxonomyFilter($taxId, $taxNodes, $parentObjId, $parentObjType)
isActiveQuestionType($questionData)
setAvailableTaxonomyIds($availableTaxonomyIds)
getJoinObjectData()
Get if object data table should be joined.
setAnswerStatusFilter($answerStatusFilter)
__construct(ilDBInterface $db, ilLanguage $lng, ilPluginAdmin $pluginAdmin)
Constructor.
setJoinObjectData($a_val)
Set if object data table should be joined.
setParentObjIdsFilter($parentObjIdsFilter)
loadTaxonomyAssignmentData($parentObjId, $questionId)
setQuestionInstanceTypeFilter($questionInstanceTypeFilter)
const QUESTION_ANSWER_STATUS_NON_ANSWERED
answer status domain for single questions
addFieldFilter($fieldName, $fieldValue)
getTitle($a_comp_id, $a_item_type, $a_item_id)
Get title of an assigned item.
static completeMissingPluginName($questionTypeData)
language handling
Administration class for plugins.
Taxonomy node <-> item assignment.
Interface ilDBInterface.
Interface for assigned items of taxonomies.