Page MenuHomePhabricator

D19120.id.diff
No OneTemporary

D19120.id.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2588,6 +2588,7 @@
'PhabricatorCoreCreateTransaction' => 'applications/transactions/xaction/PhabricatorCoreCreateTransaction.php',
'PhabricatorCoreTransactionType' => 'applications/transactions/xaction/PhabricatorCoreTransactionType.php',
'PhabricatorCoreVoidTransaction' => 'applications/transactions/xaction/PhabricatorCoreVoidTransaction.php',
+ 'PhabricatorCountFact' => 'applications/fact/fact/PhabricatorCountFact.php',
'PhabricatorCountdown' => 'applications/countdown/storage/PhabricatorCountdown.php',
'PhabricatorCountdownApplication' => 'applications/countdown/application/PhabricatorCountdownApplication.php',
'PhabricatorCountdownController' => 'applications/countdown/controller/PhabricatorCountdownController.php',
@@ -8078,6 +8079,7 @@
'PhabricatorCoreCreateTransaction' => 'PhabricatorCoreTransactionType',
'PhabricatorCoreTransactionType' => 'PhabricatorModularTransactionType',
'PhabricatorCoreVoidTransaction' => 'PhabricatorModularTransactionType',
+ 'PhabricatorCountFact' => 'PhabricatorFact',
'PhabricatorCountdown' => array(
'PhabricatorCountdownDAO',
'PhabricatorPolicyInterface',
diff --git a/src/applications/fact/controller/PhabricatorFactChartController.php b/src/applications/fact/controller/PhabricatorFactChartController.php
--- a/src/applications/fact/controller/PhabricatorFactChartController.php
+++ b/src/applications/fact/controller/PhabricatorFactChartController.php
@@ -16,6 +16,9 @@
$key_id = id(new PhabricatorFactKeyDimension())
->newDimensionID($fact->getKey());
+ if (!$key_id) {
+ return new Aphront404Response();
+ }
$table = $fact->newDatapoint();
$conn_r = $table->establishConnection('r');
diff --git a/src/applications/fact/daemon/PhabricatorFactDaemon.php b/src/applications/fact/daemon/PhabricatorFactDaemon.php
--- a/src/applications/fact/daemon/PhabricatorFactDaemon.php
+++ b/src/applications/fact/daemon/PhabricatorFactDaemon.php
@@ -70,20 +70,28 @@
$result = null;
$datapoints = array();
+ $count = 0;
foreach ($iterator as $key => $object) {
$phid = $object->getPHID();
$this->log(pht('Processing %s...', $phid));
- $datapoints[$phid] = $this->newDatapoints($object);
- if (count($datapoints) > 1024) {
+ $object_datapoints = $this->newDatapoints($object);
+ $count += count($object_datapoints);
+
+ $datapoints[$phid] = $object_datapoints;
+
+ if ($count > 1024) {
$this->updateDatapoints($datapoints);
$datapoints = array();
+ $count = 0;
}
+
$result = $key;
}
- if ($datapoints) {
+ if ($count) {
$this->updateDatapoints($datapoints);
$datapoints = array();
+ $count = 0;
}
return $result;
@@ -111,7 +119,6 @@
return;
}
-
$fact_keys = array();
$objects = array();
foreach ($map as $phid => $facts) {
@@ -129,9 +136,9 @@
}
$key_map = id(new PhabricatorFactKeyDimension())
- ->newDimensionMap(array_keys($fact_keys));
+ ->newDimensionMap(array_keys($fact_keys), true);
$object_map = id(new PhabricatorFactObjectDimension())
- ->newDimensionMap(array_keys($objects));
+ ->newDimensionMap(array_keys($objects), true);
$table = new PhabricatorFactIntDatapoint();
$conn = $table->establishConnection('w');
diff --git a/src/applications/fact/engine/PhabricatorFactEngine.php b/src/applications/fact/engine/PhabricatorFactEngine.php
--- a/src/applications/fact/engine/PhabricatorFactEngine.php
+++ b/src/applications/fact/engine/PhabricatorFactEngine.php
@@ -35,4 +35,8 @@
return $this->factMap[$key];
}
+ final protected function getViewer() {
+ return PhabricatorUser::getOmnipotentUser();
+ }
+
}
diff --git a/src/applications/fact/engine/PhabricatorFactManiphestTaskEngine.php b/src/applications/fact/engine/PhabricatorFactManiphestTaskEngine.php
--- a/src/applications/fact/engine/PhabricatorFactManiphestTaskEngine.php
+++ b/src/applications/fact/engine/PhabricatorFactManiphestTaskEngine.php
@@ -5,8 +5,77 @@
public function newFacts() {
return array(
+ id(new PhabricatorCountFact())
+ ->setKey('tasks.count.create'),
+
+ id(new PhabricatorCountFact())
+ ->setKey('tasks.open-count.create'),
+ id(new PhabricatorCountFact())
+ ->setKey('tasks.open-count.status'),
+
+ id(new PhabricatorCountFact())
+ ->setKey('tasks.count.create.project'),
+ id(new PhabricatorCountFact())
+ ->setKey('tasks.count.assign.project'),
+ id(new PhabricatorCountFact())
+ ->setKey('tasks.open-count.create.project'),
+ id(new PhabricatorCountFact())
+ ->setKey('tasks.open-count.status.project'),
+ id(new PhabricatorCountFact())
+ ->setKey('tasks.open-count.assign.project'),
+
+ id(new PhabricatorCountFact())
+ ->setKey('tasks.count.create.owner'),
+ id(new PhabricatorCountFact())
+ ->setKey('tasks.count.assign.owner'),
+ id(new PhabricatorCountFact())
+ ->setKey('tasks.open-count.create.owner'),
+ id(new PhabricatorCountFact())
+ ->setKey('tasks.open-count.status.owner'),
+ id(new PhabricatorCountFact())
+ ->setKey('tasks.open-count.assign.owner'),
+
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.points.create'),
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.points.score'),
+
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.open-points.create'),
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.open-points.status'),
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.open-points.score'),
+
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.points.create.project'),
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.points.assign.project'),
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.points.score.project'),
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.open-points.create.project'),
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.open-points.status.project'),
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.open-points.score.project'),
id(new PhabricatorPointsFact())
- ->setKey('tasks.count.open'),
+ ->setKey('tasks.open-points.assign.project'),
+
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.points.create.owner'),
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.points.assign.owner'),
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.points.score.owner'),
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.open-points.create.owner'),
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.open-points.status.owner'),
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.open-points.score.project'),
+ id(new PhabricatorPointsFact())
+ ->setKey('tasks.open-points.assign.owner'),
);
}
@@ -15,18 +84,319 @@
}
public function newDatapointsForObject(PhabricatorLiskDAO $object) {
+ $viewer = $this->getViewer();
+
+ $xaction_query = PhabricatorApplicationTransactionQuery::newQueryForObject(
+ $object);
+ $xactions = $xaction_query
+ ->setViewer($viewer)
+ ->withObjectPHIDs(array($object->getPHID()))
+ ->execute();
+
+ $xactions = msortv($xactions, 'newChronologicalSortVector');
+
+ $old_open = false;
+ $old_points = 0;
+ $old_owner = null;
+ $project_map = array();
+ $object_phid = $object->getPHID();
+ $is_create = true;
+
+ $specs = array();
$datapoints = array();
+ foreach ($xactions as $xaction_group) {
+ $add_projects = array();
+ $rem_projects = array();
+
+ $new_open = $old_open;
+ $new_points = $old_points;
+ $new_owner = $old_owner;
+
+ // TODO: Actually group concurrent transactions.
+ $xaction_group = array($xaction_group);
+
+ $group_epoch = last($xaction_group)->getDateCreated();
+ foreach ($xaction_group as $xaction) {
+ $old_value = $xaction->getOldValue();
+ $new_value = $xaction->getNewValue();
+ switch ($xaction->getTransactionType()) {
+ case ManiphestTaskStatusTransaction::TRANSACTIONTYPE:
+ $new_open = !ManiphestTaskStatus::isClosedStatus($new_value);
+ break;
+ case ManiphestTaskMergedIntoTransaction::TRANSACTIONTYPE:
+ // When a task is merged into another task, it is changed to a
+ // closed status without generating a separate status transaction.
+ $new_open = false;
+ break;
+ case ManiphestTaskPointsTransaction::TRANSACTIONTYPE:
+ $new_points = (int)$xaction->getNewValue();
+ break;
+ case ManiphestTaskOwnerTransaction::TRANSACTIONTYPE:
+ $new_owner = $xaction->getNewValue();
+ break;
+ case PhabricatorTransactions::TYPE_EDGE:
+ $edge_type = $xaction->getMetadataValue('edge:type');
+ switch ($edge_type) {
+ case PhabricatorProjectObjectHasProjectEdgeType::EDGECONST:
+ $record = PhabricatorEdgeChangeRecord::newFromTransaction(
+ $xaction);
+ $add_projects += array_fuse($record->getAddedPHIDs());
+ $rem_projects += array_fuse($record->getRemovedPHIDs());
+ break;
+ }
+ break;
+ }
+ }
+
+ // If a project was both added and removed, moot it.
+ $mix_projects = array_intersect_key($add_projects, $rem_projects);
+ $add_projects = array_diff_key($add_projects, $mix_projects);
+ $rem_projects = array_diff_key($rem_projects, $mix_projects);
+
+ $project_sets = array(
+ array(
+ 'phids' => $rem_projects,
+ 'scale' => -1,
+ ),
+ array(
+ 'phids' => $add_projects,
+ 'scale' => 1,
+ ),
+ );
+
+ if ($is_create) {
+ $action = 'create';
+ $action_points = $new_points;
+ } else {
+ $action = 'assign';
+ $action_points = $old_points;
+ }
+
+ foreach ($project_sets as $project_set) {
+ $scale = $project_set['scale'];
+ foreach ($project_set['phids'] as $project_phid) {
+ if ($old_open) {
+ $specs[] = array(
+ "tasks.open-count.{$action}.project",
+ 1 * $scale,
+ $project_phid,
+ );
+
+ $specs[] = array(
+ "tasks.open-points.{$action}.project",
+ $action_points * $scale,
+ $project_phid,
+ );
+ }
+
+ $specs[] = array(
+ "tasks.count.{$action}.project",
+ 1 * $scale,
+ $project_phid,
+ );
+
+ $specs[] = array(
+ "tasks.points.{$action}.project",
+ $action_points * $scale,
+ $project_phid,
+ );
+
+ if ($scale < 0) {
+ unset($project_map[$project_phid]);
+ } else {
+ $project_map[$project_phid] = $project_phid;
+ }
+ }
+ }
+
+ if ($new_owner !== $old_owner) {
+ $owner_sets = array(
+ array(
+ 'phid' => $old_owner,
+ 'scale' => -1,
+ ),
+ array(
+ 'phid' => $new_owner,
+ 'scale' => 1,
+ ),
+ );
+
+ foreach ($owner_sets as $owner_set) {
+ $owner_phid = $owner_set['phid'];
+ if ($owner_phid === null) {
+ continue;
+ }
+
+ if ($old_open) {
+ $specs[] = array(
+ "tasks.open-count.{$action}.owner",
+ 1 * $scale,
+ $owner_phid,
+ );
+
+ $specs[] = array(
+ "tasks.open-points.{$action}.owner",
+ $action_points * $scale,
+ $owner_phid,
+ );
+ }
+
+ $specs[] = array(
+ "tasks.count.{$action}.owner",
+ 1 * $scale,
+ $owner_phid,
+ );
+
+ $specs[] = array(
+ "tasks.points.{$action}.owner",
+ $action_points * $scale,
+ $owner_phid,
+ );
+ }
+
+ $old_owner = $new_owner;
+ }
+
+ if ($is_create) {
+ $specs[] = array(
+ 'tasks.count.create',
+ 1,
+ );
+ $specs[] = array(
+ 'tasks.points.create',
+ $new_points,
+ );
+
+ if ($new_open) {
+ $specs[] = array(
+ 'tasks.open-count.create',
+ 1,
+ );
+ $specs[] = array(
+ 'tasks.open-points.create',
+ $new_points,
+ );
+ }
+ } else if ($new_open !== $old_open) {
+ if ($new_open) {
+ $scale = 1;
+ } else {
+ $scale = -1;
+ }
+
+ $specs[] = array(
+ 'tasks.open-count.status',
+ 1 * $scale,
+ );
+
+ $specs[] = array(
+ 'tasks.open-points.status',
+ $action_points * $scale,
+ );
+
+ if ($new_owner !== null) {
+ $specs[] = array(
+ 'tasks.open-count.status.owner',
+ 1 * $scale,
+ $new_owner,
+ );
+ $specs[] = array(
+ 'tasks.open-points.status.owner',
+ $action_points * $scale,
+ $new_owner,
+ );
+ }
+
+ foreach ($project_map as $project_phid) {
+ $specs[] = array(
+ 'tasks.open-count.status.project',
+ 1 * $scale,
+ $project_phid,
+ );
+ $specs[] = array(
+ 'tasks.open-points.status.project',
+ $action_points * $scale,
+ $new_owner,
+ );
+ }
+
+ $old_open = $new_open;
+ }
+
+ // The "score" facts only apply to rescoring tasks which already
+ // exist, so we skip them if the task is being created.
+ if (($new_points !== $old_points) && !$is_create) {
+ $delta = ($new_points - $old_points);
+
+ $specs[] = array(
+ 'tasks.points.score',
+ $delta,
+ );
+
+ foreach ($project_map as $project_phid) {
+ $specs[] = array(
+ 'tasks.points.score.project',
+ $delta,
+ $project_phid,
+ );
+
+ if ($old_open && $new_open) {
+ $specs[] = array(
+ 'tasks.open-points.score.project',
+ $delta,
+ $project_phid,
+ );
+ }
+ }
+
+ if ($new_owner !== null) {
+ $specs[] = array(
+ 'tasks.points.score.owner',
+ $delta,
+ $new_owner,
+ );
+
+ if ($old_open && $new_open) {
+ $specs[] = array(
+ 'tasks.open-points.score.owner',
+ $delta,
+ $new_owner,
+ );
+ }
+ }
+
+ if ($old_open && $new_open) {
+ $specs[] = array(
+ 'tasks.open-points.score',
+ $delta,
+ );
+ }
+
+ $old_points = $new_points;
+ }
+
+ foreach ($specs as $spec) {
+ $spec_key = $spec[0];
+ $spec_value = $spec[1];
+
+ $datapoint = $this->getFact($spec_key)
+ ->newDatapoint();
+
+ $datapoint
+ ->setObjectPHID($object_phid)
+ ->setValue($spec_value)
+ ->setEpoch($group_epoch);
- $phid = $object->getPHID();
- $type = phid_get_type($phid);
+ if (isset($spec[2])) {
+ $datapoint->setDimensionPHID($spec[2]);
+ }
- $datapoint = $this->getFact('tasks.count.open')
- ->newDatapoint();
+ $datapoints[] = $datapoint;
+ }
- $datapoints[] = $datapoint
- ->setObjectPHID($phid)
- ->setValue(1)
- ->setEpoch($object->getDateCreated());
+ $specs = array();
+ $is_create = false;
+ }
return $datapoints;
}
diff --git a/src/applications/fact/fact/PhabricatorCountFact.php b/src/applications/fact/fact/PhabricatorCountFact.php
new file mode 100644
--- /dev/null
+++ b/src/applications/fact/fact/PhabricatorCountFact.php
@@ -0,0 +1,9 @@
+<?php
+
+final class PhabricatorCountFact extends PhabricatorFact {
+
+ protected function newTemplateDatapoint() {
+ return new PhabricatorFactIntDatapoint();
+ }
+
+}
diff --git a/src/applications/fact/storage/PhabricatorFactDimension.php b/src/applications/fact/storage/PhabricatorFactDimension.php
--- a/src/applications/fact/storage/PhabricatorFactDimension.php
+++ b/src/applications/fact/storage/PhabricatorFactDimension.php
@@ -6,10 +6,10 @@
final public function newDimensionID($key) {
$map = $this->newDimensionMap(array($key));
- return $map[$key];
+ return idx($map, $key);
}
- final public function newDimensionMap(array $keys) {
+ final public function newDimensionMap(array $keys, $create = false) {
if (!$keys) {
return array();
}
@@ -40,6 +40,10 @@
return $map;
}
+ if (!$create) {
+ return $map;
+ }
+
$sql = array();
foreach ($need as $key) {
$sql[] = qsprintf(
@@ -66,7 +70,7 @@
$need);
$rows = ipull($rows, 'id', $column);
- foreach ($keys as $key) {
+ foreach ($need as $key) {
if (isset($rows[$key])) {
$map[$key] = (int)$rows[$key];
} else {
diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php
--- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php
+++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php
@@ -260,6 +260,11 @@
return $this->oldValueHasBeenSet;
}
+ public function newChronologicalSortVector() {
+ return id(new PhutilSortVector())
+ ->addInt((int)$this->getDateCreated())
+ ->addInt((int)$this->getID());
+ }
/* -( Rendering )---------------------------------------------------------- */

File Metadata

Mime Type
text/plain
Expires
Wed, Mar 12, 7:15 AM (1 w, 4 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7394630
Default Alt Text
D19120.id.diff (17 KB)

Event Timeline