@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 upstream/main 321 lines 9.0 kB view raw
1<?php 2 3final class PhabricatorFactObjectController 4 extends PhabricatorFactController { 5 6 public function handleRequest(AphrontRequest $request) { 7 $viewer = $request->getViewer(); 8 9 $phid = $request->getURIData('phid'); 10 $object = id(new PhabricatorObjectQuery()) 11 ->setViewer($viewer) 12 ->withNames(array($phid)) 13 ->executeOne(); 14 if (!$object) { 15 return new Aphront404Response(); 16 } 17 18 $engines = PhabricatorFactEngine::loadAllEngines(); 19 foreach ($engines as $key => $engine) { 20 $engine = id(clone $engine) 21 ->setViewer($viewer); 22 $engines[$key] = $engine; 23 24 if (!$engine->supportsDatapointsForObject($object)) { 25 unset($engines[$key]); 26 } 27 } 28 29 if (!$engines) { 30 return $this->newDialog() 31 ->setTitle(pht('No Engines')) 32 ->appendParagraph( 33 pht( 34 'No fact engines support generating facts for this object.')) 35 ->addCancelButton($this->getApplicationURI()); 36 } 37 38 $key_dimension = new PhabricatorFactKeyDimension(); 39 $object_phid = $object->getPHID(); 40 41 $facts = array(); 42 $generated_datapoints = array(); 43 $timings = array(); 44 foreach ($engines as $key => $engine) { 45 $engine_facts = $engine->newFacts(); 46 $engine_facts = mpull($engine_facts, null, 'getKey'); 47 $facts[$key] = $engine_facts; 48 49 $t_start = microtime(true); 50 $generated_datapoints[$key] = $engine->newDatapointsForObject($object); 51 $t_end = microtime(true); 52 53 $timings[$key] = ($t_end - $t_start); 54 } 55 56 $object_id = id(new PhabricatorFactObjectDimension()) 57 ->newDimensionID($object_phid, true); 58 59 $stored_datapoints = id(new PhabricatorFactDatapointQuery()) 60 ->withFacts(array_mergev($facts)) 61 ->withObjectPHIDs(array($object_phid)) 62 ->needVectors(true) 63 ->execute(); 64 65 $stored_groups = array(); 66 foreach ($stored_datapoints as $stored_datapoint) { 67 $stored_groups[$stored_datapoint['key']][] = $stored_datapoint; 68 } 69 70 $stored_map = array(); 71 foreach ($engines as $key => $engine) { 72 $stored_map[$key] = array(); 73 foreach ($facts[$key] as $fact) { 74 $fact_datapoints = idx($stored_groups, $fact->getKey(), array()); 75 $fact_datapoints = igroup($fact_datapoints, 'vector'); 76 $stored_map[$key] += $fact_datapoints; 77 } 78 } 79 80 $handle_phids = array(); 81 $handle_phids[] = $object->getPHID(); 82 foreach ($generated_datapoints as $key => $datapoint_set) { 83 foreach ($datapoint_set as $datapoint) { 84 $dimension_phid = $datapoint->getDimensionPHID(); 85 if ($dimension_phid !== null) { 86 $handle_phids[$dimension_phid] = $dimension_phid; 87 } 88 } 89 } 90 91 foreach ($stored_map as $key => $stored_datapoints) { 92 foreach ($stored_datapoints as $vector_key => $datapoints) { 93 foreach ($datapoints as $datapoint) { 94 $dimension_phid = $datapoint['dimensionPHID']; 95 if ($dimension_phid !== null) { 96 $handle_phids[$dimension_phid] = $dimension_phid; 97 } 98 } 99 } 100 } 101 102 $handles = $viewer->loadHandles($handle_phids); 103 104 $dimension_map = id(new PhabricatorFactObjectDimension()) 105 ->newDimensionMap($handle_phids, true); 106 107 $content = array(); 108 109 $object_list = id(new PHUIPropertyListView()) 110 ->setViewer($viewer) 111 ->addProperty( 112 pht('Object'), 113 $handles[$object->getPHID()]->renderLink()); 114 115 $total_cost = array_sum($timings); 116 $total_cost = pht('%sms', new PhutilNumber((int)(1000 * $total_cost))); 117 $object_list->addProperty(pht('Total Cost'), $total_cost); 118 119 $object_box = id(new PHUIObjectBoxView()) 120 ->setHeaderText(pht('Fact Extraction Report')) 121 ->addPropertyList($object_list); 122 123 $content[] = $object_box; 124 125 $icon_fact = id(new PHUIIconView()) 126 ->setIcon('fa-line-chart green') 127 ->setTooltip(pht('Consistent Fact')); 128 129 $icon_nodata = id(new PHUIIconView()) 130 ->setIcon('fa-question-circle-o violet') 131 ->setTooltip(pht('No Stored Datapoints')); 132 133 $icon_new = id(new PHUIIconView()) 134 ->setIcon('fa-plus red') 135 ->setTooltip(pht('Not Stored')); 136 137 $icon_surplus = id(new PHUIIconView()) 138 ->setIcon('fa-minus red') 139 ->setTooltip(pht('Not Generated')); 140 141 foreach ($engines as $key => $engine) { 142 $rows = array(); 143 foreach ($generated_datapoints[$key] as $datapoint) { 144 $dimension_phid = $datapoint->getDimensionPHID(); 145 if ($dimension_phid !== null) { 146 $dimension = $handles[$datapoint->getDimensionPHID()]->renderLink(); 147 } else { 148 $dimension = null; 149 } 150 151 $fact_key = $datapoint->getKey(); 152 153 $fact = idx($facts[$key], $fact_key, null); 154 if ($fact) { 155 $fact_label = $fact->getName(); 156 } else { 157 $fact_label = $fact_key; 158 } 159 160 $vector_key = $datapoint->newDatapointVector(); 161 if (isset($stored_map[$key][$vector_key])) { 162 unset($stored_map[$key][$vector_key]); 163 $icon = $icon_fact; 164 } else { 165 $icon = $icon_new; 166 } 167 168 $rows[] = array( 169 $icon, 170 $fact_label, 171 $dimension, 172 $datapoint->getValue(), 173 phabricator_datetime($datapoint->getEpoch(), $viewer), 174 ); 175 } 176 177 foreach ($stored_map[$key] as $vector_key => $datapoints) { 178 foreach ($datapoints as $datapoint) { 179 $dimension_phid = $datapoint['dimensionPHID']; 180 if ($dimension_phid !== null) { 181 $dimension = $handles[$dimension_phid]->renderLink(); 182 } else { 183 $dimension = null; 184 } 185 186 $fact_key = $datapoint['key']; 187 $fact = idx($facts[$key], $fact_key, null); 188 if ($fact) { 189 $fact_label = $fact->getName(); 190 } else { 191 $fact_label = $fact_key; 192 } 193 194 $rows[] = array( 195 $icon_surplus, 196 $fact_label, 197 $dimension, 198 $datapoint['value'], 199 phabricator_datetime($datapoint['epoch'], $viewer), 200 ); 201 } 202 } 203 204 foreach ($facts[$key] as $fact) { 205 $has_any = id(new PhabricatorFactDatapointQuery()) 206 ->withFacts(array($fact)) 207 ->setLimit(1) 208 ->execute(); 209 if ($has_any) { 210 continue; 211 } 212 213 $rows[] = array( 214 $icon_nodata, 215 $fact->getName(), 216 null, 217 null, 218 null, 219 ); 220 } 221 222 $table = id(new AphrontTableView($rows)) 223 ->setHeaders( 224 array( 225 null, 226 pht('Fact'), 227 pht('Dimension'), 228 pht('Value'), 229 pht('Date'), 230 )) 231 ->setColumnClasses( 232 array( 233 '', 234 '', 235 '', 236 'n wide right', 237 'right', 238 )); 239 240 $extraction_cost = $timings[$key]; 241 $extraction_cost = pht( 242 '%sms', 243 new PhutilNumber((int)(1000 * $extraction_cost))); 244 245 $header = pht( 246 '%s (%s)', 247 get_class($engine), 248 $extraction_cost); 249 250 $box = id(new PHUIObjectBoxView()) 251 ->setHeaderText($header) 252 ->setTable($table); 253 254 $content[] = $box; 255 256 if ($engine instanceof PhabricatorTransactionFactEngine) { 257 $groups = $engine->newTransactionGroupsForObject($object); 258 $groups = array_values($groups); 259 260 $xaction_phids = array(); 261 foreach ($groups as $group_key => $xactions) { 262 foreach ($xactions as $xaction) { 263 $xaction_phids[] = $xaction->getAuthorPHID(); 264 } 265 } 266 $xaction_handles = $viewer->loadHandles($xaction_phids); 267 268 $rows = array(); 269 foreach ($groups as $group_key => $xactions) { 270 foreach ($xactions as $xaction) { 271 $rows[] = array( 272 $group_key, 273 $xaction->getTransactionType(), 274 $xaction_handles[$xaction->getAuthorPHID()]->renderLink(), 275 phabricator_datetime($xaction->getDateCreated(), $viewer), 276 ); 277 } 278 } 279 280 $table = id(new AphrontTableView($rows)) 281 ->setHeaders( 282 array( 283 pht('Group'), 284 pht('Type'), 285 pht('Author'), 286 pht('Date'), 287 )) 288 ->setColumnClasses( 289 array( 290 null, 291 'pri', 292 'wide', 293 'right', 294 )); 295 296 $header = pht( 297 '%s (Transactions)', 298 get_class($engine)); 299 300 $xaction_box = id(new PHUIObjectBoxView()) 301 ->setHeaderText($header) 302 ->setTable($table); 303 304 $content[] = $xaction_box; 305 } 306 307 } 308 309 $crumbs = $this->buildApplicationCrumbs() 310 ->addTextCrumb(pht('Chart')); 311 312 $title = pht('Chart'); 313 314 return $this->newPage() 315 ->setTitle($title) 316 ->setCrumbs($crumbs) 317 ->appendChild($content); 318 319 } 320 321}