[ Index ]

PHP Cross Reference of Eventum

title

Body

[close]

/include/ -> class.round_robin.php (source)

   1  <?php
   2  /* vim: set expandtab tabstop=4 shiftwidth=4 encoding=utf-8: */
   3  // +----------------------------------------------------------------------+
   4  // | Eventum - Issue Tracking System                                      |
   5  // +----------------------------------------------------------------------+
   6  // | Copyright (c) 2003, 2004, 2005, 2006, 2007 MySQL AB                  |
   7  // |                                                                      |
   8  // | This program is free software; you can redistribute it and/or modify |
   9  // | it under the terms of the GNU General Public License as published by |
  10  // | the Free Software Foundation; either version 2 of the License, or    |
  11  // | (at your option) any later version.                                  |
  12  // |                                                                      |
  13  // | This program is distributed in the hope that it will be useful,      |
  14  // | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
  15  // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        |
  16  // | GNU General Public License for more details.                         |
  17  // |                                                                      |
  18  // | You should have received a copy of the GNU General Public License    |
  19  // | along with this program; if not, write to:                           |
  20  // |                                                                      |
  21  // | Free Software Foundation, Inc.                                       |
  22  // | 59 Temple Place - Suite 330                                          |
  23  // | Boston, MA 02111-1307, USA.                                          |
  24  // +----------------------------------------------------------------------+
  25  // | Authors: João Prado Maia <jpm@mysql.com>                             |
  26  // +----------------------------------------------------------------------+
  27  //
  28  // @(#) $Id: class.round_robin.php 3246 2007-02-09 09:10:12Z glen $
  29  //
  30  
  31  require_once (APP_INC_PATH . 'class.date.php');
  32  require_once (APP_INC_PATH . 'class.error_handler.php');
  33  
  34  class Round_Robin
  35  {
  36      /**
  37       * Returns the blackout dates according to the user's timezone.
  38       *
  39       * @access  public
  40       * @param   object $user The Date object associated with the user's timezone
  41       * @param   integer $start The blackout start hour
  42       * @param   integer $end The blackout end hour
  43       * @return  array The blackout dates
  44       */
  45      function getBlackoutDates(&$user, $start, $end)
  46      {
  47          $start = substr($start, 0, 2);
  48          $end = substr($end, 0, 2);
  49  
  50          // if start is AM and end is PM, then use only today
  51          // if start is AM and end is AM (and end is smaller than start), then use today and tomorrow
  52          //   - if date is between zero and the end, then use yesterday and today
  53          // if start is AM and end is AM (and end is bigger than start), then use only today
  54          // if start is PM and end is PM (and end is smaller than start), then use today and tomorrow
  55          //   - if date is between zero and the end, then use yesterday and today
  56          // if start is PM and end is PM (and end is bigger than start), then use only today
  57          // if start is PM and end is AM, then use today and tomorrow
  58          //   - if date is between zero and the end, then use yesterday and today
  59          if ((Date_API::isAM($start)) && (Date_API::isPM($end))) {
  60              $first = 0;
  61              $second = 0;
  62          }
  63          if ((Date_API::isAM($start)) && (Date_API::isAM($end)) && ($end < $start)) {
  64              if (($user->getHour() >= 0) && ($user->getHour() <= $end)) {
  65                  $first = -DAY;
  66                  $second = 0;
  67              } else {
  68                  $first = 0;
  69                  $second = DAY;
  70              }
  71          }
  72          if ((Date_API::isAM($start)) && (Date_API::isAM($end)) && ($end > $start)) {
  73              $first = 0;
  74              $second = 0;
  75          }
  76          if ((Date_API::isPM($start)) && (Date_API::isPM($end)) && ($end < $start)) {
  77              if (($user->getHour() >= 0) && ($user->getHour() <= $end)) {
  78                  $first = -DAY;
  79                  $second = 0;
  80              } else {
  81                  $first = 0;
  82                  $second = DAY;
  83              }
  84          }
  85          if ((Date_API::isPM($start)) && (Date_API::isPM($end)) && ($end > $start)) {
  86              $first = 0;
  87              $second = 0;
  88          }
  89          if ((Date_API::isPM($start)) && (Date_API::isAM($end))) {
  90              if (($user->getHour() >= 0) && ($user->getHour() <= $end)) {
  91                  $first = -DAY;
  92                  $second = 0;
  93              } else {
  94                  $first = 0;
  95                  $second = DAY;
  96              }
  97          }
  98  
  99          return array(
 100              date('Y-m-d', $user->getDate(DATE_FORMAT_UNIXTIME) + $first),
 101              date('Y-m-d', $user->getDate(DATE_FORMAT_UNIXTIME) + $second)
 102          );
 103      }
 104  
 105  
 106      /**
 107       * Retrieves the next assignee in the given project's round robin queue.
 108       *
 109       * @access  public
 110       * @param   integer $prj_id The project ID
 111       * @return  integer The assignee's user ID
 112       */
 113      function getNextAssignee($prj_id)
 114      {
 115          // get the full list of users for the given project
 116          list($blackout_start, $blackout_end, $users) = Round_Robin::getUsersByProject($prj_id);
 117          if (count($users) == 0) {
 118              return 0;
 119          } else {
 120              $user_ids = array_keys($users);
 121              $next_usr_id = 0;
 122              foreach ($users as $usr_id => $details) {
 123                  if ($details['is_next']) {
 124                      $next_usr_id = $usr_id;
 125                      break;
 126                  }
 127              }
 128              // if no user is currently set as the 'next' assignee,
 129              // then just get the first one in the list
 130              if (empty($next_usr_id)) {
 131                  $next_usr_id = $user_ids[0];
 132              }
 133              // counter to keep the number of times we found an invalid user
 134              $ignored_users = 0;
 135              // check the blackout hours
 136              do {
 137                  $user = new Date(Date_API::getCurrentUnixTimestampGMT());
 138                  $user->convertTZById($users[$next_usr_id]['timezone']);
 139                  list($today, $tomorrow) = Round_Robin::getBlackoutDates($user, $blackout_start, $blackout_end);
 140                  $first = new Date($today . ' ' . $blackout_start);
 141                  $first->setTZById($users[$next_usr_id]['timezone']);
 142                  $second = new Date($tomorrow . ' ' . $blackout_end);
 143                  $second->setTZById($users[$next_usr_id]['timezone']);
 144                  if ((Date::compare($first, $user) == -1) && (Date::compare($user, $second) == -1)) {
 145                      $ignored_users++;
 146                      $current_index = array_search($next_usr_id, $user_ids);
 147                      // if we reached the end of the list of users and none of them
 148                      // was a valid one, then just select the first one
 149                      // however, we want to complete at least one full iteration over the list of users
 150                      // that is, if we didn't start checking the users in the beginning of the list,
 151                      // then do another run over the users just in case
 152                      if (($ignored_users >= count($user_ids)) && ($current_index == (count($user_ids) - 1))) {
 153                          $assignee = $user_ids[0];
 154                          break;
 155                      }
 156                      // if we reached the end of the list, and we still didn't find an user,
 157                      // then go back to the beginning of the list one last time
 158                      if ($current_index == (count($user_ids) - 1)) {
 159                          $current_index = 0;
 160                          $next_usr_id = $user_ids[++$current_index];
 161                          $found = 0;
 162                          continue;
 163                      }
 164                      $next_usr_id = $user_ids[++$current_index];
 165                      $found = 0;
 166                  } else {
 167                      $assignee = $next_usr_id;
 168                      $found = 1;
 169                  }
 170              } while (!$found);
 171              // mark the next user in the list as the 'next' assignment
 172              $assignee_index = array_search($assignee, $user_ids);
 173              if ($assignee_index == (count($user_ids) -1)) {
 174                  $next_assignee = $user_ids[0];
 175              } else {
 176                  $next_assignee = $user_ids[++$assignee_index];
 177              }
 178              Round_Robin::markNextAssignee($prj_id, $next_assignee);
 179              return $assignee;
 180          }
 181      }
 182  
 183  
 184      /**
 185       * Marks the next user in the round robin list as the next assignee in the
 186       * round robin queue.
 187       *
 188       * @access  public
 189       * @param   integer $prj_id The project ID
 190       * @param   integer $usr_id The assignee's user ID
 191       * @return  boolean
 192       */
 193      function markNextAssignee($prj_id, $usr_id)
 194      {
 195          $prr_id = Round_Robin::getID($prj_id);
 196          $stmt = "UPDATE
 197                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "round_robin_user
 198                   SET
 199                      rru_next=0
 200                   WHERE
 201                      rru_prr_id=$prr_id";
 202          $res = $GLOBALS["db_api"]->dbh->query($stmt);
 203          if (PEAR::isError($res)) {
 204              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 205              return false;
 206          } else {
 207              $stmt = "UPDATE
 208                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "round_robin_user
 209                       SET
 210                          rru_next=1
 211                       WHERE
 212                          rru_usr_id=" . Misc::escapeInteger($usr_id) . " AND
 213                          rru_prr_id=$prr_id";
 214              $res = $GLOBALS["db_api"]->dbh->query($stmt);
 215              if (PEAR::isError($res)) {
 216                  Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 217                  return false;
 218              } else {
 219                  return true;
 220              }
 221          }
 222      }
 223  
 224  
 225      /**
 226       * Returns the round robin entry ID associated with a given project.
 227       *
 228       * @access  public
 229       * @param   integer $prj_id The project ID
 230       * @return  integer The round robin entry ID
 231       */
 232      function getID($prj_id)
 233      {
 234          $stmt = "SELECT
 235                      prr_id
 236                   FROM
 237                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_round_robin
 238                   WHERE
 239                      prr_prj_id=" . Misc::escapeInteger($prj_id);
 240          $res = $GLOBALS["db_api"]->dbh->getOne($stmt);
 241          if (PEAR::isError($res)) {
 242              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 243              return "";
 244          } else {
 245              return $res;
 246          }
 247      }
 248  
 249  
 250      /**
 251       * Retrieves the list of users, round robin blackout hours and their
 252       * respective preferences with regards to timezones.
 253       *
 254       * @access  public
 255       * @param   integer $prj_id The project ID
 256       * @return  array The list of users
 257       */
 258      function getUsersByProject($prj_id)
 259      {
 260          $stmt = "SELECT
 261                      usr_id,
 262                      usr_preferences,
 263                      rru_next,
 264                      prr_blackout_start,
 265                      prr_blackout_end
 266                   FROM
 267                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_round_robin,
 268                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "round_robin_user,
 269                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
 270                   WHERE
 271                      prr_prj_id=" . Misc::escapeInteger($prj_id) . " AND
 272                      prr_id=rru_prr_id AND
 273                      rru_usr_id=usr_id
 274                   ORDER BY
 275                      usr_id ASC";
 276          $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC);
 277          if (PEAR::isError($res)) {
 278              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 279              return array();
 280          } else {
 281              $blackout_start = '';
 282              $blackout_end = '';
 283              $t = array();
 284              for ($i = 0; $i < count($res); $i++) {
 285                  $blackout_start = $res[$i]['prr_blackout_start'];
 286                  $blackout_end = $res[$i]['prr_blackout_end'];
 287                  $prefs = unserialize($res[$i]['usr_preferences']);
 288                  $t[$res[$i]['usr_id']] = array(
 289                      'timezone' => $prefs['timezone'],
 290                      'is_next'  => $res[$i]['rru_next']
 291                  );
 292              }
 293              return array(
 294                  $blackout_start,
 295                  $blackout_end,
 296                  $t
 297              );
 298          }
 299      }
 300  
 301  
 302      /**
 303       * Creates a new round robin entry.
 304       *
 305       * @access  public
 306       * @return  integer 1 if the creation worked, -1 otherwise
 307       */
 308      function insert()
 309      {
 310          $blackout_start = $_POST['blackout_start']['Hour'] . ':' . $_POST['blackout_start']['Minute'] . ':00';
 311          $blackout_end = $_POST['blackout_end']['Hour'] . ':' . $_POST['blackout_end']['Minute'] . ':00';
 312          $stmt = "INSERT INTO
 313                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_round_robin
 314                   (
 315                      prr_prj_id,
 316                      prr_blackout_start,
 317                      prr_blackout_end
 318                   ) VALUES (
 319                      " . Misc::escapeInteger($_POST["project"]) . ",
 320                      '" . Misc::escapeString($blackout_start) . "',
 321                      '" . Misc::escapeString($blackout_end) . "'
 322                   )";
 323          $res = $GLOBALS["db_api"]->dbh->query($stmt);
 324          if (PEAR::isError($res)) {
 325              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 326              return -1;
 327          } else {
 328              $new_id = $GLOBALS["db_api"]->get_last_insert_id();
 329              // add all of the user associated with this round robin entry
 330              foreach ($_POST['users'] as $usr_id) {
 331                  Round_Robin::addUserAssociation($new_id, $usr_id);
 332              }
 333              return 1;
 334          }
 335      }
 336  
 337  
 338      /**
 339       * Associates a round robin entry with a user ID.
 340       *
 341       * @access  public
 342       * @param   integer $prr_id The round robin entry ID
 343       * @param   integer $usr_id The user ID
 344       * @return  boolean
 345       */
 346      function addUserAssociation($prr_id, $usr_id)
 347      {
 348          $stmt = "INSERT INTO
 349                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "round_robin_user
 350                   (
 351                      rru_prr_id,
 352                      rru_usr_id,
 353                      rru_next
 354                   ) VALUES (
 355                      " . Misc::escapeInteger($prr_id) . ",
 356                      " . Misc::escapeInteger($usr_id) . ",
 357                      0
 358                   )";
 359          $res = $GLOBALS["db_api"]->dbh->query($stmt);
 360          if (PEAR::isError($res)) {
 361              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 362              return false;
 363          } else {
 364              return true;
 365          }
 366      }
 367  
 368  
 369      /**
 370       * Method used to get the list of round robin entries available in the
 371       * system.
 372       *
 373       * @access  public
 374       * @return  array The list of round robin entries
 375       */
 376      function getList()
 377      {
 378          $stmt = "SELECT
 379                      prr_id,
 380                      prj_title
 381                   FROM
 382                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_round_robin,
 383                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project
 384                   WHERE
 385                      prr_prj_id=prj_id
 386                   ORDER BY
 387                      prj_title ASC";
 388          $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC);
 389          if (PEAR::isError($res)) {
 390              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 391              return "";
 392          } else {
 393              // get the list of associated users
 394              for ($i = 0; $i < count($res); $i++) {
 395                  $res[$i]['users'] = implode(", ", array_values(Round_Robin::getAssociatedUsers($res[$i]['prr_id'])));
 396              }
 397              return $res;
 398          }
 399      }
 400  
 401  
 402      /**
 403       * Returns an associative array in the form of user id => name of the users
 404       * associated to a given round robin entry ID.
 405       *
 406       * @access  public
 407       * @param   integer $prr_id The round robin entry ID
 408       * @return  array The list of users
 409       */
 410      function getAssociatedUsers($prr_id)
 411      {
 412          $stmt = "SELECT
 413                      usr_id,
 414                      usr_full_name
 415                   FROM
 416                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "round_robin_user,
 417                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
 418                   WHERE
 419                      rru_usr_id=usr_id AND
 420                      rru_prr_id=" . Misc::escapeInteger($prr_id) . "
 421                   ORDER BY
 422                      usr_id ASC";
 423          $res = $GLOBALS["db_api"]->dbh->getAssoc($stmt);
 424          if (PEAR::isError($res)) {
 425              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 426              return array();
 427          } else {
 428              return $res;
 429          }
 430      }
 431  
 432  
 433      /**
 434       * Method used to get the details of a round robin entry.
 435       *
 436       * @access  public
 437       * @param   integer $prr_id The round robin entry ID
 438       * @return  array The round robin entry details
 439       */
 440      function getDetails($prr_id)
 441      {
 442          $stmt = "SELECT
 443                      *
 444                   FROM
 445                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_round_robin
 446                   WHERE
 447                      prr_id=" . Misc::escapeInteger($prr_id);
 448          $res = $GLOBALS["db_api"]->dbh->getRow($stmt, DB_FETCHMODE_ASSOC);
 449          if (PEAR::isError($res)) {
 450              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 451              return "";
 452          } else {
 453              // get all of the user associations here as well
 454              $res['users'] = array_keys(Round_Robin::getAssociatedUsers($res['prr_id']));
 455              return $res;
 456          }
 457      }
 458  
 459  
 460      /**
 461       * Method used to update a round robin entry in the system.
 462       *
 463       * @access  public
 464       * @return  integer 1 if the update worked, -1 otherwise
 465       */
 466      function update()
 467      {
 468          $blackout_start = $_POST['blackout_start']['Hour'] . ':' . $_POST['blackout_start']['Minute'] . ':00';
 469          $blackout_end = $_POST['blackout_end']['Hour'] . ':' . $_POST['blackout_end']['Minute'] . ':00';
 470          $stmt = "UPDATE
 471                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_round_robin
 472                   SET
 473                      prr_prj_id=" . Misc::escapeInteger($_POST["project"]) . ",
 474                      prr_blackout_start='" . Misc::escapeString($blackout_start) . "',
 475                      prr_blackout_end='" . Misc::escapeString($blackout_end) . "'
 476                   WHERE
 477                      prr_id=" . Misc::escapeInteger($_POST["id"]);
 478          $res = $GLOBALS["db_api"]->dbh->query($stmt);
 479          if (PEAR::isError($res)) {
 480              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 481              return -1;
 482          } else {
 483              // remove all of the associations with users, then add them all again
 484              Round_Robin::removeUserAssociations($_POST['id']);
 485              foreach ($_POST['users'] as $usr_id) {
 486                  Round_Robin::addUserAssociation($_POST['id'], $usr_id);
 487              }
 488              return 1;
 489          }
 490      }
 491  
 492  
 493      /**
 494       * Method used to remove the user associations for a given round robin
 495       * entry ID.
 496       *
 497       * @access  public
 498       * @param   integer $prr_id The round robin ID
 499       * @return  boolean
 500       */
 501      function removeUserAssociations($prr_id)
 502      {
 503          if (!is_array($prr_id)) {
 504              $prr_id = array($prr_id);
 505          }
 506          $items = @implode(", ", Misc::escapeInteger($prr_id));
 507          $stmt = "DELETE FROM
 508                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "round_robin_user
 509                   WHERE
 510                      rru_prr_id IN ($items)";
 511          $res = $GLOBALS["db_api"]->dbh->query($stmt);
 512          if (PEAR::isError($res)) {
 513              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 514              return false;
 515          } else {
 516              return true;
 517          }
 518      }
 519  
 520  
 521      /**
 522       * Method used to remove a round robin entry from the system.
 523       *
 524       * @access  public
 525       * @return  boolean
 526       */
 527      function remove()
 528      {
 529          $items = @implode(", ", Misc::escapeInteger($_POST["items"]));
 530          $stmt = "DELETE FROM
 531                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_round_robin
 532                   WHERE
 533                      prr_id IN ($items)";
 534          $res = $GLOBALS["db_api"]->dbh->query($stmt);
 535          if (PEAR::isError($res)) {
 536              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 537              return false;
 538          } else {
 539              Round_Robin::removeUserAssociations($_POST['items']);
 540              return true;
 541          }
 542      }
 543  }
 544  
 545  // benchmarking the included file (aka setup time)
 546  if (APP_BENCHMARK) {
 547      $GLOBALS['bench']->setMarker('Included Round_Robin Class');
 548  }


Generated: Wed Dec 19 21:21:33 2007 Cross-referenced by PHPXref 0.7