[ Index ] |
PHP Cross Reference of Eventum |
[Summary view] [Print] [Text view]
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.report.php 3510 2007-12-17 19:56:20Z balsdorf $ 29 // 30 31 require_once (APP_INC_PATH . "class.error_handler.php"); 32 require_once (APP_INC_PATH . "class.misc.php"); 33 require_once (APP_INC_PATH . "class.user.php"); 34 require_once (APP_INC_PATH . "class.date.php"); 35 require_once (APP_INC_PATH . "class.status.php"); 36 require_once (APP_INC_PATH . "class.history.php"); 37 require_once (APP_INC_PATH . "class.phone_support.php"); 38 require_once (APP_INC_PATH . "class.prefs.php"); 39 require_once(APP_PEAR_PATH . "Math/Stats.php"); 40 41 /** 42 * Class to handle the business logic related to all aspects of the 43 * reporting system. 44 * 45 * @version 1.0 46 * @author João Prado Maia <jpm@mysql.com> 47 */ 48 49 class Report 50 { 51 52 53 /** 54 * Method used to get all open issues and group them by user. 55 * 56 * @access public 57 * @param integer $prj_id The project ID 58 * @return array The list of issues 59 */ 60 function getStalledIssuesByUser($prj_id, $users, $status, $before_date, $after_date, $sort_order) 61 { 62 $prj_id = Misc::escapeInteger($prj_id); 63 $ts = Date_API::getCurrentUnixTimestampGMT(); 64 $before_ts = strtotime($before_date); 65 $after_ts = strtotime($after_date); 66 67 // split groups out of users array 68 $groups = array(); 69 foreach ($users as $key => $value) { 70 if (substr($value, 0, 3) == 'grp') { 71 $groups[] = substr($value, 4); 72 unset($users[$key]); 73 } 74 } 75 76 $stmt = "SELECT 77 usr_full_name, 78 iss_id, 79 iss_summary, 80 sta_title, 81 iss_sta_id, 82 iss_created_date, 83 iss_updated_date, 84 iss_last_response_date, 85 sta_color, 86 iss_private 87 FROM 88 ( 89 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue, 90 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user, 91 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user 92 ) 93 LEFT JOIN 94 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status 95 ON 96 iss_sta_id=sta_id 97 WHERE 98 sta_is_closed=0 AND 99 iss_prj_id=$prj_id AND 100 iss_id=isu_iss_id AND 101 isu_usr_id=usr_id AND 102 UNIX_TIMESTAMP(iss_last_response_date) < $before_ts AND 103 UNIX_TIMESTAMP(iss_last_response_date) > $after_ts"; 104 if (count($users) > 0) { 105 $stmt .= " AND\nisu_usr_id IN(" . join(', ', Misc::escapeInteger($users)) . ")"; 106 } 107 if (count($groups) > 0) { 108 $stmt .= " AND\nusr_grp_id IN(" . join(', ', Misc::escapeInteger($groups)) . ")"; 109 } 110 if (count($status) > 0) { 111 $stmt .= " AND\niss_sta_id IN(" . join(', ', Misc::escapeInteger($status)) . ")"; 112 } 113 $stmt .= " 114 ORDER BY 115 usr_full_name, 116 iss_last_response_date " . Misc::escapeString($sort_order); 117 $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); 118 if (PEAR::isError($res)) { 119 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 120 return ""; 121 } else { 122 Time_Tracking::getTimeSpentByIssues($res); 123 $issues = array(); 124 for ($i = 0; $i < count($res); $i++) { 125 if (empty($res[$i]['iss_updated_date'])) { 126 $res[$i]['iss_updated_date'] = $res[$i]['iss_created_date']; 127 } 128 if (empty($res[$i]['iss_last_response_date'])) { 129 $res[$i]['iss_last_response_date'] = $res[$i]['iss_created_date']; 130 } 131 $issues[$res[$i]['usr_full_name']][$res[$i]['iss_id']] = array( 132 'iss_summary' => $res[$i]['iss_summary'], 133 'sta_title' => $res[$i]['sta_title'], 134 'iss_created_date' => Date_API::getFormattedDate($res[$i]['iss_created_date']), 135 'iss_last_response_date' => Date_API::getFormattedDate($res[$i]['iss_last_response_date']), 136 'time_spent' => Misc::getFormattedTime($res[$i]['time_spent']), 137 'status_color' => $res[$i]['sta_color'], 138 'last_update' => Date_API::getFormattedDateDiff($ts, Date_API::getUnixTimestamp($res[$i]['iss_updated_date'], Date_API::getDefaultTimezone())), 139 'last_email_response' => Date_API::getFormattedDateDiff($ts, Date_API::getUnixTimestamp($res[$i]['iss_last_response_date'], Date_API::getDefaultTimezone())) 140 ); 141 } 142 return $issues; 143 } 144 } 145 146 /** 147 * Method used to get all open issues and group them by assignee or reporter. 148 * 149 * @access public 150 * @param integer $prj_id The project ID 151 * @param integer $cutoff_days The number of days to use as a cutoff period 152 * @return array The list of issues 153 */ 154 function getOpenIssuesByUser($prj_id, $cutoff_days, $group_by_reporter = false) 155 { 156 $prj_id = Misc::escapeInteger($prj_id); 157 $cutoff_days = Misc::escapeInteger($cutoff_days); 158 $ts = Date_API::getCurrentUnixTimestampGMT(); 159 $ts_diff = $cutoff_days * DAY; 160 161 162 $stmt = "SELECT 163 assignee.usr_full_name as assignee_name, 164 reporter.usr_full_name as reporter_name, 165 iss_id, 166 iss_summary, 167 sta_title, 168 iss_sta_id, 169 iss_created_date, 170 iss_updated_date, 171 iss_last_response_date, 172 sta_color 173 FROM 174 ( 175 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue, 176 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user, 177 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user as assignee, 178 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user as reporter 179 ) 180 LEFT JOIN 181 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status 182 ON 183 iss_sta_id=sta_id 184 WHERE 185 sta_is_closed=0 AND 186 iss_prj_id=$prj_id AND 187 iss_id=isu_iss_id AND 188 isu_usr_id=assignee.usr_id AND 189 iss_usr_id=reporter.usr_id AND 190 UNIX_TIMESTAMP(iss_created_date) < (UNIX_TIMESTAMP() - $ts_diff) 191 ORDER BY\n"; 192 if ($group_by_reporter) { 193 $stmt .= "reporter.usr_full_name"; 194 } else { 195 $stmt .= "assignee.usr_full_name"; 196 } 197 $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); 198 if (PEAR::isError($res)) { 199 print_r($res); 200 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 201 return ""; 202 } else { 203 Time_Tracking::getTimeSpentByIssues($res); 204 $issues = array(); 205 for ($i = 0; $i < count($res); $i++) { 206 if (empty($res[$i]['iss_updated_date'])) { 207 $res[$i]['iss_updated_date'] = $res[$i]['iss_created_date']; 208 } 209 if (empty($res[$i]['iss_last_response_date'])) { 210 $res[$i]['iss_last_response_date'] = $res[$i]['iss_created_date']; 211 } 212 if ($group_by_reporter) { 213 $name = $res[$i]['reporter_name']; 214 } else { 215 $name = $res[$i]['assignee_name']; 216 } 217 $issues[$name][$res[$i]['iss_id']] = array( 218 'iss_summary' => $res[$i]['iss_summary'], 219 'sta_title' => $res[$i]['sta_title'], 220 'iss_created_date' => Date_API::getFormattedDate($res[$i]['iss_created_date']), 221 'time_spent' => Misc::getFormattedTime($res[$i]['time_spent']), 222 'status_color' => $res[$i]['sta_color'], 223 'last_update' => Date_API::getFormattedDateDiff($ts, Date_API::getUnixTimestamp($res[$i]['iss_updated_date'], Date_API::getDefaultTimezone())), 224 'last_email_response' => Date_API::getFormattedDateDiff($ts, Date_API::getUnixTimestamp($res[$i]['iss_last_response_date'], Date_API::getDefaultTimezone())) 225 ); 226 } 227 return $issues; 228 } 229 } 230 231 232 /** 233 * Method used to get the list of issues in a project, and group 234 * them by the assignee. 235 * 236 * @access public 237 * @param integer $prj_id The project ID 238 * @return array The list of issues 239 */ 240 function getIssuesByUser($prj_id) 241 { 242 $stmt = "SELECT 243 usr_full_name, 244 iss_id, 245 iss_summary, 246 sta_title, 247 iss_sta_id, 248 iss_created_date, 249 sta_color 250 FROM 251 ( 252 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue, 253 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user, 254 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user 255 ) 256 LEFT JOIN 257 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status 258 ON 259 iss_sta_id=sta_id 260 WHERE 261 iss_prj_id=" . Misc::escapeInteger($prj_id) . " AND 262 iss_id=isu_iss_id AND 263 isu_usr_id=usr_id 264 ORDER BY 265 usr_full_name"; 266 $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); 267 if (PEAR::isError($res)) { 268 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 269 return ""; 270 } else { 271 Time_Tracking::getTimeSpentByIssues($res); 272 $issues = array(); 273 for ($i = 0; $i < count($res); $i++) { 274 $issues[$res[$i]['usr_full_name']][$res[$i]['iss_id']] = array( 275 'iss_summary' => $res[$i]['iss_summary'], 276 'sta_title' => $res[$i]['sta_title'], 277 'iss_created_date' => Date_API::getFormattedDate($res[$i]['iss_created_date']), 278 'time_spent' => Misc::getFormattedTime($res[$i]['time_spent']), 279 'status_color' => $res[$i]['sta_color'] 280 ); 281 } 282 return $issues; 283 } 284 } 285 286 287 /** 288 * Returns the data used by the weekly report. 289 * 290 * @access public 291 * @param string $usr_id The ID of the user this report is for. 292 * @param string The start date of this report. 293 * @param string The end date of this report. 294 * @param boolean If closed issues should be separated from other issues. 295 * @param boolean If issue status changes should be ignored in report. 296 * @return array An array of data containing all the elements of the weekly report. 297 */ 298 function getWeeklyReport($usr_id, $start, $end, $separate_closed = false, $ignore_statuses = false) 299 { 300 $prj_id = Auth::getCurrentProject(); 301 $usr_id = Misc::escapeInteger($usr_id); 302 303 // figure out timezone 304 $user_prefs = Prefs::get($usr_id); 305 $tz = @$user_prefs["timezone"]; 306 307 $start_dt = new Date(); 308 $end_dt = new Date(); 309 // set timezone to that of user. 310 $start_dt->setTZById($tz); 311 $end_dt->setTZById($tz); 312 313 // set the dates in the users time zone 314 $start_dt->setDate($start . " 00:00:00"); 315 $end_dt->setDate($end . " 23:59:59"); 316 317 // convert time to GMT 318 $start_dt->toUTC(); 319 $end_dt->toUTC(); 320 321 $start_ts = $start_dt->getDate(); 322 $end_ts = $end_dt->getDate(); 323 324 $time_tracking = Time_Tracking::getSummaryByUser($usr_id, $start_ts, $end_ts); 325 326 // replace spaces in index with _ and calculate total time 327 $total_time = 0; 328 foreach ($time_tracking as $category => $data) { 329 unset($time_tracking[$category]); 330 $time_tracking[str_replace(" ", "_", $category)] = $data; 331 $total_time += $data["total_time"]; 332 } 333 334 // get count of issues assigned in week of report. 335 $stmt = "SELECT 336 COUNT(*) 337 FROM 338 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue, 339 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user, 340 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status 341 WHERE 342 iss_id = isu_iss_id AND 343 iss_sta_id = sta_id AND 344 isu_usr_id = $usr_id AND 345 iss_prj_id = " . Auth::getCurrentProject() . " AND 346 isu_assigned_date BETWEEN '$start_ts' AND '$end_ts'"; 347 $newly_assigned = $GLOBALS["db_api"]->dbh->getOne($stmt); 348 if (PEAR::isError($newly_assigned)) { 349 Error_Handler::logError(array($newly_assigned->getMessage(), $newly_assigned->getDebugInfo()), __FILE__, __LINE__); 350 } 351 352 $email_count = array( 353 "associated" => Support::getSentEmailCountByUser($usr_id, $start_ts, $end_ts, true), 354 "other" => Support::getSentEmailCountByUser($usr_id, $start_ts, $end_ts, false) 355 ); 356 357 $htt_exclude = array(); 358 if ($ignore_statuses) { 359 $htt_exclude[] = 'status_changed'; 360 $htt_exclude[] = 'status_auto_changed'; 361 $htt_exclude[] = 'remote_status_change'; 362 } 363 364 $data = array( 365 "start" => str_replace('-', '.', $start), 366 "end" => str_replace('-', '.', $end), 367 "user" => User::getDetails($usr_id), 368 "group_name"=> Group::getName(User::getGroupID($usr_id)), 369 "issues" => History::getTouchedIssuesByUser($usr_id, $start_ts, $end_ts, $separate_closed, $htt_exclude), 370 "status_counts" => History::getTouchedIssueCountByStatus($usr_id, $start_ts, $end_ts), 371 "new_assigned_count" => $newly_assigned, 372 "time_tracking" => $time_tracking, 373 "email_count" => $email_count, 374 "phone_count" => Phone_Support::getCountByUser($usr_id, $start_ts, $end_ts), 375 "note_count" => Note::getCountByUser($usr_id, $start_ts, $end_ts), 376 "total_time" => Misc::getFormattedTime($total_time, false) 377 ); 378 379 return $data; 380 } 381 382 383 /** 384 * Returns data used by the workload by time period report. 385 * 386 * @access public 387 * @param string $timezone Timezone to display time in in addition to GMT 388 * @param boolean $graph If the data should be formatted for use in a graph. Default false 389 * @return array An array of data. 390 */ 391 function getWorkloadByTimePeriod($timezone, $graph = false) 392 { 393 $stmt = "SELECT 394 count(*) as events, 395 hour(his_created_date) AS time_period, 396 if (pru_role > 3, 'developer', 'customer') as performer, 397 SUM(if (pru_role > 3, 1, 0)) as dev_events, 398 SUM(if (pru_role > 3, 0, 1)) as cust_events 399 FROM 400 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_history, 401 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user, 402 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_user 403 WHERE 404 his_usr_id = usr_id AND 405 usr_id = pru_usr_id AND 406 pru_prj_id = " . Auth::getCurrentProject() . " 407 GROUP BY 408 time_period, performer 409 ORDER BY 410 time_period"; 411 $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); 412 if (PEAR::isError($res)) { 413 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 414 return array(); 415 } 416 // get total number of developer and customer events 417 $event_count = array( 418 "developer" => 0, 419 "customer" => 0 420 ); 421 foreach ($res as $row) { 422 $event_count["developer"] += $row["dev_events"]; 423 $event_count["customer"] += $row["cust_events"]; 424 } 425 426 $data = array(); 427 $sort_values = array(); 428 for ($i = 0; $i < 24; $i++) { 429 430 // convert to the users time zone 431 $dt = new Date(mktime($i,0,0)); 432 $gmt_time = $dt->format('%H:%M'); 433 $dt->convertTZbyID($timezone); 434 if ($graph) { 435 $data["developer"][$dt->format('%H')] = ""; 436 $data["customer"][$dt->format('%H')] = ""; 437 } else { 438 $data[$i]["display_time_gmt"] = $gmt_time; 439 $data[$i]["display_time_user"] = $dt->format('%H:%M'); 440 } 441 442 // loop through results, assigning appropriate results to data array 443 foreach ($res as $index => $row) { 444 if ($row["time_period"] == $i) { 445 $sort_values[$row["performer"]][$i] = $row["events"]; 446 447 if ($graph) { 448 $data[$row["performer"]][$dt->format('%H')] = (($row["events"] / $event_count[$row["performer"]]) * 100); 449 } else { 450 $data[$i][$row["performer"]]["count"] = $row["events"]; 451 $data[$i][$row["performer"]]["percentage"] = (($row["events"] / $event_count[$row["performer"]]) * 100); 452 } 453 unset($res[$index]); 454 } 455 } 456 } 457 458 if (!$graph) { 459 // get the highest action times 460 foreach ($sort_values as $performer => $values) { 461 arsort($values); 462 reset($values); 463 $data[key($values)][$performer]["rank"] = 1; 464 } 465 } 466 467 return $data; 468 } 469 470 471 /** 472 * Returns data on when support emails are sent/recieved. 473 * 474 * @access public 475 * @param string $timezone Timezone to display time in in addition to GMT 476 * @param boolean $graph If the data should be formatted for use in a graph. Default false 477 * @return array An array of data. 478 */ 479 function getEmailWorkloadByTimePeriod($timezone, $graph = false) 480 { 481 // get total counts 482 $stmt = "SELECT 483 hour(sup_date) AS time_period, 484 count(*) as events 485 FROM 486 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email 487 GROUP BY 488 time_period"; 489 $total = $GLOBALS["db_api"]->dbh->getAssoc($stmt); 490 if (PEAR::isError($total)) { 491 Error_Handler::logError(array($total->getMessage(), $total->getDebugInfo()), __FILE__, __LINE__); 492 return array(); 493 } 494 495 // get all developer email addresses 496 $users = User::getActiveAssocList(Auth::getCurrentProject(), User::getRoleID("customer")); 497 $emails = array(); 498 foreach ($users as $usr_id => $usr_full_name) { 499 $emails[] = Misc::escapeString(User::getFromHeader($usr_id)); 500 } 501 502 // get number of support emails from developers 503 $stmt = "SELECT 504 hour(sup_date) AS time_period, 505 count(*) as events 506 FROM 507 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email 508 WHERE 509 sup_from IN('" . join("','", $emails) . "') 510 GROUP BY 511 time_period"; 512 $dev_stats = $GLOBALS["db_api"]->dbh->getAssoc($stmt); 513 if (PEAR::isError($dev_stats)) { 514 Error_Handler::logError(array($dev_stats->getMessage(), $dev_stats->getDebugInfo()), __FILE__, __LINE__); 515 return array(); 516 } 517 518 // get total number of developer and customer events and build cust_stats array 519 $dev_count = 0; 520 $cust_count = 0; 521 $cust_stats = array(); 522 for ($i = 0; $i < 24; $i++) { 523 if (empty($dev_stats[$i])) { 524 $dev_stats[$i] = 0; 525 } 526 $cust_stats[$i] = (@$total[$i] - @$dev_stats[$i]); 527 $cust_count += (@$total[$i] - @$dev_stats[$i]); 528 $dev_count += @$dev_stats[$i]; 529 } 530 531 $data = array(); 532 $sort_values = array(); 533 for ($i = 0; $i < 24; $i++) { 534 535 // convert to the users time zone 536 $dt = new Date(mktime($i,0,0)); 537 $gmt_time = $dt->format('%H:%M'); 538 $dt->convertTZbyID($timezone); 539 if ($graph) { 540 $data["developer"][$dt->format('%H')] = ""; 541 $data["customer"][$dt->format('%H')] = ""; 542 } else { 543 $data[$i]["display_time_gmt"] = $gmt_time; 544 $data[$i]["display_time_user"] = $dt->format('%H:%M'); 545 } 546 547 // use later to find highest value 548 $sort_values["developer"][$i] = $dev_stats[$i]; 549 $sort_values["customer"][$i] = $cust_stats[$i]; 550 551 if ($graph) { 552 if ($dev_count == 0) { 553 $data["developer"][$dt->format('%H')] = 0; 554 } else { 555 $data["developer"][$dt->format('%H')] = (($dev_stats[$i] / $dev_count) * 100); 556 } 557 if ($cust_count == 0) { 558 $data["customer"][$dt->format('%H')] = 0; 559 } else { 560 $data["customer"][$dt->format('%H')] = (($cust_stats[$i] / $cust_count) * 100); 561 } 562 } else { 563 $data[$i]["developer"]["count"] = $dev_stats[$i]; 564 if ($dev_count == 0){ 565 $data[$i]["developer"]["percentage"] = 0; 566 } else { 567 $data[$i]["developer"]["percentage"] = (($dev_stats[$i] / $dev_count) * 100); 568 } 569 $data[$i]["customer"]["count"] = $cust_stats[$i]; 570 if ($cust_count == 0) { 571 $data[$i]["customer"]["percentage"] = 0; 572 } else { 573 $data[$i]["customer"]["percentage"] = (($cust_stats[$i] / $cust_count) * 100); 574 } 575 } 576 } 577 578 if (!$graph) { 579 // get the highest action times 580 foreach ($sort_values as $performer => $values) { 581 arsort($values); 582 reset($values); 583 $data[key($values)][$performer]["rank"] = 1; 584 } 585 } 586 587 return $data; 588 } 589 590 591 /** 592 * Returns data for the custom fields report, based on the field and options passed in. 593 * 594 * @access public 595 * @param integer $fld_id The id of the custom field. 596 * @param array $cfo_ids An array of option ids. 597 * @param string $group_by How the data should be grouped. 598 * @param string $start_date 599 * @param string $end_date 600 * @param boolean $list If the values should be listed out instead of just counted. 601 * @param string $interval The interval values should be grouped over time, empty (none) by default. 602 * @param integer $assignee The assignee the issue should belong to. 603 * @return array An array of data. 604 */ 605 function getCustomFieldReport($fld_id, $cfo_ids, $group_by = "issue", $start_date, $end_date, $list = false, $interval = '', $assignee = false) 606 { 607 $prj_id = Auth::getCurrentProject(); 608 $fld_id = Misc::escapeInteger($fld_id); 609 $cfo_ids = Misc::escapeInteger($cfo_ids); 610 611 // get field values 612 $options = Custom_Field::getOptions($fld_id, $cfo_ids); 613 614 if ($group_by == "customer") { 615 $group_by_field = "iss_customer_id"; 616 } else { 617 $group_by_field = "iss_id"; 618 } 619 620 if ($assignee == -1) { 621 $assignee = false; 622 } 623 624 $label_field = ''; 625 $interval_group_by_field = ''; 626 switch ($interval) { 627 case "day": 628 $label_field = "CONCAT(YEAR(iss_created_date), '-', MONTH(iss_created_date), '-', DAY(iss_created_date))"; 629 $interval_group_by_field = "CONCAT(YEAR(iss_created_date), MONTH(iss_created_date), DAY(iss_created_date))"; 630 break; 631 case "week": 632 $label_field = "CONCAT(YEAR(iss_created_date), '/', WEEK(iss_created_date))"; 633 $interval_group_by_field = "WEEK(iss_created_date)"; 634 break; 635 case "month": 636 $label_field = "CONCAT(YEAR(iss_created_date), '/', MONTH(iss_created_date))"; 637 $interval_group_by_field = "MONTH(iss_created_date)"; 638 break; 639 case "year": 640 $label_field = "YEAR(iss_created_date)"; 641 $interval_group_by_field = "YEAR(iss_created_date)"; 642 break; 643 } 644 645 if ($list == true) { 646 $sql = "SELECT 647 DISTINCT($group_by_field), 648 iss_id, 649 iss_summary, 650 iss_customer_id, 651 count(DISTINCT(iss_id)) as row_count, 652 iss_private, 653 " . Custom_Field::getDBValueFieldSQL() . " as field_value"; 654 if ($label_field != '') { 655 $sql .= ", 656 $label_field as interval_label"; 657 } 658 $sql .= " 659 FROM 660 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field,"; 661 if (count($options) > 0) { 662 $sql .= " 663 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field_option,"; 664 } 665 $sql .= " 666 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field, 667 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue, 668 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user 669 WHERE 670 fld_id = icf_fld_id AND"; 671 if (count($options) > 0) { 672 $sql .= 673 "cfo_id = icf_value AND"; 674 } 675 $sql .= " 676 icf_iss_id = iss_id AND 677 isu_iss_id = iss_id AND 678 icf_fld_id = $fld_id"; 679 if (count($options) > 0) { 680 $sql .= " AND 681 cfo_id IN('" . join("','", Misc::escapeString(array_keys($options))) . "')"; 682 } 683 if (($start_date != false) && ($end_date != false)) { 684 $sql .= " AND\niss_created_date BETWEEN '" . Misc::escapeString($start_date) . "' AND '" . Misc::escapeString($end_date) . "'"; 685 } 686 if ($assignee != false) { 687 $sql .= " AND\nisu_usr_id = " . Misc::escapeInteger($assignee); 688 } 689 $sql .= " 690 GROUP BY 691 $group_by_field 692 ORDER BY"; 693 if ($label_field != '') { 694 $sql .= " 695 $label_field DESC,"; 696 } 697 $sql .= " 698 row_count DESC"; 699 $res = $GLOBALS["db_api"]->dbh->getAll($sql, DB_FETCHMODE_ASSOC); 700 if (PEAR::isError($res)) { 701 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 702 return array(); 703 } 704 if (Customer::hasCustomerIntegration($prj_id)) { 705 Customer::getCustomerTitlesByIssues($prj_id, $res); 706 if ($group_by == "issue") { 707 usort($res, create_function('$a,$b', 'if ($a["customer_title"] < $b["customer_title"]) { 708 return -1; 709 } elseif ($a["customer_title"] > $b["customer_title"]) { 710 return 1; 711 } else { 712 return 0; 713 }')); 714 } 715 } 716 return $res; 717 } 718 719 $data = array(); 720 foreach ($options as $cfo_id => $value) { 721 $stmt = "SELECT"; 722 if ($label_field != '') { 723 $stmt .= " 724 $label_field as label,"; 725 } 726 $stmt .= " 727 COUNT(DISTINCT $group_by_field) 728 FROM 729 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field, 730 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue, 731 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user 732 WHERE 733 icf_iss_id = iss_id AND 734 isu_iss_id = iss_id AND 735 icf_fld_id = $fld_id AND 736 icf_value = '$cfo_id'"; 737 if (($start_date != false) && ($end_date != false)) { 738 $stmt .= " AND\niss_created_date BETWEEN '" . Misc::escapeString($start_date) . "' AND '" . Misc::escapeString($end_date) . "'"; 739 } 740 if ($assignee != false) { 741 $sql .= " AND\nisu_usr_id = " . Misc::escapeInteger($assignee); 742 } 743 if ($interval_group_by_field != '') { 744 $stmt .= " 745 GROUP BY 746 $interval_group_by_field 747 ORDER BY 748 $label_field ASC"; 749 $res = $GLOBALS['db_api']->dbh->getAssoc($stmt); 750 } else { 751 $res = $GLOBALS["db_api"]->dbh->getOne($stmt); 752 } 753 if (PEAR::isError($res)) { 754 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 755 return array(); 756 } 757 $data[$value] = $res; 758 } 759 760 // include count of all other values (used in pie chart) 761 $stmt = "SELECT 762 COUNT(DISTINCT $group_by_field) 763 FROM 764 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field_option, 765 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field, 766 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue 767 WHERE 768 cfo_id = icf_value AND 769 icf_iss_id = iss_id AND 770 icf_fld_id = $fld_id AND 771 cfo_id NOT IN(" . join(",", $cfo_ids) . ")"; 772 $res = $GLOBALS["db_api"]->dbh->getOne($stmt); 773 if (PEAR::isError($res)) { 774 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 775 return array(); 776 } 777 $data["All Others"] = $res; 778 779 return $data; 780 } 781 782 783 /** 784 * Returns workload information for the specified date range and interval. 785 * 786 * @access public 787 * @param string $interval The interval to use in this report. 788 * @param string $type If this report is aggregate or individual 789 * @param string $start The start date of this report. 790 * @param string $end The end date of this report. 791 * @param integer $category The category to restrict this report to 792 * @return array An array containing workload data. 793 */ 794 function getWorkloadByDateRange($interval, $type, $start, $end, $category) 795 { 796 $data = array(); 797 $start = Misc::escapeString($start); 798 $end = Misc::escapeString($end); 799 $category = Misc::escapeInteger($category); 800 801 // figure out the correct format code 802 switch ($interval) { 803 case "day": 804 $format = '%m/%d/%y'; 805 $order_by = "%1\$s"; 806 break; 807 case "dow": 808 $format = '%W'; 809 $order_by = "IF(DATE_FORMAT(%1\$s, '%%w') = 0, 7, DATE_FORMAT(%1\$s, '%%w'))"; 810 break; 811 case "week": 812 if ($type == "aggregate") { 813 $format = '%v'; 814 } else { 815 $format = '%v/%y'; 816 } 817 $order_by = "%1\$s"; 818 break; 819 case "dom": 820 $format = '%d'; 821 break; 822 case "month": 823 if ($type == "aggregate") { 824 $format = '%b'; 825 $order_by = "DATE_FORMAT(%1\$s, '%%m')"; 826 } else { 827 $format = '%b/%y'; 828 $order_by = "%1\$s"; 829 } 830 break; 831 } 832 833 // get issue counts 834 $stmt = "SELECT 835 DATE_FORMAT(iss_created_date, '$format'), 836 count(*) 837 FROM 838 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue 839 WHERE 840 iss_prj_id=" . Auth::getCurrentProject() . " AND 841 iss_created_date BETWEEN '$start' AND '$end'"; 842 if (!empty($category)) { 843 $stmt .= " AND 844 iss_prc_id = $category"; 845 } 846 $stmt .= " 847 GROUP BY 848 DATE_FORMAT(iss_created_date, '$format')"; 849 if (!empty($order_by)) { 850 $stmt .= "\nORDER BY " . sprintf($order_by, 'iss_created_date'); 851 } 852 $res = $GLOBALS["db_api"]->dbh->getAssoc($stmt); 853 if (PEAR::isError($res)) { 854 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 855 return array(); 856 } 857 $data["issues"]["points"] = $res; 858 859 if (count($res) > 0) { 860 $stats = new Math_Stats(); 861 $stats->setData($res); 862 863 $data["issues"]["stats"] = array( 864 "total" => $stats->sum(), 865 "avg" => $stats->mean(), 866 "median" => $stats->median(), 867 "max" => $stats->max() 868 ); 869 } else { 870 $data["issues"]["stats"] = array( 871 "total" => 0, 872 "avg" => 0, 873 "median" => 0, 874 "max" => 0 875 ); 876 } 877 878 879 // get email counts 880 $stmt = "SELECT 881 DATE_FORMAT(sup_date, '$format'), 882 count(*) 883 FROM 884 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email, 885 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "email_account"; 886 if (!empty($category)) { 887 $stmt .= ", 888 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue"; 889 } 890 $stmt .= " 891 WHERE 892 sup_ema_id=ema_id AND 893 ema_prj_id=" . Auth::getCurrentProject() . " AND 894 sup_date BETWEEN '$start' AND '$end'"; 895 if (!empty($category)) { 896 $stmt .= " AND 897 sup_iss_id = iss_id AND 898 iss_prc_id = $category"; 899 } 900 $stmt .= " 901 GROUP BY 902 DATE_FORMAT(sup_date, '$format')"; 903 if (!empty($order_by)) { 904 $stmt .= "\nORDER BY " . sprintf($order_by, 'sup_date'); 905 } 906 $res = $GLOBALS["db_api"]->dbh->getAssoc($stmt); 907 if (PEAR::isError($res)) { 908 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 909 return array(); 910 } 911 $data["emails"]["points"] = $res; 912 913 if (count($res) > 0) { 914 $stats = new Math_Stats(); 915 $stats->setData($res); 916 917 $data["emails"]["stats"] = array( 918 "total" => $stats->sum(), 919 "avg" => $stats->mean(), 920 "median" => $stats->median(), 921 "max" => $stats->max() 922 ); 923 } else { 924 $data["emails"]["stats"] = array( 925 "total" => 0, 926 "avg" => 0, 927 "median" => 0, 928 "max" => 0 929 ); 930 } 931 932 933 return $data; 934 } 935 } 936 937 // benchmarking the included file (aka setup time) 938 if (APP_BENCHMARK) { 939 $GLOBALS['bench']->setMarker('Included Report Class'); 940 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Dec 19 21:21:33 2007 | Cross-referenced by PHPXref 0.7 |