@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Put all DifferentialComment loading behind DifferentialCommentQuery

Summary:
Ref T2222.

I'm thinking about how I want to approach the Asana sync, and I want to try to do T2222 first so that we can build it cleanly on top of ApplicationTransactions. I think we can at least walk down this road a little bit and if it turns out to be scary we can take another approach.

I was generally very happy with how the auth migration turned out (seemingly, it was almost completely clean), and want to pursue a similar strategy here. Basically:

- Wrap the new objects in the old objects for reads/writes.
- Migrate all the existing data to the new table.
- Everything hard is done; move things over a piece at a time at a leisurely pace in lots of smallish, relatively-easy-to-understand changes.

This deletes or abstracts all reads of the DifferentialComment table. In particular, these things are **deleted**:

- The script `undo_commits.php`, which I haven't pointed anyone at in a very long time.
- The `differential.getrevisionfeedback` Conduit method, which has been marked deprecated for a year or more.
- The `/stats/` interface in Differential, which should be rebuilt on Fact and has never been exposed in the UI. It does a ton of joins and such which are prohibitively difficult to migrate.

This leaves a small number of reading interfaces, which I replaced with a new `DifferentialCommentQuery`. Some future change will make this actually load transactions and wrap them with DifferentialComment interfaces.

Test Plan: Viewed a revision; made revision comments

Reviewers: btrahan

Reviewed By: btrahan

CC: edward, chad, aran

Maniphest Tasks: T2222

Differential Revision: https://secure.phabricator.com/D6260

+60 -755
-178
scripts/repository/undo_commits.php
··· 1 - #!/usr/bin/env php 2 - <?php 3 - 4 - $root = dirname(dirname(dirname(__FILE__))); 5 - require_once $root.'/scripts/__init_script__.php'; 6 - 7 - $args = new PhutilArgumentParser($argv); 8 - $args->setTagline('reopen reviews accidentally closed by a bad push'); 9 - $args->setSynopsis(<<<EOSYNOPSIS 10 - **undo_commits.php** --repository __callsign__ < __commits__ 11 - 12 - Reopen code reviews accidentally closed by a bad push. If someone 13 - pushes a bunch of commits to a tracked branch that they shouldn't 14 - have, you can pipe in all the commit hashes to this script to 15 - "undo" the damage in Differential after you revert the commits. 16 - 17 - To use this script: 18 - 19 - 1. Identify the commits you want to undo the effects of. 20 - 2. Put all their identifiers (commit hashes in git/hg, revision 21 - numbers in svn) into a file, one per line. 22 - 3. Pipe that file into this script with relevant arguments. 23 - 4. Revisions marked "closed" by those commits will be 24 - restored to their previous state. 25 - 26 - EOSYNOPSIS 27 - ); 28 - $args->parseStandardArguments(); 29 - $args->parse( 30 - array( 31 - array( 32 - 'name' => 'repository', 33 - 'param' => 'callsign', 34 - 'help' => 'Callsign for the repository these commits appear in.', 35 - ), 36 - )); 37 - 38 - $callsign = $args->getArg('repository'); 39 - if (!$callsign) { 40 - $args->printHelpAndExit(); 41 - } 42 - 43 - $repository = id(new PhabricatorRepository())->loadOneWhere( 44 - 'callsign = %s', 45 - $callsign); 46 - 47 - if (!$repository) { 48 - throw new Exception("No repository with callsign '{$callsign}'!"); 49 - } 50 - 51 - echo "Reading commit identifiers from stdin...\n"; 52 - 53 - $identifiers = @file_get_contents('php://stdin'); 54 - $identifiers = trim($identifiers); 55 - $identifiers = explode("\n", $identifiers); 56 - 57 - echo "Read ".count($identifiers)." commit identifiers.\n"; 58 - 59 - if (!$identifiers) { 60 - throw new Exception("You must provide commmit identifiers on stdin!"); 61 - } 62 - 63 - echo "Looking up commits...\n"; 64 - $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere( 65 - 'repositoryID = %d AND commitIdentifier IN (%Ls)', 66 - $repository->getID(), 67 - $identifiers); 68 - 69 - echo "Found ".count($commits)." matching commits.\n"; 70 - 71 - if (!$commits) { 72 - throw new Exception("None of the commits could be found!"); 73 - } 74 - 75 - $commit_phids = mpull($commits, 'getPHID', 'getPHID'); 76 - 77 - echo "Looking up revisions marked 'closed' by these commits...\n"; 78 - $revision_ids = queryfx_all( 79 - id(new DifferentialRevision())->establishConnection('r'), 80 - 'SELECT DISTINCT revisionID from %T WHERE commitPHID IN (%Ls)', 81 - DifferentialRevision::TABLE_COMMIT, 82 - $commit_phids); 83 - $revision_ids = ipull($revision_ids, 'revisionID'); 84 - 85 - echo "Found ".count($revision_ids)." associated revisions.\n"; 86 - if (!$revision_ids) { 87 - echo "Done -- nothing to do.\n"; 88 - return; 89 - } 90 - 91 - $status_closed = ArcanistDifferentialRevisionStatus::CLOSED; 92 - 93 - $revisions = array(); 94 - $map = array(); 95 - 96 - if ($revision_ids) { 97 - foreach ($revision_ids as $revision_id) { 98 - echo "Assessing revision D{$revision_id}...\n"; 99 - $revision = id(new DifferentialRevision())->load($revision_id); 100 - 101 - if ($revision->getStatus() != $status_closed) { 102 - echo "Revision is not 'closed', skipping.\n"; 103 - } 104 - 105 - $assoc_commits = queryfx_all( 106 - $revision->establishConnection('r'), 107 - 'SELECT commitPHID FROM %T WHERE revisionID = %d', 108 - DifferentialRevision::TABLE_COMMIT, 109 - $revision_id); 110 - $assoc_commits = ipull($assoc_commits, 'commitPHID', 'commitPHID'); 111 - 112 - if (array_diff_key($assoc_commits, $commit_phids)) { 113 - echo "Revision is associated with other commits, skipping.\n"; 114 - } 115 - 116 - $comments = id(new DifferentialComment())->loadAllWhere( 117 - 'revisionID = %d', 118 - $revision_id); 119 - 120 - $new_status = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW; 121 - foreach ($comments as $comment) { 122 - switch ($comment->getAction()) { 123 - case DifferentialAction::ACTION_ACCEPT: 124 - $new_status = ArcanistDifferentialRevisionStatus::ACCEPTED; 125 - break; 126 - case DifferentialAction::ACTION_REJECT: 127 - case DifferentialAction::ACTION_RETHINK: 128 - $new_status = ArcanistDifferentialRevisionStatus::NEEDS_REVISION; 129 - break; 130 - case DifferentialAction::ACTION_ABANDON: 131 - $new_status = ArcanistDifferentialRevisionStatus::ABANDONED; 132 - break; 133 - case DifferentialAction::ACTION_RECLAIM: 134 - case DifferentialAction::ACTION_UPDATE: 135 - $new_status = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW; 136 - break; 137 - } 138 - } 139 - 140 - $revisions[$revision_id] = $revision; 141 - $map[$revision_id] = $new_status; 142 - } 143 - } 144 - 145 - if (!$revisions) { 146 - echo "Done -- nothing to do.\n"; 147 - } 148 - 149 - echo "Found ".count($revisions)." revisions to update:\n\n"; 150 - foreach ($revisions as $id => $revision) { 151 - 152 - $old_status = ArcanistDifferentialRevisionStatus::getNameForRevisionStatus( 153 - $revision->getStatus()); 154 - $new_status = ArcanistDifferentialRevisionStatus::getNameForRevisionStatus( 155 - $map[$id]); 156 - 157 - echo " - D{$id}: ".$revision->getTitle()."\n"; 158 - echo " Will update: {$old_status} -> {$new_status}\n\n"; 159 - } 160 - 161 - $ok = phutil_console_confirm('Apply these changes?'); 162 - if (!$ok) { 163 - echo "Aborted.\n"; 164 - exit(1); 165 - } 166 - 167 - echo "Saving changes...\n"; 168 - foreach ($revisions as $id => $revision) { 169 - queryfx( 170 - $revision->establishConnection('r'), 171 - 'UPDATE %T SET status = %d WHERE id = %d', 172 - $revision->getTableName(), 173 - $map[$id], 174 - $id); 175 - } 176 - echo "Done.\n"; 177 - 178 -
+2 -7
src/__phutil_library_map__.php
··· 141 141 'ConduitAPI_differential_getdiff_Method' => 'applications/differential/conduit/ConduitAPI_differential_getdiff_Method.php', 142 142 'ConduitAPI_differential_getrevision_Method' => 'applications/differential/conduit/ConduitAPI_differential_getrevision_Method.php', 143 143 'ConduitAPI_differential_getrevisioncomments_Method' => 'applications/differential/conduit/ConduitAPI_differential_getrevisioncomments_Method.php', 144 - 'ConduitAPI_differential_getrevisionfeedback_Method' => 'applications/differential/conduit/ConduitAPI_differential_getrevisionfeedback_Method.php', 145 144 'ConduitAPI_differential_markcommitted_Method' => 'applications/differential/conduit/ConduitAPI_differential_markcommitted_Method.php', 146 145 'ConduitAPI_differential_parsecommitmessage_Method' => 'applications/differential/conduit/ConduitAPI_differential_parsecommitmessage_Method.php', 147 146 'ConduitAPI_differential_query_Method' => 'applications/differential/conduit/ConduitAPI_differential_query_Method.php', ··· 331 330 'DifferentialCommentEditor' => 'applications/differential/editor/DifferentialCommentEditor.php', 332 331 'DifferentialCommentMail' => 'applications/differential/mail/DifferentialCommentMail.php', 333 332 'DifferentialCommentPreviewController' => 'applications/differential/controller/DifferentialCommentPreviewController.php', 333 + 'DifferentialCommentQuery' => 'applications/differential/query/DifferentialCommentQuery.php', 334 334 'DifferentialCommentSaveController' => 'applications/differential/controller/DifferentialCommentSaveController.php', 335 335 'DifferentialCommitsFieldSpecification' => 'applications/differential/field/specification/DifferentialCommitsFieldSpecification.php', 336 336 'DifferentialConflictsFieldSpecification' => 'applications/differential/field/specification/DifferentialConflictsFieldSpecification.php', ··· 390 390 'DifferentialRevertPlanFieldSpecification' => 'applications/differential/field/specification/DifferentialRevertPlanFieldSpecification.php', 391 391 'DifferentialReviewRequestMail' => 'applications/differential/mail/DifferentialReviewRequestMail.php', 392 392 'DifferentialReviewedByFieldSpecification' => 'applications/differential/field/specification/DifferentialReviewedByFieldSpecification.php', 393 - 'DifferentialReviewerStats' => 'applications/differential/stats/DifferentialReviewerStats.php', 394 - 'DifferentialReviewerStatsTestCase' => 'applications/differential/stats/__tests__/DifferentialReviewerStatsTestCase.php', 395 393 'DifferentialReviewersFieldSpecification' => 'applications/differential/field/specification/DifferentialReviewersFieldSpecification.php', 396 394 'DifferentialRevision' => 'applications/differential/storage/DifferentialRevision.php', 397 395 'DifferentialRevisionCommentListView' => 'applications/differential/view/DifferentialRevisionCommentListView.php', ··· 408 406 'DifferentialRevisionListView' => 'applications/differential/view/DifferentialRevisionListView.php', 409 407 'DifferentialRevisionMailReceiver' => 'applications/differential/mail/DifferentialRevisionMailReceiver.php', 410 408 'DifferentialRevisionQuery' => 'applications/differential/query/DifferentialRevisionQuery.php', 411 - 'DifferentialRevisionStatsController' => 'applications/differential/controller/DifferentialRevisionStatsController.php', 412 409 'DifferentialRevisionStatsView' => 'applications/differential/view/DifferentialRevisionStatsView.php', 413 410 'DifferentialRevisionStatus' => 'applications/differential/constants/DifferentialRevisionStatus.php', 414 411 'DifferentialRevisionStatusFieldSpecification' => 'applications/differential/field/specification/DifferentialRevisionStatusFieldSpecification.php', ··· 2025 2022 'ConduitAPI_differential_getdiff_Method' => 'ConduitAPIMethod', 2026 2023 'ConduitAPI_differential_getrevision_Method' => 'ConduitAPIMethod', 2027 2024 'ConduitAPI_differential_getrevisioncomments_Method' => 'ConduitAPI_differential_Method', 2028 - 'ConduitAPI_differential_getrevisionfeedback_Method' => 'ConduitAPIMethod', 2029 2025 'ConduitAPI_differential_markcommitted_Method' => 'ConduitAPIMethod', 2030 2026 'ConduitAPI_differential_parsecommitmessage_Method' => 'ConduitAPIMethod', 2031 2027 'ConduitAPI_differential_query_Method' => 'ConduitAPIMethod', ··· 2210 2206 'DifferentialCommentEditor' => 'PhabricatorEditor', 2211 2207 'DifferentialCommentMail' => 'DifferentialMail', 2212 2208 'DifferentialCommentPreviewController' => 'DifferentialController', 2209 + 'DifferentialCommentQuery' => 'PhabricatorOffsetPagedQuery', 2213 2210 'DifferentialCommentSaveController' => 'DifferentialController', 2214 2211 'DifferentialCommitsFieldSpecification' => 'DifferentialFieldSpecification', 2215 2212 'DifferentialConflictsFieldSpecification' => 'DifferentialFieldSpecification', ··· 2268 2265 'DifferentialRevertPlanFieldSpecification' => 'DifferentialFieldSpecification', 2269 2266 'DifferentialReviewRequestMail' => 'DifferentialMail', 2270 2267 'DifferentialReviewedByFieldSpecification' => 'DifferentialFieldSpecification', 2271 - 'DifferentialReviewerStatsTestCase' => 'PhabricatorTestCase', 2272 2268 'DifferentialReviewersFieldSpecification' => 'DifferentialFieldSpecification', 2273 2269 'DifferentialRevision' => 2274 2270 array( ··· 2287 2283 'DifferentialRevisionListController' => 'DifferentialController', 2288 2284 'DifferentialRevisionListView' => 'AphrontView', 2289 2285 'DifferentialRevisionMailReceiver' => 'PhabricatorObjectMailReceiver', 2290 - 'DifferentialRevisionStatsController' => 'DifferentialController', 2291 2286 'DifferentialRevisionStatsView' => 'AphrontView', 2292 2287 'DifferentialRevisionStatusFieldSpecification' => 'DifferentialFieldSpecification', 2293 2288 'DifferentialRevisionUpdateHistoryView' => 'AphrontView',
-1
src/applications/differential/application/PhabricatorApplicationDifferential.php
··· 42 42 '' => 'DifferentialRevisionListController', 43 43 'filter/(?P<filter>\w+)/(?:(?P<username>[\w._-]+)/)?' => 44 44 'DifferentialRevisionListController', 45 - 'stats/(?P<filter>\w+)/' => 'DifferentialRevisionStatsController', 46 45 'diff/' => array( 47 46 '(?P<id>[1-9]\d*)/' => 'DifferentialDiffViewController', 48 47 'create/' => 'DifferentialDiffCreateController',
+3 -3
src/applications/differential/conduit/ConduitAPI_differential_getrevisioncomments_Method.php
··· 34 34 return $results; 35 35 } 36 36 37 - $comments = id(new DifferentialComment())->loadAllWhere( 38 - 'revisionID IN (%Ld)', 39 - $revision_ids); 37 + $comments = id(new DifferentialCommentQuery()) 38 + ->withRevisionIDs($revision_ids) 39 + ->execute(); 40 40 41 41 $with_inlines = $request->getValue('inlines'); 42 42 if ($with_inlines) {
-67
src/applications/differential/conduit/ConduitAPI_differential_getrevisionfeedback_Method.php
··· 1 - <?php 2 - 3 - /** 4 - * @group conduit 5 - */ 6 - final class ConduitAPI_differential_getrevisionfeedback_Method 7 - extends ConduitAPIMethod { 8 - 9 - public function getMethodStatus() { 10 - return self::METHOD_STATUS_DEPRECATED; 11 - } 12 - 13 - public function getMethodStatusDescription() { 14 - return "Replaced by 'differential.getrevisioncomments'."; 15 - } 16 - 17 - public function getMethodDescription() { 18 - return "Retrieve Differential Revision Feedback."; 19 - } 20 - 21 - public function defineParamTypes() { 22 - return array( 23 - 'ids' => 'required list<int>', 24 - ); 25 - } 26 - 27 - public function defineReturnType() { 28 - return 'nonempty list<dict<string, wild>>'; 29 - } 30 - 31 - public function defineErrorTypes() { 32 - return array( 33 - ); 34 - } 35 - 36 - protected function execute(ConduitAPIRequest $request) { 37 - $results = array(); 38 - $revision_ids = $request->getValue('ids'); 39 - 40 - if (!$revision_ids) { 41 - return $results; 42 - } 43 - 44 - $comments = id(new DifferentialComment())->loadAllWhere( 45 - 'revisionID IN (%Ld)', 46 - $revision_ids); 47 - 48 - // Helper dictionary to keep track of where the id/action pair is 49 - // stored in results array. 50 - $indexes = array(); 51 - foreach ($comments as $comment) { 52 - $action = $comment->getAction(); 53 - $revision_id = $comment->getRevisionID(); 54 - 55 - if (isset($indexes[$action.$revision_id])) { 56 - $results[$indexes[$action.$revision_id]]['count']++; 57 - } else { 58 - $indexes[$action.$revision_id] = count($results); 59 - $results[] = array('id' => $revision_id, 60 - 'action' => $action, 61 - 'count' => 1); 62 - } 63 - } 64 - 65 - return $results; 66 - } 67 - }
-163
src/applications/differential/controller/DifferentialRevisionStatsController.php
··· 1 - <?php 2 - 3 - final class DifferentialRevisionStatsController extends DifferentialController { 4 - private $filter; 5 - 6 - private function loadRevisions($phid) { 7 - $table = new DifferentialRevision(); 8 - $conn_r = $table->establishConnection('r'); 9 - $rows = queryfx_all( 10 - $conn_r, 11 - 'SELECT revisions.* FROM %T revisions ' . 12 - 'JOIN %T comments ON comments.revisionID = revisions.id ' . 13 - 'JOIN (' . 14 - ' SELECT revisionID FROM %T WHERE objectPHID = %s ' . 15 - ' UNION ALL ' . 16 - ' SELECT id from differential_revision WHERE authorPHID = %s) rel ' . 17 - 'ON (comments.revisionID = rel.revisionID)' . 18 - 'WHERE comments.action = %s' . 19 - 'AND comments.authorPHID = %s', 20 - $table->getTableName(), 21 - id(new DifferentialComment())->getTableName(), 22 - DifferentialRevision::RELATIONSHIP_TABLE, 23 - $phid, 24 - $phid, 25 - $this->filter, 26 - $phid); 27 - return $table->loadAllFromArray($rows); 28 - } 29 - 30 - private function loadComments($phid) { 31 - $table = new DifferentialComment(); 32 - $conn_r = $table->establishConnection('r'); 33 - $rows = queryfx_all( 34 - $conn_r, 35 - 'SELECT comments.* FROM %T comments ' . 36 - 'JOIN (' . 37 - ' SELECT revisionID FROM %T WHERE objectPHID = %s ' . 38 - ' UNION ALL ' . 39 - ' SELECT id from differential_revision WHERE authorPHID = %s) rel ' . 40 - 'ON (comments.revisionID = rel.revisionID)' . 41 - 'WHERE comments.action = %s' . 42 - 'AND comments.authorPHID = %s', 43 - $table->getTableName(), 44 - DifferentialRevision::RELATIONSHIP_TABLE, 45 - $phid, 46 - $phid, 47 - $this->filter, 48 - $phid); 49 - 50 - return $table->loadAllFromArray($rows); 51 - } 52 - 53 - private function loadDiffs(array $revisions) { 54 - if (!$revisions) { 55 - return array(); 56 - } 57 - 58 - $diff_teml = new DifferentialDiff(); 59 - $diffs = $diff_teml->loadAllWhere( 60 - 'revisionID in (%Ld)', 61 - array_keys($revisions)); 62 - return $diffs; 63 - } 64 - 65 - public function willProcessRequest(array $data) { 66 - $this->filter = idx($data, 'filter'); 67 - } 68 - 69 - public function processRequest() { 70 - $request = $this->getRequest(); 71 - $user = $request->getUser(); 72 - 73 - if ($request->isFormPost()) { 74 - $phid_arr = $request->getArr('view_user'); 75 - $view_target = head($phid_arr); 76 - return id(new AphrontRedirectResponse()) 77 - ->setURI($request->getRequestURI()->alter('phid', $view_target)); 78 - } 79 - 80 - $params = array_filter( 81 - array( 82 - 'phid' => $request->getStr('phid'), 83 - )); 84 - 85 - // Fill in the defaults we'll actually use for calculations if any 86 - // parameters are missing. 87 - $params += array( 88 - 'phid' => $user->getPHID(), 89 - ); 90 - 91 - $side_nav = new AphrontSideNavFilterView(); 92 - $side_nav->setBaseURI(id(new PhutilURI('/differential/stats/')) 93 - ->alter('phid', $params['phid'])); 94 - foreach (array( 95 - DifferentialAction::ACTION_CLOSE, 96 - DifferentialAction::ACTION_ACCEPT, 97 - DifferentialAction::ACTION_REJECT, 98 - DifferentialAction::ACTION_UPDATE, 99 - DifferentialAction::ACTION_COMMENT, 100 - ) as $action) { 101 - $verb = ucfirst(DifferentialAction::getActionPastTenseVerb($action)); 102 - $side_nav->addFilter($action, $verb); 103 - } 104 - $this->filter = 105 - $side_nav->selectFilter($this->filter, 106 - DifferentialAction::ACTION_CLOSE); 107 - 108 - $panels = array(); 109 - $handles = $this->loadViewerHandles(array($params['phid'])); 110 - 111 - $filter_form = id(new AphrontFormView()) 112 - ->setAction('/differential/stats/'.$this->filter.'/') 113 - ->setUser($user); 114 - 115 - $filter_form->appendChild( 116 - $this->renderControl($params['phid'], $handles)); 117 - $filter_form->appendChild(id(new AphrontFormSubmitControl()) 118 - ->setValue(pht('Filter Revisions'))); 119 - 120 - $side_nav->appendChild($filter_form); 121 - 122 - $comments = $this->loadComments($params['phid']); 123 - $revisions = $this->loadRevisions($params['phid']); 124 - $diffs = $this->loadDiffs($revisions); 125 - 126 - $panel = new AphrontPanelView(); 127 - $panel->setHeader(pht('Differential rate analysis')); 128 - $panel->appendChild( 129 - id(new DifferentialRevisionStatsView()) 130 - ->setComments($comments) 131 - ->setFilter($this->filter) 132 - ->setRevisions($revisions) 133 - ->setDiffs($diffs) 134 - ->setUser($user)); 135 - $panels[] = $panel; 136 - 137 - foreach ($panels as $panel) { 138 - $side_nav->appendChild($panel); 139 - } 140 - 141 - return $this->buildStandardPageResponse( 142 - $side_nav, 143 - array( 144 - 'title' => pht('Differential Statistics'), 145 - )); 146 - } 147 - 148 - private function renderControl($view_phid, $handles) { 149 - $value = array(); 150 - if ($view_phid) { 151 - $value = array( 152 - $view_phid => $handles[$view_phid]->getFullName(), 153 - ); 154 - } 155 - return id(new AphrontFormTokenizerControl()) 156 - ->setDatasource('/typeahead/common/users/') 157 - ->setLabel(pht('View User')) 158 - ->setName('view_user') 159 - ->setValue($value) 160 - ->setLimit(1); 161 - } 162 - 163 - }
+43
src/applications/differential/query/DifferentialCommentQuery.php
··· 1 + <?php 2 + 3 + /** 4 + * Temporary wrapper for transitioning Differential to ApplicationTransactions. 5 + */ 6 + final class DifferentialCommentQuery 7 + extends PhabricatorOffsetPagedQuery { 8 + 9 + private $revisionIDs; 10 + 11 + public function withRevisionIDs(array $ids) { 12 + $this->revisionIDs = $ids; 13 + return $this; 14 + } 15 + 16 + public function execute() { 17 + $table = new DifferentialComment(); 18 + $conn_r = $table->establishConnection('r'); 19 + 20 + $data = queryfx_all( 21 + $conn_r, 22 + 'SELECT * FROM %T %Q %Q', 23 + $table->getTableName(), 24 + $this->buildWhereClause($conn_r), 25 + $this->buildLimitClause($conn_r)); 26 + 27 + return $table->loadAllFromArray($data); 28 + } 29 + 30 + private function buildWhereClause(AphrontDatabaseConnection $conn_r) { 31 + $where = array(); 32 + 33 + if ($this->revisionIDs) { 34 + $where[] = qsprintf( 35 + $conn_r, 36 + 'revisionID IN (%Ld)', 37 + $this->revisionIDs); 38 + } 39 + 40 + return $this->formatWhereClause($where); 41 + } 42 + 43 + }
+3 -1
src/applications/differential/search/DifferentialSearchIndexer.php
··· 53 53 time()); 54 54 } 55 55 56 - $comments = $rev->loadRelatives(new DifferentialComment(), 'revisionID'); 56 + $comments = id(new DifferentialCommentQuery()) 57 + ->withRevisionIDs(array($rev->getID())) 58 + ->execute(); 57 59 58 60 $inlines = $rev->loadRelatives( 59 61 new DifferentialInlineComment(),
-226
src/applications/differential/stats/DifferentialReviewerStats.php
··· 1 - <?php 2 - 3 - final class DifferentialReviewerStats { 4 - private $since = 0; 5 - private $until; 6 - 7 - public function setSince($value) { 8 - $this->since = $value; 9 - return $this; 10 - } 11 - 12 - public function setUntil($value) { 13 - $this->until = $value; 14 - return $this; 15 - } 16 - 17 - /** 18 - * @return array($reviewed, $not_reviewed) 19 - */ 20 - public function computeTimes( 21 - DifferentialRevision $revision, 22 - array $comments) { 23 - assert_instances_of($comments, 'DifferentialComment'); 24 - 25 - $add_rev = DifferentialComment::METADATA_ADDED_REVIEWERS; 26 - $rem_rev = DifferentialComment::METADATA_REMOVED_REVIEWERS; 27 - 28 - $date = $revision->getDateCreated(); 29 - 30 - // Find out original reviewers. 31 - $reviewers = array_fill_keys($revision->getReviewers(), $date); 32 - foreach (array_reverse($comments) as $comment) { 33 - $metadata = $comment->getMetadata(); 34 - foreach (idx($metadata, $add_rev, array()) as $phid) { 35 - unset($reviewers[$phid]); 36 - } 37 - foreach (idx($metadata, $rem_rev, array()) as $phid) { 38 - $reviewers[$phid] = $date; 39 - } 40 - } 41 - 42 - $reviewed = array(); 43 - $not_reviewed = array(); 44 - $status = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW; 45 - 46 - foreach ($comments as $comment) { 47 - $date = $comment->getDateCreated(); 48 - $old_status = $status; 49 - 50 - switch ($comment->getAction()) { 51 - case DifferentialAction::ACTION_UPDATE: 52 - if ($status != ArcanistDifferentialRevisionStatus::CLOSED && 53 - $status != ArcanistDifferentialRevisionStatus::ACCEPTED) { 54 - $status = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW; 55 - } 56 - break; 57 - case DifferentialAction::ACTION_REQUEST: 58 - case DifferentialAction::ACTION_RECLAIM: 59 - $status = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW; 60 - break; 61 - case DifferentialAction::ACTION_REJECT: 62 - case DifferentialAction::ACTION_RETHINK: 63 - $status = ArcanistDifferentialRevisionStatus::NEEDS_REVISION; 64 - break; 65 - case DifferentialAction::ACTION_ACCEPT: 66 - $status = ArcanistDifferentialRevisionStatus::ACCEPTED; 67 - break; 68 - case DifferentialAction::ACTION_CLOSE: 69 - $status = ArcanistDifferentialRevisionStatus::CLOSED; 70 - break; 71 - case DifferentialAction::ACTION_ABANDON: 72 - $status = ArcanistDifferentialRevisionStatus::ABANDONED; 73 - break; 74 - } 75 - 76 - // Update current reviewers. 77 - $metadata = $comment->getMetadata(); 78 - foreach (idx($metadata, $add_rev, array()) as $phid) { 79 - // If someone reviewed a revision without being its reviewer then give 80 - // him zero response time. 81 - $reviewers[$phid] = $date; 82 - } 83 - foreach (idx($metadata, $rem_rev, array()) as $phid) { 84 - $start = idx($reviewers, $phid); 85 - if ($start !== null) { 86 - if ($date >= $this->since) { 87 - $reviewed[$phid][] = $date - $start; 88 - } 89 - unset($reviewers[$phid]); 90 - } 91 - } 92 - 93 - // TODO: Respect workdays and status away. 94 - 95 - if ($old_status != $status) { 96 - if ($status == ArcanistDifferentialRevisionStatus::NEEDS_REVIEW) { 97 - $reviewers = array_fill_keys(array_keys($reviewers), $date); 98 - } else if ($date >= $this->since) { 99 - if ($old_status == ArcanistDifferentialRevisionStatus::NEEDS_REVIEW) { 100 - foreach ($reviewers as $phid => $start) { 101 - if ($phid == $comment->getAuthorPHID()) { 102 - $reviewed[$phid][] = $date - $start; 103 - } else { 104 - $not_reviewed[$phid][] = $date - $start; 105 - } 106 - } 107 - } 108 - } 109 - } 110 - } 111 - 112 - if ($status == ArcanistDifferentialRevisionStatus::NEEDS_REVIEW) { 113 - $date = ($this->until !== null ? $this->until : time()); 114 - if ($date >= $this->since) { 115 - foreach ($reviewers as $phid => $start) { 116 - $not_reviewed[$phid][] = $date - $start; 117 - } 118 - } 119 - } 120 - 121 - return array($reviewed, $not_reviewed); 122 - } 123 - 124 - public function loadAvgs() { 125 - $limit = 1000; 126 - $conn_r = id(new DifferentialRevision())->establishConnection('r'); 127 - 128 - $sums = array(); 129 - $counts = array(); 130 - $all_not_reviewed = array(); 131 - 132 - $last_id = 0; 133 - do { 134 - $where = ''; 135 - if ($this->until !== null) { 136 - $where .= qsprintf( 137 - $conn_r, 138 - ' AND dateCreated < %d', 139 - $this->until); 140 - } 141 - if ($this->since) { 142 - $where .= qsprintf( 143 - $conn_r, 144 - ' AND (dateModified > %d OR status = %s)', 145 - $this->since, 146 - ArcanistDifferentialRevisionStatus::NEEDS_REVIEW); 147 - } 148 - $revisions = id(new DifferentialRevision())->loadAllWhere( 149 - 'id > %d%Q ORDER BY id LIMIT %d', 150 - $last_id, 151 - $where, 152 - $limit); 153 - 154 - if (!$revisions) { 155 - break; 156 - } 157 - $last_id = last_key($revisions); 158 - 159 - $relations = queryfx_all( 160 - $conn_r, 161 - 'SELECT * FROM %T WHERE revisionID IN (%Ld) AND relation = %s', 162 - DifferentialRevision::RELATIONSHIP_TABLE, 163 - array_keys($revisions), 164 - DifferentialRevision::RELATION_REVIEWER); 165 - $relations = igroup($relations, 'revisionID'); 166 - 167 - $where = ''; 168 - if ($this->until !== null) { 169 - $where = qsprintf( 170 - $conn_r, 171 - ' AND dateCreated < %d', 172 - $this->until); 173 - } 174 - $all_comments = id(new DifferentialComment())->loadAllWhere( 175 - 'revisionID IN (%Ld)%Q ORDER BY revisionID, id', 176 - array_keys($revisions), 177 - $where); 178 - $all_comments = mgroup($all_comments, 'getRevisionID'); 179 - 180 - foreach ($revisions as $id => $revision) { 181 - $revision->attachRelationships(idx($relations, $id, array())); 182 - $comments = idx($all_comments, $id, array()); 183 - 184 - list($reviewed, $not_reviewed) = 185 - $this->computeTimes($revision, $comments); 186 - 187 - foreach ($reviewed as $phid => $times) { 188 - $sums[$phid] = idx($sums, $phid, 0) + array_sum($times); 189 - $counts[$phid] = idx($counts, $phid, 0) + count($times); 190 - } 191 - 192 - foreach ($not_reviewed as $phid => $times) { 193 - $all_not_reviewed[$phid][] = $times; 194 - } 195 - } 196 - } while (count($revisions) >= $limit); 197 - 198 - foreach ($all_not_reviewed as $phid => $not_reviewed) { 199 - if (!array_key_exists($phid, $counts)) { 200 - // If the person didn't make any reviews than take maximum time because 201 - // he is at least that slow. 202 - $sums[$phid] = max(array_map('max', $not_reviewed)); 203 - $counts[$phid] = 1; 204 - continue; 205 - } 206 - $avg = $sums[$phid] / $counts[$phid]; 207 - foreach ($not_reviewed as $times) { 208 - foreach ($times as $time) { 209 - // Don't shorten the average time just because the reviewer was lucky 210 - // to be in a group with someone faster. 211 - if ($time > $avg) { 212 - $sums[$phid] += $time; 213 - $counts[$phid]++; 214 - } 215 - } 216 - } 217 - } 218 - 219 - $avgs = array(); 220 - foreach ($sums as $phid => $sum) { 221 - $avgs[$phid] = $sum / $counts[$phid]; 222 - } 223 - return $avgs; 224 - } 225 - 226 - }
-100
src/applications/differential/stats/__tests__/DifferentialReviewerStatsTestCase.php
··· 1 - <?php 2 - 3 - final class DifferentialReviewerStatsTestCase extends PhabricatorTestCase { 4 - 5 - public function testReviewerStats() { 6 - $revision = new DifferentialRevision(); 7 - $revision->setDateCreated(1); 8 - $revision->attachRelationships(array( 9 - $this->newReviewer('R1'), 10 - $this->newReviewer('R3'), 11 - )); 12 - 13 - $comments = array( 14 - $this->newComment(2, 'A', DifferentialAction::ACTION_COMMENT), 15 - $this->newComment(4, 'A', DifferentialAction::ACTION_ADDREVIEWERS, 16 - array(DifferentialComment::METADATA_ADDED_REVIEWERS => array('R3'))), 17 - $this->newComment(8, 'R1', DifferentialAction::ACTION_REJECT), 18 - $this->newComment(16, 'A', DifferentialAction::ACTION_COMMENT), 19 - $this->newComment(32, 'A', DifferentialAction::ACTION_UPDATE), 20 - $this->newComment(64, 'A', DifferentialAction::ACTION_UPDATE), 21 - $this->newComment(128, 'A', DifferentialAction::ACTION_COMMENT), 22 - $this->newComment(256, 'R2', DifferentialAction::ACTION_RESIGN, 23 - array(DifferentialComment::METADATA_REMOVED_REVIEWERS => array('R2'))), 24 - $this->newComment(512, 'R3', DifferentialAction::ACTION_ACCEPT), 25 - $this->newComment(1024, 'A', DifferentialAction::ACTION_UPDATE), 26 - // TODO: claim, abandon, reclaim 27 - ); 28 - 29 - $stats = new DifferentialReviewerStats(); 30 - list($reviewed, $not_reviewed) = $stats->computeTimes($revision, $comments); 31 - 32 - ksort($reviewed); 33 - $this->assertEqual( 34 - array( 35 - 'R1' => array(8 - 1), 36 - 'R2' => array(256 - 32), 37 - 'R3' => array(512 - 32), 38 - ), 39 - $reviewed); 40 - 41 - ksort($not_reviewed); 42 - $this->assertEqual( 43 - array( 44 - 'R1' => array(512 - 32), 45 - 'R2' => array(8 - 1), 46 - 'R3' => array(8 - 4), 47 - ), 48 - $not_reviewed); 49 - } 50 - 51 - public function testReviewerStatsSince() { 52 - $revision = new DifferentialRevision(); 53 - $revision->setDateCreated(1); 54 - $revision->attachRelationships(array($this->newReviewer('R'))); 55 - 56 - $comments = array( 57 - $this->newComment(2, 'R', DifferentialAction::ACTION_REJECT), 58 - $this->newComment(4, 'A', DifferentialAction::ACTION_REQUEST), 59 - $this->newComment(8, 'R', DifferentialAction::ACTION_ACCEPT), 60 - ); 61 - 62 - $stats = new DifferentialReviewerStats(); 63 - $stats->setSince(4); 64 - list($reviewed, $not_reviewed) = $stats->computeTimes($revision, $comments); 65 - 66 - $this->assertEqual(array('R' => array(8 - 4)), $reviewed); 67 - $this->assertEqual(array(), $not_reviewed); 68 - } 69 - 70 - public function testReviewerStatsRequiresReview() { 71 - $revision = new DifferentialRevision(); 72 - $revision->setDateCreated(1); 73 - $revision->attachRelationships(array($this->newReviewer('R'))); 74 - 75 - $comments = array(); 76 - 77 - $stats = new DifferentialReviewerStats(); 78 - $stats->setUntil(2); 79 - list($reviewed, $not_reviewed) = $stats->computeTimes($revision, $comments); 80 - 81 - $this->assertEqual(array(), $reviewed); 82 - $this->assertEqual(array('R' => array(2 - 1)), $not_reviewed); 83 - } 84 - 85 - private function newReviewer($phid) { 86 - return array( 87 - 'relation' => DifferentialRevision::RELATION_REVIEWER, 88 - 'objectPHID' => $phid, 89 - ); 90 - } 91 - 92 - private function newComment($date, $author, $action, $metadata = array()) { 93 - return id(new DifferentialComment()) 94 - ->setDateCreated($date) 95 - ->setAuthorPHID($author) 96 - ->setAction($action) 97 - ->setMetadata($metadata); 98 - } 99 - 100 - }
+6 -6
src/applications/differential/storage/DifferentialRevision.php
··· 153 153 if (!$this->getID()) { 154 154 return array(); 155 155 } 156 - return id(new DifferentialComment())->loadAllWhere( 157 - 'revisionID = %d', 158 - $this->getID()); 156 + return id(new DifferentialCommentQuery()) 157 + ->withRevisionIDs(array($this->getID())) 158 + ->execute(); 159 159 } 160 160 161 161 public function loadActiveDiff() { ··· 192 192 self::TABLE_COMMIT, 193 193 $this->getID()); 194 194 195 - $comments = id(new DifferentialComment())->loadAllWhere( 196 - 'revisionID = %d', 197 - $this->getID()); 195 + $comments = id(new DifferentialCommentQuery()) 196 + ->withRevisionIDs(array($this->getID())) 197 + ->execute(); 198 198 foreach ($comments as $comment) { 199 199 $comment->delete(); 200 200 }
+3 -3
src/applications/releeph/field/specification/ReleephDiffChurnFieldSpecification.php
··· 19 19 } 20 20 21 21 $diff_rev = $this->getReleephRequest()->loadDifferentialRevision(); 22 - $comments = $diff_rev->loadRelatives( 23 - new DifferentialComment(), 24 - 'revisionID'); 22 + $comments = id(new DifferentialRevisionQuery()) 23 + ->withRevisionIDs(array($diff_rev->getID())) 24 + ->excute(); 25 25 26 26 $counts = array(); 27 27 foreach ($comments as $comment) {