public function renderBurn() { $request = $this->getRequest(); $user = $request->getUser(); $handle = null; $project_phid = $request->getStr('project'); if ($project_phid) { $phids = array($project_phid); $handles = $this->loadViewerHandles($phids); $handle = $handles[$project_phid]; } $table = new ManiphestTransaction(); $conn = $table->establishConnection('r'); $joins = ''; if ($project_phid) { $joins = qsprintf($conn, 'JOIN %T t ON x.taskID = t.id JOIN %T p ON p.taskPHID = t.phid AND p.projectPHID = %s', id(new ManiphestTask())->getTableName(), id(new ManiphestTaskProject())->getTableName(), $project_phid); } $data = queryfx_all($conn, 'SELECT x.oldValue, x.newValue, x.dateCreated FROM %T x %Q WHERE transactionType = %s ORDER BY x.dateCreated ASC', $table->getTableName(), $joins, ManiphestTransactionType::TYPE_STATUS); $stats = array(); $day_buckets = array(); $open_tasks = array(); foreach ($data as $key => $row) { // NOTE: Hack to avoid json_decode(). $oldv = trim($row['oldValue'], '"'); $newv = trim($row['newValue'], '"'); $old_is_open = $oldv === (string) ManiphestTaskStatus::STATUS_OPEN; $new_is_open = $newv === (string) ManiphestTaskStatus::STATUS_OPEN; $is_open = $new_is_open && !$old_is_open; $is_close = $old_is_open && !$new_is_open; $data[$key]['_is_open'] = $is_open; $data[$key]['_is_close'] = $is_close; if (!$is_open && !$is_close) { // This is either some kind of bogus event, or a resolution change // (e.g., resolved -> invalid). Just skip it. continue; } $day_bucket = phabricator_format_local_time($row['dateCreated'], $user, 'Yz'); $day_buckets[$day_bucket] = $row['dateCreated']; if (empty($stats[$day_bucket])) { $stats[$day_bucket] = array('open' => 0, 'close' => 0); } $stats[$day_bucket][$is_close ? 'close' : 'open']++; } $template = array('open' => 0, 'close' => 0); $rows = array(); $rowc = array(); $last_month = null; $last_month_epoch = null; $last_week = null; $last_week_epoch = null; $week = null; $month = null; $last = last_key($stats) - 1; $period = $template; foreach ($stats as $bucket => $info) { $epoch = $day_buckets[$bucket]; $week_bucket = phabricator_format_local_time($epoch, $user, 'YW'); if ($week_bucket != $last_week) { if ($week) { $rows[] = $this->formatBurnRow('Week of ' . phabricator_date($last_week_epoch, $user), $week); $rowc[] = 'week'; } $week = $template; $last_week = $week_bucket; $last_week_epoch = $epoch; } $month_bucket = phabricator_format_local_time($epoch, $user, 'Ym'); if ($month_bucket != $last_month) { if ($month) { $rows[] = $this->formatBurnRow(phabricator_format_local_time($last_month_epoch, $user, 'F, Y'), $month); $rowc[] = 'month'; } $month = $template; $last_month = $month_bucket; $last_month_epoch = $epoch; } $rows[] = $this->formatBurnRow(phabricator_date($epoch, $user), $info); $rowc[] = null; $week['open'] += $info['open']; $week['close'] += $info['close']; $month['open'] += $info['open']; $month['close'] += $info['close']; $period['open'] += $info['open']; $period['close'] += $info['close']; } if ($week) { $rows[] = $this->formatBurnRow('Week To Date', $week); $rowc[] = 'week'; } if ($month) { $rows[] = $this->formatBurnRow('Month To Date', $month); $rowc[] = 'month'; } $rows[] = $this->formatBurnRow('All Time', $period); $rowc[] = 'aggregate'; $rows = array_reverse($rows); $rowc = array_reverse($rowc); $table = new AphrontTableView($rows); $table->setRowClasses($rowc); $table->setHeaders(array('Period', 'Opened', 'Closed', 'Change')); $table->setColumnClasses(array('right wide', 'n', 'n', 'n')); if ($handle) { $header = "Task Burn Rate for Project " . $handle->renderLink(); $caption = "<p>NOTE: This table reflects tasks <em>currently</em> in " . "the project. If a task was opened in the past but added to " . "the project recently, it is counted on the day it was " . "opened, not the day it was categorized. If a task was part " . "of this project in the past but no longer is, it is not " . "counted at all.</p>"; } else { $header = "Task Burn Rate for All Tasks"; $caption = null; } $panel = new AphrontPanelView(); $panel->setHeader($header); $panel->setCaption($caption); $panel->appendChild($table); $tokens = array(); if ($handle) { $tokens = array($handle->getPHID() => $handle->getFullName()); } $filter = $this->renderReportFilters($tokens, $has_window = false); $id = celerity_generate_unique_node_id(); $chart = phutil_render_tag('div', array('id' => $id, 'style' => 'border: 1px solid #6f6f6f; ' . 'margin: 1em 2em; ' . 'height: 400px; '), ''); list($burn_x, $burn_y) = $this->buildSeries($data); require_celerity_resource('raphael-core'); require_celerity_resource('raphael-g'); require_celerity_resource('raphael-g-line'); Javelin::initBehavior('line-chart', array('hardpoint' => $id, 'x' => array($burn_x), 'y' => array($burn_y), 'xformat' => 'epoch')); return array($filter, $chart, $panel); }
<?php $table = new ManiphestTransaction(); $conn_w = $table->establishConnection('w'); echo pht("Converting Maniphest project transactions to modern edge transactions...\n"); $metadata = array('edge:type' => PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); foreach (new LiskMigrationIterator($table) as $txn) { if ($txn->getTransactionType() != 'projects') { continue; } $old_value = mig20141222_build_edge_data($txn->getOldValue(), $txn->getObjectPHID()); $new_value = mig20141222_build_edge_data($txn->getNewValue(), $txn->getObjectPHID()); queryfx($conn_w, 'UPDATE %T SET ' . 'transactionType = %s, oldValue = %s, newValue = %s, metaData = %s ' . 'WHERE id = %d', $table->getTableName(), PhabricatorTransactions::TYPE_EDGE, json_encode($old_value), json_encode($new_value), json_encode($metadata), $txn->getID()); } echo pht('Done.') . "\n"; function mig20141222_build_edge_data($project_phids, $task_phid) { $edge_data = array(); // See T9464. If we didn't get a proper array value out of the transaction, // just return an empty value so we can move forward. if (!is_array($project_phids)) { return $edge_data; } foreach ($project_phids as $project_phid) { if (!is_scalar($project_phid)) { continue; } $edge_data[$project_phid] = array('src' => $task_phid, 'type' => PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, 'dst' => $project_phid); } return $edge_data; }
public function renderBurn() { $request = $this->getRequest(); $viewer = $request->getUser(); $handle = null; $project_phid = $request->getStr('project'); if ($project_phid) { $phids = array($project_phid); $handles = $this->loadViewerHandles($phids); $handle = $handles[$project_phid]; } $table = new ManiphestTransaction(); $conn = $table->establishConnection('r'); $joins = ''; if ($project_phid) { $joins = qsprintf($conn, 'JOIN %T t ON x.objectPHID = t.phid JOIN %T p ON p.src = t.phid AND p.type = %d AND p.dst = %s', id(new ManiphestTask())->getTableName(), PhabricatorEdgeConfig::TABLE_NAME_EDGE, PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, $project_phid); } $data = queryfx_all($conn, 'SELECT x.oldValue, x.newValue, x.dateCreated FROM %T x %Q WHERE transactionType = %s ORDER BY x.dateCreated ASC', $table->getTableName(), $joins, ManiphestTransaction::TYPE_STATUS); $stats = array(); $day_buckets = array(); $open_tasks = array(); foreach ($data as $key => $row) { // NOTE: Hack to avoid json_decode(). $oldv = trim($row['oldValue'], '"'); $newv = trim($row['newValue'], '"'); if ($oldv == 'null') { $old_is_open = false; } else { $old_is_open = ManiphestTaskStatus::isOpenStatus($oldv); } $new_is_open = ManiphestTaskStatus::isOpenStatus($newv); $is_open = $new_is_open && !$old_is_open; $is_close = $old_is_open && !$new_is_open; $data[$key]['_is_open'] = $is_open; $data[$key]['_is_close'] = $is_close; if (!$is_open && !$is_close) { // This is either some kind of bogus event, or a resolution change // (e.g., resolved -> invalid). Just skip it. continue; } $day_bucket = phabricator_format_local_time($row['dateCreated'], $viewer, 'Yz'); $day_buckets[$day_bucket] = $row['dateCreated']; if (empty($stats[$day_bucket])) { $stats[$day_bucket] = array('open' => 0, 'close' => 0); } $stats[$day_bucket][$is_close ? 'close' : 'open']++; } $template = array('open' => 0, 'close' => 0); $rows = array(); $rowc = array(); $last_month = null; $last_month_epoch = null; $last_week = null; $last_week_epoch = null; $week = null; $month = null; $last = last_key($stats) - 1; $period = $template; foreach ($stats as $bucket => $info) { $epoch = $day_buckets[$bucket]; $week_bucket = phabricator_format_local_time($epoch, $viewer, 'YW'); if ($week_bucket != $last_week) { if ($week) { $rows[] = $this->formatBurnRow(pht('Week of %s', phabricator_date($last_week_epoch, $viewer)), $week); $rowc[] = 'week'; } $week = $template; $last_week = $week_bucket; $last_week_epoch = $epoch; } $month_bucket = phabricator_format_local_time($epoch, $viewer, 'Ym'); if ($month_bucket != $last_month) { if ($month) { $rows[] = $this->formatBurnRow(phabricator_format_local_time($last_month_epoch, $viewer, 'F, Y'), $month); $rowc[] = 'month'; } $month = $template; $last_month = $month_bucket; $last_month_epoch = $epoch; } $rows[] = $this->formatBurnRow(phabricator_date($epoch, $viewer), $info); $rowc[] = null; $week['open'] += $info['open']; $week['close'] += $info['close']; $month['open'] += $info['open']; $month['close'] += $info['close']; $period['open'] += $info['open']; $period['close'] += $info['close']; } if ($week) { $rows[] = $this->formatBurnRow(pht('Week To Date'), $week); $rowc[] = 'week'; } if ($month) { $rows[] = $this->formatBurnRow(pht('Month To Date'), $month); $rowc[] = 'month'; } $rows[] = $this->formatBurnRow(pht('All Time'), $period); $rowc[] = 'aggregate'; $rows = array_reverse($rows); $rowc = array_reverse($rowc); $table = new AphrontTableView($rows); $table->setRowClasses($rowc); $table->setHeaders(array(pht('Period'), pht('Opened'), pht('Closed'), pht('Change'))); $table->setColumnClasses(array('right wide', 'n', 'n', 'n')); if ($handle) { $inst = pht('NOTE: This table reflects tasks currently in ' . 'the project. If a task was opened in the past but added to ' . 'the project recently, it is counted on the day it was ' . 'opened, not the day it was categorized. If a task was part ' . 'of this project in the past but no longer is, it is not ' . 'counted at all.'); $header = pht('Task Burn Rate for Project %s', $handle->renderLink()); $caption = phutil_tag('p', array(), $inst); } else { $header = pht('Task Burn Rate for All Tasks'); $caption = null; } if ($caption) { $caption = id(new PHUIInfoView())->appendChild($caption)->setSeverity(PHUIInfoView::SEVERITY_NOTICE); } $panel = new PHUIObjectBoxView(); $panel->setHeaderText($header); if ($caption) { $panel->setInfoView($caption); } $panel->setTable($table); $tokens = array(); if ($handle) { $tokens = array($handle); } $filter = $this->renderReportFilters($tokens, $has_window = false); $id = celerity_generate_unique_node_id(); $chart = phutil_tag('div', array('id' => $id, 'style' => 'border: 1px solid #BFCFDA; ' . 'background-color: #fff; ' . 'margin: 8px 16px; ' . 'height: 400px; '), ''); list($burn_x, $burn_y) = $this->buildSeries($data); require_celerity_resource('d3'); require_celerity_resource('phui-chart-css'); Javelin::initBehavior('line-chart', array('hardpoint' => $id, 'x' => array($burn_x), 'y' => array($burn_y), 'xformat' => 'epoch', 'yformat' => 'int')); $box = id(new PHUIObjectBoxView())->setHeaderText(pht('Burnup Rate'))->appendChild($chart); return array($filter, $box, $panel); }
} echo "Done.\n"; } if ($purge_differential) { echo "Purging Differential comment cache...\n"; $table = new DifferentialComment(); queryfx($table->establishConnection('w'), 'UPDATE %T SET cache = NULL', $table->getTableName()); echo "Purging Differential inline comment cache...\n"; $table = new DifferentialInlineComment(); queryfx($table->establishConnection('w'), 'UPDATE %T SET cache = NULL', $table->getTableName()); echo "Done.\n"; } if ($purge_maniphest) { echo "Purging Maniphest comment cache...\n"; $table = new ManiphestTransaction(); queryfx($table->establishConnection('w'), 'UPDATE %T SET cache = NULL', $table->getTableName()); echo "Done.\n"; } echo "Ok, caches purged.\n"; function usage($message) { echo "Usage Error: {$message}"; echo "\n\n"; echo "Run 'purge_cache.php --help' for detailed help.\n"; exit(1); } function help() { $help = <<<EOHELP **SUMMARY**