ILIAS  eassessment Revision 61809
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.ilObjSCORMTracking.php
Go to the documentation of this file.
1 <?php
2 
3 /* Copyright (c) 1998-2011 ILIAS open source, Extended GPL, see docs/LICENSE */
4 
13 {
18  function ilObjSCORMTracking()
19  {
20  global $ilias;
21 
22  }
23 
24  function extractData()
25  {
26  $this->insert = array();
27  if (is_array($_GET["iL"]))
28  {
29  foreach($_GET["iL"] as $key => $value)
30  {
31  $this->insert[] = array("left" => $value, "right" => $_GET["iR"][$key]);
32  }
33  }
34  if (is_array($_POST["iL"]))
35  {
36  foreach($_POST["iL"] as $key => $value)
37  {
38  $this->insert[] = array("left" => $value, "right" => $_POST["iR"][$key]);
39  }
40  }
41 
42  $this->update = array();
43  if (is_array($_GET["uL"]))
44  {
45  foreach($_GET["uL"] as $key => $value)
46  {
47  $this->update[] = array("left" => $value, "right" => $_GET["uR"][$key]);
48  }
49  }
50  if (is_array($_POST["uL"]))
51  {
52  foreach($_POST["uL"] as $key => $value)
53  {
54  $this->update[] = array("left" => $value, "right" => $_POST["uR"][$key]);
55  }
56  }
57  }
58 
59  function store($obj_id=0, $sahs_id=0, $extractData=1)
60  {
61  global $ilDB, $ilUser;
62 
63  $ref_id = $_GET["ref_id"];
64  if (empty($obj_id))
65  {
66  $obj_id = ilObject::_lookupObjId($_GET["ref_id"]);
67  }
68 
69  // writing to scorm test log
70  $f = fopen("./Modules/ScormAicc/log/scorm.log", "a");
71  fwrite($f, "\nCALLING SCORM store()\n");
72  fwrite($f,'POST: '.print_r($_POST,true));
73 
74 
75  if (empty($sahs_id))
76  $sahs_id = ($_GET["sahs_id"] != "") ? $_GET["sahs_id"] : $_POST["sahs_id"];
77 
78  if ($extractData==1)
79  $this->extractData();
80 
81  if (is_object($ilUser))
82  {
83  $user_id = $ilUser->getId();
84  }
85 
86 
87 
88  if ($obj_id <= 1)
89  {
90  fwrite($f, "Error: No obj_id given.\n");
91  }
92  else
93  {
94  foreach($this->insert as $insert)
95  {
96  $set = $ilDB->queryF('
97  SELECT * FROM scorm_tracking
98  WHERE user_id = %s
99  AND sco_id = %s
100  AND lvalue = %s
101  AND obj_id = %s',
102  array('integer','integer','text','integer'),
103  array($user_id,$sahs_id,$insert["left"],$obj_id));
104  if ($rec = $ilDB->fetchAssoc($set))
105  {
106  fwrite($f, "Error Insert, left value already exists. L:".$insert["left"].",R:".
107  $insert["right"].",sahs_id:".$sahs_id.",user_id:".$user_id."\n");
108  }
109  else
110  {
111  $ilDB->insert('scorm_tracking', array(
112  'obj_id' => array('integer', $obj_id),
113  'user_id' => array('integer', $user_id),
114  'sco_id' => array('integer', $sahs_id),
115  'lvalue' => array('text', $insert["left"]),
116  'rvalue' => array('clob', $insert["right"]),
117  'c_timestamp' => array('timestamp', ilUtil::now())
118  ));
119 
120  fwrite($f, "Insert - L:".$insert["left"].",R:".
121  $insert["right"].",sahs_id:".$sahs_id.",user_id:".$user_id."\n");
122  }
123  }
124  foreach($this->update as $update)
125  {
126  $set = $ilDB->queryF('
127  SELECT * FROM scorm_tracking
128  WHERE user_id = %s
129  AND sco_id = %s
130  AND lvalue = %s
131  AND obj_id = %s',
132  array('integer','integer','text','integer'),
133  array($user_id,$sahs_id,$update["left"],$obj_id));
134 
135  if ($rec = $ilDB->fetchAssoc($set))
136  {
137  $ilDB->update('scorm_tracking',
138  array(
139  'rvalue' => array('clob', $update["right"]),
140  'c_timestamp' => array('timestamp', ilUtil::now())
141  ),
142  array(
143  'user_id' => array('integer', $user_id),
144  'sco_id' => array('integer', $sahs_id),
145  'lvalue' => array('text', $update["left"]),
146  'obj_id' => array('integer', $obj_id)
147  )
148  );
149  }
150  else
151  {
152  fwrite($f, "ERROR Update, left value does not exist. L:".$update["left"].",R:".
153  $update["right"].",sahs_id:".$sahs_id.",user_id:".$user_id."\n");
154  }
155 
156  }
157  }
158  fclose($f);
159 
160  // update status
161  include_once("./Services/Tracking/classes/class.ilLPStatusWrapper.php");
162  ilLPStatusWrapper::_updateStatus($obj_id, $user_id);
163 
164  // update time and numbers of attempts in change event
165  ilObjSCORMTracking::_syncReadEvent($obj_id, $user_id, "sahs", $ref_id);
166  }
167 
168  function storeJsApi($obj_id=0) {
169  global $ilLog, $ilDB, $ilUser;
170 
171  $b_updateStatus=false;
172 
173  $b_messageLog=false;
174  if ($ilLog->current_log_level == 30)
175  $b_messageLog=true;
176 
177  $ref_id = $_GET["ref_id"];
178 
179  if (empty($obj_id))
180  $obj_id = ilObject::_lookupObjId($_GET["ref_id"]);
181 
182  if ($b_messageLog)
183  $ilLog->write("ScormAicc: CALLING SCORM storeJsApi() ".$_POST);
184 
185  if (is_object($ilUser))
186  $user_id = $ilUser->getId();
187 
188  $aa_data = array();
189  if (is_array($_POST["S"])) {
190  foreach($_POST["S"] as $key => $value) {
191  $aa_data[] = array("sco_id" => $value, "left" => $_POST["L"][$key], "right" => rawurldecode($_POST["R"][$key]));
192  }
193  }
194 
195  if ($obj_id <= 1) {
196  $ilLog->write("ScormAicc: storeJsApi: Error: No valid obj_id given.");
197  }
198  else {
199  foreach($aa_data as $a_data) {
200  $set = $ilDB->queryF('
201  SELECT rvalue FROM scorm_tracking
202  WHERE user_id = %s
203  AND sco_id = %s
204  AND lvalue = %s
205  AND obj_id = %s',
206  array('integer','integer','text','integer'),
207  array($user_id,$a_data["sco_id"],$a_data["left"],$obj_id));
208  if ($rec = $ilDB->fetchAssoc($set)) {
209  if ($a_data["left"] == 'cmi.core.lesson_status' && $a_data["right"] != $rec["rvalue"]) {
210  $b_updateStatus = true;
211  }
212  $ilDB->update('scorm_tracking',
213  array(
214  'rvalue' => array('clob', $a_data["right"]),
215  'c_timestamp' => array('timestamp', ilUtil::now())
216  ),
217  array(
218  'user_id' => array('integer', $user_id),
219  'sco_id' => array('integer', $a_data["sco_id"]),
220  'lvalue' => array('text', $a_data["left"]),
221  'obj_id' => array('integer', $obj_id)
222  )
223  );
224  if ($b_messageLog) {
225  $ilLog->write("ScormAicc: storeJsApi Updated - L:".$a_data["left"].",R:".
226  $a_data["right"]." for obj_id:".$obj_id.",sco_id:".$a_data["sco_id"].",user_id:".$user_id);
227  }
228  }
229  else {
230  if ($a_data["left"] == 'cmi.core.lesson_status') {
231  $b_updateStatus = true;
232  }
233  $ilDB->insert('scorm_tracking', array(
234  'obj_id' => array('integer', $obj_id),
235  'user_id' => array('integer', $user_id),
236  'sco_id' => array('integer', $a_data["sco_id"]),
237  'lvalue' => array('text', $a_data["left"]),
238  'rvalue' => array('clob', $a_data["right"]),
239  'c_timestamp' => array('timestamp', ilUtil::now())
240  ));
241  if ($b_messageLog) {
242  $ilLog->write("ScormAicc: storeJsApi Inserted - L:".$a_data["left"].",R:".
243  $a_data["right"]." for obj_id:".$obj_id.",sco_id:".$$a_data["sco_id"].",user_id:".$user_id);
244  }
245  }
246  }
247  }
248 
249  // update status
250  if ($b_updateStatus == true) {
251  include_once("./Services/Tracking/classes/class.ilLPStatusWrapper.php");
252  ilLPStatusWrapper::_updateStatus($obj_id, $user_id);
253  }
254 
255  // update time and numbers of attempts in change event
256  ilObjSCORMTracking::_syncReadEvent($obj_id, $user_id, "sahs", $ref_id);
257  }
258 
265  function _syncReadEvent($a_obj_id, $a_user_id, $a_type, $a_ref_id)
266  {
267  global $ilDB, $ilLog;
268 
269  // get attempts
270  $val_set = $ilDB->queryF('
271  SELECT * FROM scorm_tracking
272  WHERE user_id = %s
273  AND sco_id = %s
274  AND lvalue = %s
275  AND obj_id = %s',
276  array('integer','integer','text','integer'),
277  array($a_user_id,0,'package_attempts',$a_obj_id));
278 
279  $val_rec = $ilDB->fetchAssoc($val_set);
280 
281  $val_rec["rvalue"] = str_replace("\r\n", "\n", $val_rec["rvalue"]);
282  if ($val_rec["rvalue"] == null) {
283  $val_rec["rvalue"]="";
284  }
285  $attempts = $val_rec["rvalue"];
286 
287  // get learning time
288  $sco_set = $ilDB->queryF('
289  SELECT sco_id, rvalue FROM scorm_tracking
290  WHERE obj_id = %s
291  AND user_id = %s
292  AND lvalue = %s
293  AND sco_id <> %s',
294  array('integer','integer','text','integer'),
295  array($a_obj_id,$a_user_id, 'cmi.core.total_time',0));
296 
297  $time = 0;
298  while($sco_rec = $ilDB->fetchAssoc($sco_set))
299  {
300  $tarr = explode(":", $sco_rec["rvalue"]);
301  $sec = (int) $tarr[2] + (int) $tarr[1] * 60 +
302  (int) substr($tarr[0], strlen($tarr[0]) - 3) * 60 * 60;
303  $time += $sec;
304  }
305 
306  include_once("./Services/Tracking/classes/class.ilChangeEvent.php");
307  ilChangeEvent::_recordReadEvent($a_type, $a_ref_id, $a_obj_id, $a_user_id, false, $attempts, $time);
308  }
309 
310  function _insertTrackData($a_sahs_id, $a_lval, $a_rval, $a_obj_id)
311  {
312  global $ilDB, $ilUser;
313 
314  $ilDB->insert('scorm_tracking', array(
315  'obj_id' => array('integer', $a_obj_id),
316  'user_id' => array('integer', $ilUser->getId()),
317  'sco_id' => array('integer', $a_sahs_id),
318  'lvalue' => array('text', $a_lval),
319  'rvalue' => array('clob', $a_rval),
320  'c_timestamp' => array('timestamp', ilUtil::now())
321  ));
322 
323  if ($a_lval == "cmi.core.lesson_status")
324  {
325  include_once("./Services/Tracking/classes/class.ilLPStatusWrapper.php");
326  ilLPStatusWrapper::_updateStatus($a_obj_id, $ilUser->getId());
327  }
328  }
329 
330 
339  public static function _getInProgress($scorm_item_id,$a_obj_id)
340  {
341  global $ilDB;
342 
343  if(is_array($scorm_item_id))
344  {
345  $in = $ilDB->in('sco_id', $scorm_item_id, false, 'integer');
346 
347  $res = $ilDB->queryF('SELECT user_id,sco_id FROM scorm_tracking
348  WHERE '.$in.'
349  AND obj_id = %s
350  GROUP BY user_id, sco_id',
351  array('integer'),array($a_obj_id));
352 
353  }
354  else
355  {
356  $res = $ilDB->queryF('SELECT user_id,sco_id FROM scorm_tracking
357  WHERE sco_id = %s
358  AND obj_id = %s',
359  array('integer','integer'),array($scorm_item_id,$a_obj_id)
360  );
361  }
362 
363  while($row = $ilDB->fetchObject($res))
364  {
365  $in_progress[$row->sco_id][] = $row->user_id;
366  }
367  return is_array($in_progress) ? $in_progress : array();
368  }
369 
378  public static function _getCompleted($scorm_item_id,$a_obj_id)
379  {
380  global $ilDB;
381 
382  if(is_array($scorm_item_id))
383  {
384  $in = $ilDB->in('sco_id', $scorm_item_id, false, 'integer');
385 
386  $res = $ilDB->queryF('SELECT DISTINCT(user_id) FROM scorm_tracking
387  WHERE '.$in.'
388  AND obj_id = %s
389  AND lvalue = %s
390  AND ('.$ilDB->like('rvalue', 'clob', 'completed').' OR '.$ilDB->like('rvalue', 'clob', 'passed').')',
391  array('integer','text'),
392  array($a_obj_id,'cmi.core.lesson_status'));
393  }
394  else
395  {
396  $res = $ilDB->queryF('SELECT DISTINCT(user_id) FROM scorm_tracking
397  WHERE sco_id = %s
398  AND obj_id = %s
399  AND lvalue = %s
400  AND ('.$ilDB->like('rvalue', 'clob', 'completed').' OR '.$ilDB->like('rvalue', 'clob', 'passed').')',
401  array('integer','integer','text'),
402  array($scorm_item_id,$a_obj_id,'cmi.core.lesson_status'));
403  }
404 
405  while($row = $ilDB->fetchObject($res))
406  {
407  $user_ids[] = $row->user_id;
408  }
409  return $user_ids ? $user_ids : array();
410  }
411 
412  public static function _getCollectionStatus($a_scos, $a_obj_id, $a_user_id)
413  {
414  global $ilDB;
415 
416 
417  $status = "not_attempted";
418 
419  if (is_array($a_scos))
420  {
421  $in = $ilDB->in('sco_id', $a_scos, false, 'integer');
422 
423  $res = $ilDB->queryF('SELECT sco_id, rvalue FROM scorm_tracking
424  WHERE '.$in.'
425  AND obj_id = %s
426  AND lvalue = %s
427  AND user_id = %s',
428  array('integer','text', 'integer'),
429  array($a_obj_id,'cmi.core.lesson_status', $a_user_id));
430 
431  $cnt = 0;
432  $completed = true;
433  $failed = false;
434  while ($rec = $ilDB->fetchAssoc($res))
435  {
436  if ($rec["rvalue"] == "failed")
437  {
438  $failed = true;
439  }
440  if ($rec["rvalue"] != "completed" && $rec["rvalue"] != "passed")
441  {
442  $completed = false;
443  }
444  $cnt++;
445  }
446  if ($cnt > 0)
447  {
448  $status = "in_progress";
449  }
450  if ($completed && $cnt == count($a_scos))
451  {
452  $status = "completed";
453  }
454  if ($failed)
455  {
456  $status = "failed";
457  }
458 
459  }
460  return $status;
461  }
462 
463  public static function _countCompleted($a_scos, $a_obj_id, $a_user_id)
464  {
465  global $ilDB;
466 
467  if (is_array($a_scos))
468  {
469  $in = $ilDB->in('sco_id', $a_scos, false, 'integer');
470 
471  $res = $ilDB->queryF('SELECT sco_id, rvalue FROM scorm_tracking
472  WHERE '.$in.'
473  AND obj_id = %s
474  AND lvalue = %s
475  AND user_id = %s',
476  array('integer','text', 'integer'),
477  array($a_obj_id,'cmi.core.lesson_status', $a_user_id));
478 
479  $cnt = 0;
480  while ($rec = $ilDB->fetchAssoc($res))
481  {
482  if ($rec["rvalue"] == "completed" || $rec["rvalue"] == "passed")
483  {
484  $cnt++;
485  }
486  }
487  }
488  return $cnt;
489  }
490 
496  function _getTrackedUsers($a_obj_id)
497  {
498  global $ilDB, $ilLog;
499 
500  $res = $ilDB->queryF('SELECT DISTINCT user_id FROM scorm_tracking
501  WHERE obj_id = %s
502  AND lvalue = %s',
503  array('integer','text'),
504  array($a_obj_id,'cmi.core.lesson_status'));
505 
506  $users = array();
507  while ($row = $ilDB->fetchAssoc($res))
508  {
509  $users[] = $row["user_id"];
510  }
511  return $users;
512  }
513 
522  function _getFailed($scorm_item_id,$a_obj_id)
523  {
524  global $ilDB;
525 
526  if(is_array($scorm_item_id))
527  {
528  $in = $ilDB->in('sco_id', $scorm_item_id, false, 'integer');
529 
530  $res = $ilDB->queryF('
531  SELECT DISTINCT(user_id) FROM scorm_tracking
532  WHERE '.$in.'
533  AND obj_id = %s
534  AND lvalue = %s
535  AND '.$ilDB->like('rvalue', 'clob', 'failed').' ',
536  array('integer','text'),
537  array($a_obj_id,'cmi.core.lesson_status'));
538  }
539  else
540  {
541 
542  $res = $ilDB->queryF('
543  SELECT DISTINCT(user_id) FROM scorm_tracking
544  WHERE sco_id = %s
545  AND obj_id = %s
546  AND lvalue = %s
547  AND '.$ilDB->like('rvalue', 'clob', 'failed').' ',
548  array('integer','integer','text'),
549  array($scorm_item_id,$a_obj_id,'cmi.core.lesson_status'));
550  }
551 
552  while($row = $ilDB->fetchObject($res))
553  {
554  $user_ids[] = $row->user_id;
555  }
556  return $user_ids ? $user_ids : array();
557  }
558 
565  public static function _getCountCompletedPerUser($a_scorm_item_ids,$a_obj_id)
566  {
567  global $ilDB;
568 
569  $in = $ilDB->in('sco_id', $a_scorm_item_ids, false, 'integer');
570 
571  // Why does this query use a like search against "passed" and "failed"
572  /*
573  $res = $ilDB->queryF('
574  SELECT user_id, COUNT(user_id) completed FROM scorm_tracking
575  WHERE '.$in.'
576  AND obj_id = %s
577  AND lvalue = %s
578  AND ('.$ilDB->like('rvalue', 'clob', 'completed').' OR '.$ilDB->like('rvalue', 'clob', 'passed').')
579  GROUP BY user_id',
580  array('integer', 'text'),
581  array($a_obj_id, 'cmi.core.lesson_status')
582  );
583  */
584 
585  // Avoid searches against field rvalue.
586  // This gives the possibility to reuse the obj_id,sco_id,lvalue index.
587  $query = "SELECT user_id,rvalue FROM scorm_tracking ".
588  "WHERE ".$in." ".
589  "AND obj_id = ".$ilDB->quote($a_obj_id,'integer')." ".
590  "AND lvalue = ".$ilDB->quote('cmi.core.lesson_status','text');
591 
592  $res = $ilDB->query($query);
593  while($row = $ilDB->fetchObject($res))
594  {
595  if($row->rvalue == 'passed' or $row->rvalue == 'completed')
596  {
597  ++$users[$row->user_id];
598  }
599  }
600  return $users ? $users : array();
601  }
602 
609  public static function _getProgressInfo($sco_item_ids,$a_obj_id)
610  {
611  global $ilDB;
612 
613  $in = $ilDB->in('sco_id', $sco_item_ids, false, 'integer');
614 
615  $res = $ilDB->queryF('
616  SELECT * FROM scorm_tracking
617  WHERE '.$in.'
618  AND obj_id = %s
619  AND lvalue = %s ',
620  array('integer','text'),
621  array($a_obj_id,'cmi.core.lesson_status'));
622 
623  $info['completed'] = array();
624  $info['failed'] = array();
625 
626  while($row = $ilDB->fetchObject($res))
627  {
628  switch($row->rvalue)
629  {
630  case 'completed':
631  case 'passed':
632  $info['completed'][$row->sco_id][] = $row->user_id;
633  break;
634 
635  case 'failed':
636  $info['failed'][$row->sco_id][] = $row->user_id;
637  break;
638  }
639  }
640  $info['in_progress'] = ilObjSCORMTracking::_getInProgress($sco_item_ids,$a_obj_id);
641 
642  return $info;
643  }
644 
645 
646 } // END class.ilObjSCORMTracking
647 ?>