ILIAS  release_6 Revision v6.24-5-g0c8bfefb3b8
All Data Structures Namespaces Files Functions Variables Modules Pages
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 
4 require_once 'Services/Taxonomy/interfaces/interface.ilTaxAssignedItemInfo.php';
5 require_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 
117  const QUESTION_ANSWER_STATUS_NON_ANSWERED = 'nonAnswered';
118  const QUESTION_ANSWER_STATUS_WRONG_ANSWERED = 'wrongAnswered';
119  const QUESTION_ANSWER_STATUS_CORRECT_ANSWERED = 'correctAnswered';
120 
124  const ANSWER_STATUS_FILTER_ALL_NON_CORRECT = 'allNonCorrect';
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';
137  private $questionInstanceTypeFilter = self::QUESTION_INSTANCE_TYPE_ORIGINALS;
138 
141 
144  const QUESTION_COMPLETION_STATUS_BOTH = 'complete/incomplete';
145  private $questionCompletionStatusFilter = self::QUESTION_COMPLETION_STATUS_BOTH;
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 
173  public function setParentObjId($parentObjId)
174  {
175  $this->parentObjId = $parentObjId;
176  }
177 
178  public function getParentObjectType()
179  {
180  return $this->parentObjType;
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 
219  public function getIncludeQuestionIdsFilter()
220  {
222  }
223 
224  public function getExcludeQuestionIdsFilter()
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  {
303  return $this->join_obj_data;
304  }
305 
310  {
311  $this->forcedQuestionIds = $forcedQuestionIds;
312  }
313 
317  public function getForcedQuestionIds()
318  {
320  }
321 
322  private function getParentObjFilterExpression()
323  {
324  if ($this->getParentObjId()) {
325  return 'qpl_questions.obj_fi = ' . $this->db->quote($this->getParentObjId(), 'integer');
326  }
327 
328  if (count($this->getParentObjIdsFilter())) {
329  return $this->db->in('qpl_questions.obj_fi', $this->getParentObjIdsFilter(), false, 'integer');
330  }
331 
332  return null;
333  }
334 
335  private function getFieldFilterExpressions()
336  {
337  $expressions = array();
338 
339  foreach ($this->fieldFilters as $fieldName => $fieldValue) {
340  switch ($fieldName) {
341  case 'title':
342  case 'description':
343  case 'author':
344  case 'lifecycle':
345 
346  $expressions[] = $this->db->like('qpl_questions.' . $fieldName, 'text', "%%$fieldValue%%");
347  break;
348 
349  case 'type':
350 
351  $expressions[] = "qpl_qst_type.type_tag = {$this->db->quote($fieldValue, 'text')}";
352  break;
353 
354  case 'question_id':
355  if ($fieldValue != "" && !is_array($fieldValue)) {
356  $fieldValue = array($fieldValue);
357  }
358  $expressions[] = $this->db->in("qpl_questions.question_id", $fieldValue, false, "integer");
359  break;
360 
361  case 'parent_title':
362  if ($this->join_obj_data) {
363  $expressions[] = $this->db->like('object_data.title', 'text', "%%$fieldValue%%");
364  }
365  break;
366  }
367  }
368 
369  return $expressions;
370  }
371 
372  private function getTaxonomyFilterExpressions()
373  {
374  $expressions = array();
375 
376  require_once 'Services/Taxonomy/classes/class.ilTaxonomyTree.php';
377  require_once 'Services/Taxonomy/classes/class.ilTaxNodeAssignment.php';
378 
379  foreach ($this->taxFilters as $taxId => $taxNodes) {
380  $questionIds = array();
381 
382  $forceBypass = true;
383 
384  foreach ($taxNodes as $taxNode) {
385  $forceBypass = false;
386 
387  $taxItemsByTaxParent = $this->getTaxItems(
388  $this->taxParentTypes[$taxId],
389  $this->taxParentIds[$taxId],
390  $taxId,
391  $taxNode
392  );
393 
394  $taxItemsByParent = $this->getTaxItems(
395  $this->parentObjType,
396  $this->parentObjId,
397  $taxId,
398  $taxNode
399  );
400 
401  $taxItems = array_merge($taxItemsByTaxParent, $taxItemsByParent);
402  foreach ($taxItems as $taxItem) {
403  $questionIds[$taxItem['item_id']] = $taxItem['item_id'];
404  }
405  }
406 
407  if (!$forceBypass) {
408  $expressions[] = $this->db->in('question_id', $questionIds, false, 'integer');
409  }
410  }
411 
412  return $expressions;
413  }
414 
422  protected function getTaxItems($parentType, $parentObjId, $taxId, $taxNode)
423  {
424  $taxTree = new ilTaxonomyTree($taxId);
425 
426  $taxNodeAssignment = new ilTaxNodeAssignment(
427  $parentType,
428  $parentObjId,
429  'quest',
430  $taxId
431  );
432 
433  $subNodes = $taxTree->getSubTreeIds($taxNode);
434  $subNodes[] = $taxNode;
435 
436  return $taxNodeAssignment->getAssignmentsOfNode($subNodes);
437  }
438 
440  {
441  switch ($this->getQuestionInstanceTypeFilter()) {
442  case self::QUESTION_INSTANCE_TYPE_ORIGINALS:
443 
444  return 'qpl_questions.original_id IS NULL';
445 
446  case self::QUESTION_INSTANCE_TYPE_DUPLICATES:
447 
448  return 'qpl_questions.original_id IS NOT NULL';
449  }
450 
451  return null;
452  }
453 
455  {
456  $expressions = array();
457 
458  if (is_array($this->getIncludeQuestionIdsFilter())) {
459  $expressions[] = $this->db->in(
460  'qpl_questions.question_id',
462  false,
463  'integer'
464  );
465  }
466 
467  if (is_array($this->getExcludeQuestionIdsFilter())) {
468  $IN = $this->db->in(
469  'qpl_questions.question_id',
471  true,
472  'integer'
473  );
474 
475  if ($IN == ' 1=2 ') {
476  $IN = ' 1=1 ';
477  } // required for ILIAS < 5.0
478 
479  $expressions[] = $IN;
480  }
481 
482  return $expressions;
483  }
484 
486  {
487  if ($this->parentObjId) {
488  return "qpl_questions.obj_fi = {$this->db->quote($this->parentObjId, 'integer')}";
489  }
490 
491  return null;
492  }
493 
495  {
496  $expressions = array();
497 
498  switch ($this->getAnswerStatusFilter()) {
499  case self::ANSWER_STATUS_FILTER_ALL_NON_CORRECT:
500 
501  $expressions[] = '
502  (tst_test_result.question_fi IS NULL OR tst_test_result.points < qpl_questions.points)
503  ';
504  break;
505 
506  case self::ANSWER_STATUS_FILTER_NON_ANSWERED_ONLY:
507 
508  $expressions[] = 'tst_test_result.question_fi IS NULL';
509  break;
510 
511  case self::ANSWER_STATUS_FILTER_WRONG_ANSWERED_ONLY:
512 
513  $expressions[] = 'tst_test_result.question_fi IS NOT NULL';
514  $expressions[] = 'tst_test_result.points < qpl_questions.points';
515  break;
516  }
517 
518  return $expressions;
519  }
520 
521  private function getTableJoinExpression()
522  {
523  $tableJoin = "
524  INNER JOIN qpl_qst_type
525  ON qpl_qst_type.question_type_id = qpl_questions.question_type_fi
526  ";
527 
528  if ($this->join_obj_data) {
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  $tableJoin .= "
537  LEFT JOIN tst_test_result
538  ON tst_test_result.question_fi = qpl_questions.question_id
539  AND tst_test_result.active_fi = {$this->db->quote($this->getAnswerStatusActiveId(), 'integer')}
540  ";
541  }
542 
543  return $tableJoin;
544  }
545 
547  {
548  $CONDITIONS = array();
549 
550  if ($this->getQuestionInstanceTypeFilterExpression() !== null) {
551  $CONDITIONS[] = $this->getQuestionInstanceTypeFilterExpression();
552  }
553 
554  if ($this->getParentObjFilterExpression() !== null) {
555  $CONDITIONS[] = $this->getParentObjFilterExpression();
556  }
557 
558  if ($this->getParentObjectIdFilterExpression() !== null) {
559  $CONDITIONS[] = $this->getParentObjectIdFilterExpression();
560  }
561 
562  $CONDITIONS = array_merge(
563  $CONDITIONS,
565  $this->getFieldFilterExpressions(),
568  );
569 
570  $CONDITIONS = implode(' AND ', $CONDITIONS);
571 
572  return strlen($CONDITIONS) ? 'AND ' . $CONDITIONS : '';
573  }
574 
575  private function getSelectFieldsExpression()
576  {
577  $selectFields = array(
578  'qpl_questions.*',
579  'qpl_qst_type.type_tag',
580  'qpl_qst_type.plugin',
581  'qpl_qst_type.plugin_name',
582  'qpl_questions.points max_points'
583  );
584 
585  if ($this->join_obj_data) {
586  $selectFields[] = 'object_data.title parent_title';
587  }
588 
589  if ($this->getAnswerStatusActiveId()) {
590  $selectFields[] = 'tst_test_result.points reached_points';
591  $selectFields[] = "CASE
592  WHEN tst_test_result.points IS NULL THEN '" . self::QUESTION_ANSWER_STATUS_NON_ANSWERED . "'
593  WHEN tst_test_result.points < qpl_questions.points THEN '" . self::QUESTION_ANSWER_STATUS_WRONG_ANSWERED . "'
594  ELSE '" . self::QUESTION_ANSWER_STATUS_CORRECT_ANSWERED . "'
595  END question_answer_status
596  ";
597  }
598 
599  $selectFields = implode(",\n\t\t\t\t", $selectFields);
600 
601  return "
602  SELECT {$selectFields}
603  ";
604  }
605 
606  private function buildBasicQuery()
607  {
608  return "
609  {$this->getSelectFieldsExpression()}
610 
611  FROM qpl_questions
612 
613  {$this->getTableJoinExpression()}
614 
615  WHERE qpl_questions.tstamp > 0
616  ";
617  }
618 
619  private function buildQuery()
620  {
621  $query = $this->buildBasicQuery() . "
622  {$this->getConditionalFilterExpression()}
623  ";
624 
625  if (count($this->getForcedQuestionIds())) {
626  $query .= "
627  UNION {$this->buildBasicQuery()}
628  AND {$this->db->in('qpl_questions.question_id', $this->getForcedQuestionIds(), false, 'integer')}
629  ";
630  }
631 
632  return $query;
633  }
634 
635  public function load()
636  {
637  $this->checkFilters();
638 
639  $query = $this->buildQuery();
640 
641  #vd($query);
642 
643  $res = $this->db->query($query);
644 
645  //echo $this->db->db->last_query;
646 
647  #vd($this->db->db->last_query);
648 
649  while ($row = $this->db->fetchAssoc($res)) {
651 
652  if (!$this->isActiveQuestionType($row)) {
653  continue;
654  }
655 
656  $row['taxonomies'] = $this->loadTaxonomyAssignmentData($row['obj_fi'], $row['question_id']);
657 
658  $row['ttype'] = $this->lng->txt($row['type_tag']);
659 
660  $this->questions[ $row['question_id'] ] = $row;
661  }
662  }
663 
664  private function loadTaxonomyAssignmentData($parentObjId, $questionId)
665  {
666  $taxAssignmentData = array();
667 
668  foreach ($this->getAvailableTaxonomyIds() as $taxId) {
669  require_once 'Services/Taxonomy/classes/class.ilTaxonomyTree.php';
670  require_once 'Services/Taxonomy/classes/class.ilTaxNodeAssignment.php';
671 
672  $taxTree = new ilTaxonomyTree($taxId);
673 
674  $taxAssignment = new ilTaxNodeAssignment('qpl', $parentObjId, 'quest', $taxId);
675 
676  $assignments = $taxAssignment->getAssignmentsOfItem($questionId);
677 
678  foreach ($assignments as $assData) {
679  if (!isset($taxAssignmentData[ $assData['tax_id'] ])) {
680  $taxAssignmentData[ $assData['tax_id'] ] = array();
681  }
682 
683  $nodeData = $taxTree->getNodeData($assData['node_id']);
684 
685  $assData['node_lft'] = $nodeData['lft'];
686 
687  $taxAssignmentData[ $assData['tax_id'] ][ $assData['node_id'] ] = $assData;
688  }
689  }
690 
691  return $taxAssignmentData;
692  }
693 
694  private function isActiveQuestionType($questionData)
695  {
696  if (!isset($questionData['plugin'])) {
697  return false;
698  }
699 
700  if (!$questionData['plugin']) {
701  return true;
702  }
703 
704  return $this->pluginAdmin->isActive(IL_COMP_MODULE, 'TestQuestionPool', 'qst', $questionData['plugin_name']);
705  }
706 
707  public function getDataArrayForQuestionId($questionId)
708  {
709  return $this->questions[$questionId];
710  }
711 
712  public function getQuestionDataArray()
713  {
714  return $this->questions;
715  }
716 
717  public function isInList($questionId)
718  {
719  return isset($this->questions[$questionId]);
720  }
721 
731  public function getTitle($a_comp_id, $a_item_type, $a_item_id)
732  {
733  if ($a_comp_id != 'qpl' || $a_item_type != 'quest' || !(int) $a_item_id) {
734  return '';
735  }
736 
737  if (!isset($this->questions[$a_item_id])) {
738  return '';
739  }
740 
741  return $this->questions[$a_item_id]['title'];
742  }
743 
744  private function checkFilters()
745  {
746  if (strlen($this->getAnswerStatusFilter()) && !$this->getAnswerStatusActiveId()) {
747  require_once 'Modules/TestQuestionPool/exceptions/class.ilTestQuestionPoolException.php';
748 
749  throw new ilTestQuestionPoolException(
750  'No active id given! You cannot use the answer status filter without giving an active id.'
751  );
752  }
753  }
754 }
setAnswerStatusFilter($answerStatusFilter)
Taxonomy node <-> item assignment.
getTaxItems($parentType, $parentObjId, $taxId, $taxNode)
__construct(ilDBInterface $db, ilLanguage $lng, ilPluginAdmin $pluginAdmin)
Constructor.
setExcludeQuestionIdsFilter($excludeQuestionIdsFilter)
getDataArrayForQuestionId($questionId)
isActiveQuestionType($questionData)
setAnswerStatusActiveId($answerStatusActiveId)
Administration class for plugins.
Interface ilDBInterface.
static completeMissingPluginName($questionTypeData)
foreach($_POST as $key=> $value) $res
setIncludeQuestionIdsFilter($questionIdsFilter)
setParentObjectType($parentObjType)
const IL_COMP_MODULE
addTaxonomyFilter($taxId, $taxNodes, $parentObjId, $parentObjType)
$query
getJoinObjectData()
Get if object data table should be joined.
setQuestionInstanceTypeFilter($questionInstanceTypeFilter)
const ANSWER_STATUS_FILTER_ALL_NON_CORRECT
answer status filter value domain
Interface for assigned items of taxonomies.
setForcedQuestionIds($forcedQuestionIds)
setJoinObjectData($a_val)
Set if object data table should be joined.
getTitle($a_comp_id, $a_item_type, $a_item_id)
Get title of an assigned item.
addFieldFilter($fieldName, $fieldValue)
setParentObjIdsFilter($parentObjIdsFilter)
language handling
loadTaxonomyAssignmentData($parentObjId, $questionId)
const QUESTION_ANSWER_STATUS_NON_ANSWERED
answer status domain for single questions
setQuestionCompletionStatusFilter($questionCompletionStatusFilter)
setAvailableTaxonomyIds($availableTaxonomyIds)