Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15358246
D19120.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
17 KB
Referenced Files
None
Subscribers
None
D19120.id.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D19120: Extract count/point data from tasks in Fact engines
Attached
Detach File
Event Timeline
Log In to Comment