@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
at recaptime-dev/main 414 lines 12 kB view raw
1<?php 2 3final class PhabricatorManiphestTaskFactEngine 4 extends PhabricatorTransactionFactEngine { 5 6 /** 7 * @return array<PhabricatorCountFact> All known task related facts 8 */ 9 public function newFacts() { 10 return array( 11 id(new PhabricatorCountFact()) 12 ->setKey('tasks.count.create'), 13 14 id(new PhabricatorCountFact()) 15 ->setKey('tasks.open-count.create'), 16 id(new PhabricatorCountFact()) 17 ->setKey('tasks.open-count.status'), 18 19 id(new PhabricatorCountFact()) 20 ->setKey('tasks.count.create.project'), 21 id(new PhabricatorCountFact()) 22 ->setKey('tasks.count.assign.project'), 23 id(new PhabricatorCountFact()) 24 ->setKey('tasks.open-count.create.project'), 25 id(new PhabricatorCountFact()) 26 ->setKey('tasks.open-count.status.project'), 27 id(new PhabricatorCountFact()) 28 ->setKey('tasks.open-count.assign.project'), 29 30 id(new PhabricatorCountFact()) 31 ->setKey('tasks.count.create.owner'), 32 id(new PhabricatorCountFact()) 33 ->setKey('tasks.count.assign.owner'), 34 id(new PhabricatorCountFact()) 35 ->setKey('tasks.open-count.create.owner'), 36 id(new PhabricatorCountFact()) 37 ->setKey('tasks.open-count.status.owner'), 38 id(new PhabricatorCountFact()) 39 ->setKey('tasks.open-count.assign.owner'), 40 41 id(new PhabricatorPointsFact()) 42 ->setKey('tasks.points.create'), 43 id(new PhabricatorPointsFact()) 44 ->setKey('tasks.points.score'), 45 46 id(new PhabricatorPointsFact()) 47 ->setKey('tasks.open-points.create'), 48 id(new PhabricatorPointsFact()) 49 ->setKey('tasks.open-points.status'), 50 id(new PhabricatorPointsFact()) 51 ->setKey('tasks.open-points.score'), 52 53 id(new PhabricatorPointsFact()) 54 ->setKey('tasks.points.create.project'), 55 id(new PhabricatorPointsFact()) 56 ->setKey('tasks.points.assign.project'), 57 id(new PhabricatorPointsFact()) 58 ->setKey('tasks.points.score.project'), 59 id(new PhabricatorPointsFact()) 60 ->setKey('tasks.open-points.create.project'), 61 id(new PhabricatorPointsFact()) 62 ->setKey('tasks.open-points.status.project'), 63 id(new PhabricatorPointsFact()) 64 ->setKey('tasks.open-points.score.project'), 65 id(new PhabricatorPointsFact()) 66 ->setKey('tasks.open-points.assign.project'), 67 68 id(new PhabricatorPointsFact()) 69 ->setKey('tasks.points.create.owner'), 70 id(new PhabricatorPointsFact()) 71 ->setKey('tasks.points.assign.owner'), 72 id(new PhabricatorPointsFact()) 73 ->setKey('tasks.points.score.owner'), 74 id(new PhabricatorPointsFact()) 75 ->setKey('tasks.open-points.create.owner'), 76 id(new PhabricatorPointsFact()) 77 ->setKey('tasks.open-points.status.owner'), 78 id(new PhabricatorPointsFact()) 79 ->setKey('tasks.open-points.score.owner'), 80 id(new PhabricatorPointsFact()) 81 ->setKey('tasks.open-points.assign.owner'), 82 ); 83 } 84 85 public function supportsDatapointsForObject(PhabricatorLiskDAO $object) { 86 return ($object instanceof ManiphestTask); 87 } 88 89 public function newDatapointsForObject(PhabricatorLiskDAO $object) { 90 $xaction_groups = $this->newTransactionGroupsForObject($object); 91 92 $old_open = false; 93 $old_points = 0; 94 $old_owner = null; 95 $project_map = array(); 96 $object_phid = $object->getPHID(); 97 $is_create = true; 98 99 $specs = array(); 100 $datapoints = array(); 101 foreach ($xaction_groups as $xaction_group) { 102 $add_projects = array(); 103 $rem_projects = array(); 104 105 $new_open = $old_open; 106 $new_points = $old_points; 107 $new_owner = $old_owner; 108 109 if ($is_create) { 110 // Assume tasks start open. 111 // TODO: This might be a questionable assumption? 112 $new_open = true; 113 } 114 115 $group_epoch = last($xaction_group)->getDateCreated(); 116 foreach ($xaction_group as $xaction) { 117 $old_value = $xaction->getOldValue(); 118 $new_value = $xaction->getNewValue(); 119 switch ($xaction->getTransactionType()) { 120 case ManiphestTaskStatusTransaction::TRANSACTIONTYPE: 121 $new_open = !ManiphestTaskStatus::isClosedStatus($new_value); 122 break; 123 case ManiphestTaskMergedIntoTransaction::TRANSACTIONTYPE: 124 // When a task is merged into another task, it is changed to a 125 // closed status without generating a separate status transaction. 126 $new_open = false; 127 break; 128 case ManiphestTaskPointsTransaction::TRANSACTIONTYPE: 129 $new_points = (int)$xaction->getNewValue(); 130 break; 131 case ManiphestTaskOwnerTransaction::TRANSACTIONTYPE: 132 $new_owner = $xaction->getNewValue(); 133 break; 134 case PhabricatorTransactions::TYPE_EDGE: 135 $edge_type = $xaction->getMetadataValue('edge:type'); 136 switch ($edge_type) { 137 case PhabricatorProjectObjectHasProjectEdgeType::EDGECONST: 138 $record = PhabricatorEdgeChangeRecord::newFromTransaction( 139 $xaction); 140 $add_projects += array_fuse($record->getAddedPHIDs()); 141 $rem_projects += array_fuse($record->getRemovedPHIDs()); 142 break; 143 } 144 break; 145 } 146 } 147 148 // If a project was both added and removed, moot it. 149 $mix_projects = array_intersect_key($add_projects, $rem_projects); 150 $add_projects = array_diff_key($add_projects, $mix_projects); 151 $rem_projects = array_diff_key($rem_projects, $mix_projects); 152 153 $project_sets = array( 154 array( 155 'phids' => $rem_projects, 156 'scale' => -1, 157 ), 158 array( 159 'phids' => $add_projects, 160 'scale' => 1, 161 ), 162 ); 163 164 if ($is_create) { 165 $action = 'create'; 166 $action_points = $new_points; 167 $include_open = $new_open; 168 } else { 169 $action = 'assign'; 170 $action_points = $old_points; 171 $include_open = $old_open; 172 } 173 174 foreach ($project_sets as $project_set) { 175 $scale = $project_set['scale']; 176 foreach ($project_set['phids'] as $project_phid) { 177 if ($include_open) { 178 $specs[] = array( 179 "tasks.open-count.{$action}.project", 180 1 * $scale, 181 $project_phid, 182 ); 183 184 $specs[] = array( 185 "tasks.open-points.{$action}.project", 186 $action_points * $scale, 187 $project_phid, 188 ); 189 } 190 191 $specs[] = array( 192 "tasks.count.{$action}.project", 193 1 * $scale, 194 $project_phid, 195 ); 196 197 $specs[] = array( 198 "tasks.points.{$action}.project", 199 $action_points * $scale, 200 $project_phid, 201 ); 202 203 if ($scale < 0) { 204 unset($project_map[$project_phid]); 205 } else { 206 $project_map[$project_phid] = $project_phid; 207 } 208 } 209 } 210 211 if ($new_owner !== $old_owner) { 212 $owner_sets = array( 213 array( 214 'phid' => $old_owner, 215 'scale' => -1, 216 ), 217 array( 218 'phid' => $new_owner, 219 'scale' => 1, 220 ), 221 ); 222 223 foreach ($owner_sets as $owner_set) { 224 $owner_phid = $owner_set['phid']; 225 if ($owner_phid === null) { 226 continue; 227 } 228 229 $scale = $owner_set['scale']; 230 231 if ($old_open != $new_open) { 232 $specs[] = array( 233 "tasks.open-count.{$action}.owner", 234 1 * $scale, 235 $owner_phid, 236 ); 237 238 $specs[] = array( 239 "tasks.open-points.{$action}.owner", 240 $action_points * $scale, 241 $owner_phid, 242 ); 243 } 244 245 $specs[] = array( 246 "tasks.count.{$action}.owner", 247 1 * $scale, 248 $owner_phid, 249 ); 250 251 if ($action_points) { 252 $specs[] = array( 253 "tasks.points.{$action}.owner", 254 $action_points * $scale, 255 $owner_phid, 256 ); 257 } 258 } 259 } 260 261 if ($is_create) { 262 $specs[] = array( 263 'tasks.count.create', 264 1, 265 ); 266 267 $specs[] = array( 268 'tasks.points.create', 269 $new_points, 270 ); 271 272 if ($new_open) { 273 $specs[] = array( 274 'tasks.open-count.create', 275 1, 276 ); 277 $specs[] = array( 278 'tasks.open-points.create', 279 $new_points, 280 ); 281 } 282 } else if ($new_open !== $old_open) { 283 if ($new_open) { 284 $scale = 1; 285 } else { 286 $scale = -1; 287 } 288 289 $specs[] = array( 290 'tasks.open-count.status', 291 1 * $scale, 292 ); 293 294 $specs[] = array( 295 'tasks.open-points.status', 296 $action_points * $scale, 297 ); 298 299 if ($new_owner !== null) { 300 $specs[] = array( 301 'tasks.open-count.status.owner', 302 1 * $scale, 303 $new_owner, 304 ); 305 $specs[] = array( 306 'tasks.open-points.status.owner', 307 $action_points * $scale, 308 $new_owner, 309 ); 310 } 311 312 foreach ($project_map as $project_phid) { 313 $specs[] = array( 314 'tasks.open-count.status.project', 315 1 * $scale, 316 $project_phid, 317 ); 318 $specs[] = array( 319 'tasks.open-points.status.project', 320 $action_points * $scale, 321 $project_phid, 322 ); 323 } 324 } 325 326 // The "score" facts only apply to rescoring tasks which already 327 // exist, so we skip them if the task is being created. 328 if (($new_points !== $old_points) && !$is_create) { 329 $delta = ($new_points - $old_points); 330 331 $specs[] = array( 332 'tasks.points.score', 333 $delta, 334 ); 335 336 foreach ($project_map as $project_phid) { 337 $specs[] = array( 338 'tasks.points.score.project', 339 $delta, 340 $project_phid, 341 ); 342 343 if ($old_open && $new_open) { 344 $specs[] = array( 345 'tasks.open-points.score.project', 346 $delta, 347 $project_phid, 348 ); 349 } 350 } 351 352 if ($new_owner !== null) { 353 $specs[] = array( 354 'tasks.points.score.owner', 355 $delta, 356 $new_owner, 357 ); 358 359 if ($old_open && $new_open) { 360 $specs[] = array( 361 'tasks.open-points.score.owner', 362 $delta, 363 $new_owner, 364 ); 365 } 366 } 367 368 if ($old_open && $new_open) { 369 $specs[] = array( 370 'tasks.open-points.score', 371 $delta, 372 ); 373 } 374 } 375 376 $old_points = $new_points; 377 $old_open = $new_open; 378 $old_owner = $new_owner; 379 380 foreach ($specs as $spec) { 381 $spec_key = $spec[0]; 382 $spec_value = $spec[1]; 383 384 // Don't write any facts with a value of 0. The "count" facts never 385 // have a value of 0, and the "points" facts aren't meaningful if 386 // they have a value of 0. 387 if ($spec_value == 0) { 388 continue; 389 } 390 391 $datapoint = $this->getFact($spec_key) 392 ->newDatapoint(); 393 394 $datapoint 395 ->setObjectPHID($object_phid) 396 ->setValue($spec_value) 397 ->setEpoch($group_epoch); 398 399 if (isset($spec[2])) { 400 $datapoint->setDimensionPHID($spec[2]); 401 } 402 403 $datapoints[] = $datapoint; 404 } 405 406 $specs = array(); 407 $is_create = false; 408 } 409 410 return $datapoints; 411 } 412 413 414}