@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<?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}