ILIAS  release_7 Revision v7.30-3-g800a261c036
All Data Structures Namespaces Files Functions Variables Modules Pages
class.SurveyQuestionEvaluation.php
Go to the documentation of this file.
1 <?php
2 
3 /* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
4 
11 {
15  protected $lng;
16 
20  protected $db;
21 
22  protected $question; // [SurveyQuestion]
23  protected $finished_ids; // [array]
24  protected $chart_width = 400;
25  protected $chart_height = 300;
26 
34  public function __construct(SurveyQuestion $a_question, array $a_finished_ids = null)
35  {
36  global $DIC;
37 
38  $this->lng = $DIC->language();
39  $this->db = $DIC->database();
40  $this->question = $a_question;
41  $this->finished_ids = $a_finished_ids;
42  }
43 
44 
45  //
46  // RESULTS
47  //
48 
54  public function getResults()
55  {
56  $results = new ilSurveyEvaluationResults($this->question);
57  $answers = $this->getAnswerData();
58 
59  $this->parseResults(
60  $results,
61  (array) $answers[0],
62  method_exists($this->question, "getCategories")
63  ? $this->question->getCategories()
64  : null
65  );
66 
67  return $results;
68  }
69 
75  public function getSumScores() : array
76  {
77  $ilDB = $this->db;
78 
79  $res = [];
80 
81  $sql = "SELECT svy_answer.* FROM svy_answer" .
82  " JOIN svy_finished ON (svy_finished.finished_id = svy_answer.active_fi)" .
83  " WHERE svy_answer.question_fi = " . $ilDB->quote($this->question->getId(), "integer") .
84  " AND svy_finished.survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer");
85  if (is_array($this->finished_ids)) {
86  $sql .= " AND " . $ilDB->in("svy_finished.finished_id", $this->finished_ids, "", "integer");
87  }
88  $set = $ilDB->query($sql);
89  $cnt_answer_records = [];
90  while ($row = $ilDB->fetchAssoc($set)) {
91  $cnt_answer_records[(int) $row["active_fi"]] += 1;
92  if ($this->supportsSumScore()) {
93  $res[(int) $row["active_fi"]] += $row["value"] + 1;
94  } else {
95  $res[(int) $row["active_fi"]] = 0;
96  }
97  }
98 
99  foreach ($res as $active_id => $sum_score) {
100  if (!$this->isSumScoreValid($cnt_answer_records[$active_id])) {
101  $res[$active_id] = null;
102  }
103  }
104  return $res;
105  }
106 
112  protected function isSumScoreValid(int $nr_answer_records) : bool
113  {
114  return true;
115  }
116 
121  protected function supportsSumScore() : bool
122  {
123  return false;
124  }
125 
133  protected function parseResults(ilSurveyEvaluationResults $a_results, array $a_answers, SurveyCategories $a_categories = null)
134  {
135  $num_users_answered = sizeof($a_answers);
136 
137  $a_results->setUsersAnswered($num_users_answered);
138  $a_results->setUsersSkipped($this->getNrOfParticipants() - $num_users_answered);
139 
140  // parse answers
141  $has_multi = false;
142  $selections = array();
143  foreach ($a_answers as $active_id => $answers) {
144  // :TODO:
145  if (sizeof($answers) > 1) {
146  $has_multi = true;
147  }
148  foreach ($answers as $answer) {
149  // map selection value to scale/category
150  if ($a_categories &&
151  $answer["value"] != "") {
152  $scale = $a_categories->getCategoryForScale($answer["value"] + 1);
153  if ($scale instanceof ilSurveyCategory) {
154  $answer["value"] = $scale->scale;
155  }
156  }
157 
158  $parsed = new ilSurveyEvaluationResultsAnswer(
159  $active_id,
160  $answer["value"],
161  $answer["text"]
162  );
163  $a_results->addAnswer($parsed);
164 
165  if ($answer["value"] != "") {
166  $selections[$answer["value"]]++;
167  }
168  }
169  }
170 
171  $total = array_sum($selections);
172 
173  if ($total) {
174  // mode
175  $mode_nr = max($selections);
176  $tmp_mode = $selections;
177  asort($tmp_mode, SORT_NUMERIC);
178  $mode = array_keys($tmp_mode, $mode_nr);
179  $a_results->setMode($mode, $mode_nr);
180 
181  if (!$has_multi) {
182  // median
183  ksort($selections, SORT_NUMERIC);
184  $median = array();
185  foreach ($selections as $value => $count) {
186  for ($i = 0; $i < $count; $i++) {
187  $median[] = $value;
188  }
189  }
190  if ($total % 2 == 0) {
191  $lower = $median[($total / 2) - 1];
192  $upper = $median[($total / 2)];
193  $median_value = 0.5 * ($lower + $upper);
194  if ($a_categories &&
195  round($median_value) != $median_value) {
196  // mapping calculated value to scale values
197  $median_value = array($lower, $upper);
198  }
199  } else {
200  $median_value = $median[(($total + 1) / 2) - 1];
201  }
202  $a_results->setMedian($median_value);
203  }
204  }
205 
206  if ($a_categories) {
207  // selections by category
208  for ($c = 0; $c < $a_categories->getCategoryCount(); $c++) {
209  $cat = $a_categories->getCategory($c);
210  $scale = $cat->scale;
211 
213  $cat,
214  $selections[$scale],
215  $total
216  ? $selections[$scale] / $total
217  : null
218  );
219  $a_results->addVariable($var);
220  }
221  }
222  }
223 
224  public function parseUserSpecificResults($a_qres, $a_user_id)
225  {
226  $parsed_results = array();
227 
228  if (is_array($a_qres)) {
229  foreach ($a_qres as $row_idx => $row_results) {
230  $row_title = $row_results[0];
231  $user_results = $row_results[1]->getUserResults($a_user_id);
232  if ($user_results) {
233  foreach ($user_results as $item) {
234  // :TODO: layout
235  $tmp = $row_title . ": ";
236  if ($item[0] !== "") {
237  $tmp .= $item[0];
238  }
239  if ($item[1] && $item[0]) {
240  $tmp .= ", \"" . nl2br($item[1]) . "\"";
241  } elseif ($item[1]) {
242  $tmp .= "\"" . nl2br($item[1]) . "\"";
243  }
244  $parsed_results[$row_idx . "-" . $item[2]] = $tmp;
245  }
246  }
247  }
248  } else {
249  $user_results = $a_qres->getUserResults($a_user_id);
250  if ($user_results) {
251  foreach ($user_results as $item) {
252  // :TODO: layout
253  if ($item[0] !== "") {
254  $tmp = $item[0];
255  }
256  if ($item[1] && $item[0]) {
257  $tmp .= ", \"" . nl2br($item[1]) . "\"";
258  } elseif ($item[1]) {
259  $tmp = "\"" . nl2br($item[1]) . "\"";
260  }
261  $parsed_results[$item[2]] = $tmp;
262  }
263  }
264  }
265 
266  return $parsed_results;
267  }
268 
269 
270  //
271  // DETAILS
272  //
273 
282  public function getGrid($a_results, $a_abs = true, $a_perc = true)
283  {
284  $lng = $this->lng;
285 
286  if ((bool) $a_abs && (bool) $a_perc) {
287  $cols = array(
288  $lng->txt("category_nr_selected"),
289  $lng->txt("svy_fraction_of_selections")
290  );
291  } elseif ((bool) $a_abs) {
292  $cols = array(
293  $lng->txt("category_nr_selected")
294  );
295  } else {
296  $cols = array(
297  $lng->txt("svy_fraction_of_selections")
298  );
299  }
300 
301  $res = array(
302  "cols" => $cols,
303  "rows" => array()
304  );
305 
306  $vars = $a_results->getVariables();
307  if ($vars) {
308  foreach ($vars as $var) {
309  $perc = $var->perc
310  ? sprintf("%.2f", $var->perc * 100) . "%"
311  : "0%";
312 
313  if ((bool) $a_abs && (bool) $a_perc) {
314  $res["rows"][] = array(
315  $var->cat->title,
316  $var->abs,
317  $perc
318  );
319  } elseif ((bool) $a_abs) {
320  $res["rows"][] = array(
321  $var->cat->title,
322  $var->abs
323  );
324  } else {
325  $res["rows"][] = array(
326  $var->cat->title,
327  $perc
328  );
329  }
330  }
331  }
332 
333  return $res;
334  }
335 
342  public function getTextAnswers($a_results)
343  {
344  return $a_results->getMappedTextAnswers();
345  }
346 
347  protected function getChartColors()
348  {
349  return array(
350  // flot "default" theme
351  "#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed",
352  // http://godsnotwheregodsnot.blogspot.de/2012/09/color-distribution-methodology.html
353  "#1CE6FF", "#FF34FF", "#FF4A46", "#008941", "#006FA6", "#A30059",
354  "#FFDBE5", "#7A4900", "#0000A6", "#63FFAC", "#B79762", "#004D43", "#8FB0FF", "#997D87",
355  "#5A0007", "#809693", "#FEFFE6", "#1B4400", "#4FC601", "#3B5DFF", "#4A3B53", "#FF2F80",
356  "#61615A", "#BA0900", "#6B7900", "#00C2A0", "#FFAA92", "#FF90C9", "#B903AA", "#D16100",
357  "#DDEFFF", "#000035", "#7B4F4B", "#A1C299", "#300018", "#0AA6D8", "#013349", "#00846F",
358  "#372101", "#FFB500", "#C2FFED", "#A079BF", "#CC0744", "#C0B9B2", "#C2FF99", "#001E09",
359  "#00489C", "#6F0062", "#0CBD66", "#EEC3FF", "#456D75", "#B77B68", "#7A87A1", "#788D66",
360  "#885578", "#FAD09F", "#FF8A9A", "#D157A0", "#BEC459", "#456648", "#0086ED", "#886F4C",
361  "#34362D", "#B4A8BD", "#00A6AA", "#452C2C", "#636375", "#A3C8C9", "#FF913F", "#938A81",
362  "#575329", "#00FECF", "#B05B6F", "#8CD0FF", "#3B9700", "#04F757", "#C8A1A1", "#1E6E00",
363  "#7900D7", "#A77500", "#6367A9", "#A05837", "#6B002C", "#772600", "#D790FF", "#9B9700",
364  "#549E79", "#FFF69F", "#201625", "#72418F", "#BC23FF", "#99ADC0", "#3A2465", "#922329",
365  "#5B4534", "#FDE8DC", "#404E55", "#0089A3", "#CB7E98", "#A4E804", "#324E72", "#6A3A4C"
366  );
367  }
368 
375  public function getChart($a_results)
376  {
377  $chart = ilChart::getInstanceByType(ilChart::TYPE_GRID, $a_results->getQuestion()->getId());
378  $chart->setYAxisToInteger(true);
379 
380  $colors = $this->getChartColors();
381  $chart->setColors($colors);
382 
383  // :TODO:
384  $chart->setsize($this->chart_width, $this->chart_height);
385 
386  $vars = $a_results->getVariables();
387 
388  $legend = $labels = array();
389  foreach ($vars as $idx => $var) {
390  $data = $chart->getDataInstance(ilChartGrid::DATA_BARS);
391  $data->setBarOptions(0.5, "center");
392  $data->setFill(1);
393  $chart->addData($data);
394 
395  // labels
396  $labels[$idx] = "";
397  $legend[] = array(
398  $var->cat->title,
399  $colors[$idx]
400  );
401  $data->setLabel($var->cat->title);
402 
403  $data->addPoint($idx, $var->abs);
404  }
405 
406  $chart->setTicks($labels, false, true);
407 
408  return array(
409  $chart->getHTML(),
410  $legend
411  );
412  }
413 
414 
415  //
416  // USER-SPECIFIC
417  //
418 
424  public function getSkippedValue()
425  {
427  }
428 
429 
430  //
431  // HELPER
432  //
433 
434  protected function getSurveyId()
435  {
436  $ilDB = $this->db;
437 
438  // #18968
439  $set = $ilDB->query("SELECT survey_fi" .
440  " FROM svy_svy_qst" .
441  " WHERE question_fi = " . $ilDB->quote($this->question->getId(), "integer"));
442  $row = $ilDB->fetchAssoc($set);
443  return $row["survey_fi"];
444  }
445 
446 
452  protected function getNrOfParticipants()
453  {
454  $ilDB = $this->db;
455 
456  if (is_array($this->finished_ids)) {
457  return sizeof($this->finished_ids);
458  }
459 
460  $set = $ilDB->query("SELECT finished_id FROM svy_finished" .
461  " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer"));
462  return $set->numRows();
463  }
464 
465  protected function getAnswerData()
466  {
467  $ilDB = $this->db;
468 
469  $res = array();
470 
471  $sql = "SELECT svy_answer.* FROM svy_answer" .
472  " JOIN svy_finished ON (svy_finished.finished_id = svy_answer.active_fi)" .
473  " WHERE svy_answer.question_fi = " . $ilDB->quote($this->question->getId(), "integer") .
474  " AND svy_finished.survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer");
475  if (is_array($this->finished_ids)) {
476  $sql .= " AND " . $ilDB->in("svy_finished.finished_id", $this->finished_ids, "", "integer");
477  }
478  $set = $ilDB->query($sql);
479  while ($row = $ilDB->fetchAssoc($set)) {
480  $res[(int) $row["rowvalue"]][(int) $row["active_fi"]][] = array(
481  "value" => $row["value"],
482  "text" => $row["textanswer"]
483  );
484  }
485 
486  return $res;
487  }
488 
489 
490  //
491  // EXPORT
492  //
493 
494  public function exportResults($a_results, $a_do_title, $a_do_label)
495  {
496  $question = $a_results->getQuestion();
497 
498  $res = array();
499 
500  if ($a_do_title) {
501  $res[] = $question->getTitle();
502  }
503  if ($a_do_label) {
504  $res[] = $question->label;
505  }
506 
507  $res[] = $question->getQuestiontext();
508  $res[] = SurveyQuestion::_getQuestionTypeName($question->getQuestionType());
509 
510  $res[] = (int) $a_results->getUsersAnswered();
511  $res[] = (int) $a_results->getUsersSkipped();
512 
513  // :TODO:
514  $res[] = is_array($a_results->getModeValue())
515  ? implode(", ", $a_results->getModeValue())
516  : $a_results->getModeValue();
517 
518  $res[] = $a_results->getModeValueAsText();
519  $res[] = (int) $a_results->getModeNrOfSelections();
520 
521  // :TODO:
522  $res[] = $a_results->getMedianAsText();
523 
524  $res[] = $a_results->getMean();
525 
526  return array($res);
527  }
528 
535  public function getExportGrid($a_results)
536  {
537  $lng = $this->lng;
538 
539  $res = array(
540  "cols" => array(
541  $lng->txt("title"),
542  $lng->txt("value"),
543  $lng->txt("category_nr_selected"),
544  $lng->txt("svy_fraction_of_selections")
545  ),
546  "rows" => array()
547  );
548 
549  $vars = $a_results->getVariables();
550  if ($vars) {
551  foreach ($vars as $var) {
552  $res["rows"][] = array(
553  $var->cat->title,
554  $var->cat->scale,
555  $var->abs,
556  $var->perc
557  ? sprintf("%.2f", $var->perc * 100) . "%"
558  : "0%"
559  );
560  }
561  }
562 
563  return $res;
564  }
565 
574  public function getUserSpecificVariableTitles(array &$a_title_row, array &$a_title_row2, $a_do_title, $a_do_label)
575  {
576  // type-specific
577  }
578 
586  abstract public function addUserSpecificResults(array &$a_row, $a_user_id, $a_results);
587 }
getExportGrid($a_results)
Get grid data.
$data
Definition: storeScorm.php:23
$c
Definition: cli.php:37
static getSurveySkippedValue()
exportResults($a_results, $a_do_title, $a_do_label)
static _getQuestionTypeName($type_tag)
Return the translation for a given question type tag.
getUserSpecificVariableTitles(array &$a_title_row, array &$a_title_row2, $a_do_title, $a_do_label)
Get title columns for user-specific export.
$total
Definition: Utf8Test.php:87
getNrOfParticipants()
Returns the number of participants for a survey.
getGrid($a_results, $a_abs=true, $a_perc=true)
Get grid data.
Class SurveyCategories.
foreach($_POST as $key=> $value) $res
Basic class for all survey question types.
global $DIC
Definition: goto.php:24
addVariable(ilSurveyEvaluationResultsVariable $a_variable)
setMode($a_value, $a_nr_of_selections)
$results
const TYPE_GRID
parseResults(ilSurveyEvaluationResults $a_results, array $a_answers, SurveyCategories $a_categories=null)
Parse answer data into results instance.
__construct(SurveyQuestion $a_question, array $a_finished_ids=null)
Constructor.
getSkippedValue()
Get caption for skipped value.
global $ilDB
addUserSpecificResults(array &$a_row, $a_user_id, $a_results)
isSumScoreValid(int $nr_answer_records)
Is sum score ok (question needs to be fully answered)
addAnswer(ilSurveyEvaluationResultsAnswer $a_answer)
Survey category class.
$cols
Definition: xhr_table.php:11
static getInstanceByType($a_type, $a_id)
Get type instance.
getSumScores()
Get sum score for this question for all active ids of run.
$i
Definition: metadata.php:24
getTextAnswers($a_results)
Get text answers.