ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
class.ilSessionStatistics.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
3 
4 
12 {
13  const SLOT_SIZE = 15;
14 
20  public static function isActive()
21  {
22  global $ilSetting;
23 
24  return (bool) $ilSetting->get('session_statistics', 1);
25 
26  /* #13566 - includes somehow won't work this late in the request - doing it directly
27  include_once "Services/Tracking/classes/class.ilObjUserTracking.php";
28  return ilObjUserTracking::_enabledSessionStatistics();
29  */
30  }
31 
40  public static function createRawEntry($a_session_id, $a_session_type, $a_timestamp, $a_user_id)
41  {
42  global $ilDB;
43 
44  if (!$a_user_id || !$a_session_id || !self::isActive()) {
45  return;
46  }
47 
48  // #9669: if a session was destroyed and somehow the session id is still
49  // in use there will be a id-collision for the raw-entry
50 
51  $ilDB->replace(
52  "usr_session_stats_raw",
53  array(
54  "session_id" => array("text", $a_session_id)
55  ),
56  array(
57  "type" => array("integer", $a_session_type),
58  "start_time" => array("integer", $a_timestamp),
59  "user_id" => array("integer", $a_user_id)
60  )
61  );
62  }
63 
71  public static function closeRawEntry($a_session_id, $a_context = null, $a_expired_at = null)
72  {
73  global $ilDB;
74 
75  if (!self::isActive()) {
76  return;
77  }
78 
79  // single entry
80  if (!is_array($a_session_id)) {
81  if ($a_expired_at) {
82  $end_time = $a_expired_at;
83  } else {
84  $end_time = time();
85  }
86  $sql = "UPDATE usr_session_stats_raw" .
87  " SET end_time = " . $ilDB->quote($end_time, "integer");
88  if ($a_context) {
89  $sql .= ",end_context = " . $ilDB->quote($a_context, "integer");
90  }
91  $sql .= " WHERE session_id = " . $ilDB->quote($a_session_id, "text") .
92  " AND end_time IS NULL";
93  $ilDB->manipulate($sql);
94  }
95  // batch closing
96  elseif (!$a_expired_at) {
97  $sql = "UPDATE usr_session_stats_raw" .
98  " SET end_time = " . $ilDB->quote(time(), "integer");
99  if ($a_context) {
100  $sql .= ",end_context = " . $ilDB->quote($a_context, "integer");
101  }
102  $sql .= " WHERE " . $ilDB->in("session_id", $a_session_id, false, "text") .
103  " AND end_time IS NULL";
104  $ilDB->manipulate($sql);
105  }
106  // batch with individual timestamps
107  else {
108  foreach ($a_session_id as $id => $ts) {
109  $sql = "UPDATE usr_session_stats_raw" .
110  " SET end_time = " . $ilDB->quote($ts, "integer");
111  if ($a_context) {
112  $sql .= ",end_context = " . $ilDB->quote($a_context, "integer");
113  }
114  $sql .= " WHERE session_id = " . $ilDB->quote($id, "text") .
115  " AND end_time IS NULL";
116  $ilDB->manipulate($sql);
117  }
118  }
119  }
120 
127  protected static function getCurrentSlot($a_now)
128  {
129  global $ilDB;
130 
131  // get latest slot in db
132  $sql = "SELECT MAX(slot_end) previous_slot_end" .
133  " FROM usr_session_stats";
134  $res = $ilDB->query($sql);
135  $row = $ilDB->fetchAssoc($res);
136  $previous_slot_end = $row["previous_slot_end"];
137 
138  // no previous slot? calculate last complete slot
139  // should we use minimum session raw date instead? (problem: table lock)
140  if (!$previous_slot_end) {
141  $slot = floor(date("i")/self::SLOT_SIZE);
142  // last slot of previous hour
143  if (!$slot) {
144  $current_slot_begin = mktime(date("H", $a_now)-1, 60-self::SLOT_SIZE, 0);
145  }
146  // "normalize" to slot
147  else {
148  $current_slot_begin = mktime(date("H", $a_now), ($slot-1)*self::SLOT_SIZE, 0);
149  }
150  } else {
151  $current_slot_begin = $previous_slot_end+1;
152  }
153 
154  $current_slot_end = $current_slot_begin+(60*self::SLOT_SIZE)-1;
155 
156  // no complete slot: nothing to do yet
157  if ($current_slot_end < $a_now) {
158  return array($current_slot_begin, $current_slot_end);
159  }
160  }
161 
168  protected static function getNumberOfActiveRawSessions($a_time)
169  {
170  global $ilDB;
171 
172  $sql = "SELECT COUNT(*) counter FROM usr_session_stats_raw" .
173  " WHERE (end_time IS NULL OR end_time >= " . $ilDB->quote($a_time, "integer") . ")" .
174  " AND start_time <= " . $ilDB->quote($a_time, "integer") .
175  " AND " . $ilDB->in("type", ilSessionControl::$session_types_controlled, false, "integer");
176  $res = $ilDB->query($sql);
177  $row = $ilDB->fetchAssoc($res);
178  return $row["counter"];
179  }
180 
188  protected static function getRawData($a_begin, $a_end)
189  {
190  global $ilDB;
191 
192  $sql = "SELECT start_time,end_time,end_context FROM usr_session_stats_raw" .
193  " WHERE start_time <= " . $ilDB->quote($a_end, "integer") .
194  " AND (end_time IS NULL OR end_time >= " . $ilDB->quote($a_begin, "integer") . ")" .
195  " AND " . $ilDB->in("type", ilSessionControl::$session_types_controlled, false, "integer") .
196  " ORDER BY start_time";
197  $res = $ilDB->query($sql);
198  $all = array();
199  while ($row = $ilDB->fetchAssoc($res)) {
200  $all[] = $row;
201  }
202  return $all;
203  }
204 
211  protected static function createNewAggregationSlot($a_now)
212  {
213  global $ilDB;
214 
215  $ilAtomQuery = $ilDB->buildAtomQuery();
216  $ilAtomQuery->addTableLock("usr_session_stats");
217 
218  $ilAtomQuery->addQueryCallable(function (ilDBInterface $ilDB) use ($a_now, &$slot) {
219 
220  // if we had to wait for the lock, no current slot should be returned here
221  $slot = self::getCurrentSlot($a_now);
222  if (!is_array($slot)) {
223  $slot = false;
224  return;
225  }
226 
227  // save slot to mark as taken
228  $fields = array(
229  "slot_begin" => array("integer", $slot[0]),
230  "slot_end" => array("integer", $slot[1]),
231  );
232  $ilDB->insert("usr_session_stats", $fields);
233  });
234 
235  $ilAtomQuery->run();
236 
237  return $slot;
238  }
239 
245  public static function aggretateRaw($a_now)
246  {
247  if (!self::isActive()) {
248  return;
249  }
250 
251  $slot = self::createNewAggregationSlot($a_now);
252  while (is_array($slot)) {
253  self::aggregateRawHelper($slot[0], $slot[1]);
254  $slot = self::createNewAggregationSlot($a_now);
255  }
256 
257  // #12728
258  self::deleteAggregatedRaw($a_now);
259  }
260 
267  public static function aggregateRawHelper($a_begin, $a_end)
268  {
269  global $ilDB, $ilSetting;
270 
271  // "relevant" closing types
272  $separate_closed = array(ilSession::SESSION_CLOSE_USER,
278 
279  // gather/process data (build event timeline)
280  $closed_counter = $events = array();
281  $opened_counter = 0;
282  foreach (self::getRawData($a_begin, $a_end) as $item) {
283  // open/close counters are _not_ time related
284 
285  // we could filter for undefined/invalid closing contexts
286  // and ignore those items, but this would make any debugging
287  // close to impossible
288  // "closed_other" would have been a good idea...
289 
290  // session opened
291  if ($item["start_time"] >= $a_begin) {
292  $opened_counter++;
293  $events[$item["start_time"]][] = 1;
294  }
295  // session closed
296  if ($item["end_time"] && $item["end_time"] <= $a_end) {
297  if (in_array($item["end_context"], $separate_closed)) {
298  $closed_counter[$item["end_context"]]++;
299  } else {
300  $closed_counter[0]++;
301  }
302  $events[$item["end_time"]][] = -1;
303  }
304  }
305 
306  // initialising active statistical values
307  $active_begin = self::getNumberOfActiveRawSessions($a_begin-1);
308  $active_end = $active_min = $active_max = $active_avg = $active_begin;
309 
310  // parsing events / building avergages
311  if (sizeof($events)) {
312  $last_update_avg = $a_begin-1;
313  $slot_seconds = self::SLOT_SIZE*60;
314  $active_avg = 0;
315 
316  // parse all open/closing events
317  ksort($events);
318  foreach ($events as $ts => $actions) {
319  // actions which occur in the same second are "merged"
320  foreach ($actions as $action) {
321  // max
322  if ($action > 0) {
323  $active_end++;
324  }
325  // min
326  else {
327  $active_end--;
328  }
329  }
330 
331  // max
332  if ($active_end > $active_max) {
333  $active_max = $active_end;
334  }
335 
336  // min
337  if ($active_end < $active_min) {
338  $active_min = $active_end;
339  }
340 
341  // avg
342  $diff = $ts-$last_update_avg;
343  $active_avg += $diff/$slot_seconds*$active_end;
344  $last_update_avg = $ts;
345  }
346  unset($actions);
347 
348  // add up to end of slot if needed
349  if ($last_update_avg < $a_end) {
350  $diff = $a_end-$last_update_avg;
351  $active_avg += $diff/$slot_seconds*$active_end;
352  }
353 
354  $active_avg = round($active_avg);
355  }
356  unset($events);
357 
358 
359  // do we (really) need a log here?
360  // $max_sessions = (int)$ilSetting->get("session_max_count", ilSessionControl::DEFAULT_MAX_COUNT);
361  $max_sessions = self::getLimitForSlot($a_begin);
362 
363  // save aggregated data
364  $fields = array(
365  "active_min" => array("integer", $active_min),
366  "active_max" => array("integer", $active_max),
367  "active_avg" => array("integer", $active_avg),
368  "active_end" => array("integer", $active_end),
369  "opened" => array("integer", $opened_counter),
370  "closed_manual" => array("integer", (int) $closed_counter[ilSession::SESSION_CLOSE_USER]),
371  "closed_expire" => array("integer", (int) $closed_counter[ilSession::SESSION_CLOSE_EXPIRE]),
372  "closed_idle" => array("integer", (int) $closed_counter[ilSession::SESSION_CLOSE_IDLE]),
373  "closed_idle_first" => array("integer", (int) $closed_counter[ilSession::SESSION_CLOSE_FIRST]),
374  "closed_limit" => array("integer", (int) $closed_counter[ilSession::SESSION_CLOSE_LIMIT]),
375  "closed_login" => array("integer", (int) $closed_counter[ilSession::SESSION_CLOSE_LOGIN]),
376  "closed_misc" => array("integer", (int) $closed_counter[0]),
377  "max_sessions" => array("integer", (int) $max_sessions)
378  );
379  $ilDB->update(
380  "usr_session_stats",
381  $fields,
382  array("slot_begin" => array("integer", $a_begin),
383  "slot_end" => array("integer", $a_end))
384  );
385  }
386 
392  protected static function deleteAggregatedRaw($a_now)
393  {
394  global $ilDB;
395 
396  // we are rather defensive here - 7 days BEFORE current aggregation
397  $cut = $a_now-(60*60*24*7);
398 
399  $ilDB->manipulate("DELETE FROM usr_session_stats_raw" .
400  " WHERE start_time <= " . $ilDB->quote($cut, "integer"));
401  }
402 
408  public static function getLastMaxedOut()
409  {
410  global $ilDB;
411 
412  $sql = "SELECT max(slot_end) latest FROM usr_session_stats" .
413  " WHERE active_max >= max_sessions" .
414  " AND max_sessions > " . $ilDB->quote(0, "integer");
415  $res = $ilDB->query($sql);
416  $row = $ilDB->fetchAssoc($res);
417  if ($row["latest"]) {
418  return $row["latest"];
419  }
420  }
421 
429  public static function getMaxedOutDuration($a_from, $a_to)
430  {
431  global $ilDB;
432 
433  $sql = "SELECT SUM(slot_end-slot_begin) dur FROM usr_session_stats" .
434  " WHERE active_max >= max_sessions" .
435  " AND max_sessions > " . $ilDB->quote(0, "integer") .
436  " AND slot_end > " . $ilDB->quote($a_from, "integer") .
437  " AND slot_begin < " . $ilDB->quote($a_to, "integer");
438  $res = $ilDB->query($sql);
439  $row = $ilDB->fetchAssoc($res);
440  if ($row["dur"]) {
441  return $row["dur"];
442  }
443  }
444 
452  public static function getNumberOfSessionsByType($a_from, $a_to)
453  {
454  global $ilDB;
455 
456  $sql = "SELECT SUM(opened) opened, SUM(closed_manual) closed_manual," .
457  " SUM(closed_expire) closed_expire, SUM(closed_idle) closed_idle," .
458  " SUM(closed_idle_first) closed_idle_first, SUM(closed_limit) closed_limit," .
459  " SUM(closed_login) closed_login, SUM(closed_misc) closed_misc" .
460  " FROM usr_session_stats" .
461  " WHERE slot_end > " . $ilDB->quote($a_from, "integer") .
462  " AND slot_begin < " . $ilDB->quote($a_to, "integer");
463  $res = $ilDB->query($sql);
464  return $ilDB->fetchAssoc($res);
465  }
466 
474  public static function getActiveSessions($a_from, $a_to)
475  {
476  global $ilDB;
477 
478  $sql = "SELECT slot_begin, slot_end, active_min, active_max, active_avg," .
479  " max_sessions" .
480  " FROM usr_session_stats" .
481  " WHERE slot_end > " . $ilDB->quote($a_from, "integer") .
482  " AND slot_begin < " . $ilDB->quote($a_to, "integer") .
483  " ORDER BY slot_begin";
484  $res = $ilDB->query($sql);
485  $all = array();
486  while ($row = $ilDB->fetchAssoc($res)) {
487  $all[] = $row;
488  }
489  return $all;
490  }
491 
497  public static function getLastAggregation()
498  {
499  global $ilDB;
500 
501  $sql = "SELECT max(slot_end) latest FROM usr_session_stats";
502  $res = $ilDB->query($sql);
503  $row = $ilDB->fetchAssoc($res);
504  if ($row["latest"]) {
505  return $row["latest"];
506  }
507  }
508 
515  public static function getLimitForSlot($a_timestamp)
516  {
517  global $ilDB, $ilSetting;
518 
519  $ilDB->setLimit(1);
520  $sql = "SELECT maxval FROM usr_session_log" .
521  " WHERE tstamp <= " . $ilDB->quote($a_timestamp, "integer") .
522  " ORDER BY tstamp DESC";
523  $res = $ilDB->query($sql);
524  $val = $ilDB->fetchAssoc($res);
525  if ($val["maxval"]) {
526  return (int) $val["maxval"];
527  } else {
528  return (int) $ilSetting->get("session_max_count", ilSessionControl::DEFAULT_MAX_COUNT);
529  }
530  }
531 
537  public static function updateLimitLog($a_new_value)
538  {
539  global $ilDB, $ilSetting, $ilUser;
540 
541  $new_value = (int) $a_new_value;
542  $old_value = (int) $ilSetting->get("session_max_count", ilSessionControl::DEFAULT_MAX_COUNT);
543 
544  if ($new_value != $old_value) {
545  $fields = array(
546  "tstamp" => array("timestamp", time()),
547  "maxval" => array("integer", $new_value),
548  "user_id" => array("integer", $ilUser->getId())
549  );
550  $ilDB->insert("usr_session_log", $fields);
551  }
552  }
553 }
static getNumberOfActiveRawSessions($a_time)
Count number of active sessions at given time.
const SESSION_CLOSE_IDLE
const DEFAULT_MAX_COUNT
default value for settings that have not been defined in setup or administration yet ...
static getMaxedOutDuration($a_from, $a_to)
Get maxed out duration in given timeframe.
static deleteAggregatedRaw($a_now)
Remove already aggregated raw data.
static getCurrentSlot($a_now)
Get next slot to aggregate.
$action
const SESSION_CLOSE_LOGIN
if(!array_key_exists('StateId', $_REQUEST)) $id
static getNumberOfSessionsByType($a_from, $a_to)
Get session counters by type (opened, closed)
static createNewAggregationSlot($a_now)
Create new slot (using table lock)
const SESSION_CLOSE_EXPIRE
Interface ilDBInterface.
static updateLimitLog($a_new_value)
Log max session setting.
static getLastAggregation()
Get timestamp of last aggregation.
static aggregateRawHelper($a_begin, $a_end)
Aggregate statistics data for one slot.
static getLimitForSlot($a_timestamp)
Get max session setting for given timestamp.
foreach($_POST as $key=> $value) $res
const SESSION_CLOSE_USER
date( 'd-M-Y', $objPHPExcel->getProperties() ->getCreated())
$ilUser
Definition: imgupload.php:18
static createRawEntry($a_session_id, $a_session_type, $a_timestamp, $a_user_id)
Create raw data entry.
const SESSION_CLOSE_LIMIT
Create styles array
The data for the language used.
static getRawData($a_begin, $a_end)
Read raw data for timespan.
static isActive()
Is session statistics active at all?
global $ilSetting
Definition: privfeed.php:17
global $ilDB
insert($table_name, $values)
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
static getLastMaxedOut()
Get latest slot during which sessions were maxed out.
static aggretateRaw($a_now)
Aggregate raw session data (older than given time)
const SESSION_CLOSE_FIRST
static closeRawEntry($a_session_id, $a_context=null, $a_expired_at=null)
Close raw data entry.
static getActiveSessions($a_from, $a_to)
Get active sessions aggregated data.