These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /** |
||
4 | * A query printer that uses the dygraphs JavaScript library |
||
5 | * |
||
6 | * @see http://www.semantic-mediawiki.org/wiki/Help:Flot_timeseries_chart |
||
7 | * @licence GNU GPL v2 or later |
||
8 | * |
||
9 | * @since 1.8 |
||
10 | * |
||
11 | * @author mwjames |
||
12 | */ |
||
13 | class SRFDygraphs extends SMWResultPrinter { |
||
14 | |||
15 | /** |
||
16 | * @see SMWResultPrinter::getName |
||
17 | * @return string |
||
18 | */ |
||
19 | public function getName() { |
||
20 | return wfMessage( 'srf-printername-dygraphs' )->text(); |
||
21 | } |
||
22 | |||
23 | /** |
||
24 | * @see SMWResultPrinter::getResultText |
||
25 | * |
||
26 | * @param SMWQueryResult $result |
||
27 | * @param $outputMode |
||
28 | * |
||
29 | * @return string |
||
30 | */ |
||
31 | protected function getResultText( SMWQueryResult $result, $outputMode ) { |
||
32 | |||
33 | // Output mode is fixed |
||
34 | $outputMode = SMW_OUTPUT_HTML; |
||
35 | |||
36 | // Data processing |
||
37 | $data = $this->getResultData( $result, $outputMode ); |
||
38 | |||
39 | // Post-data processing check |
||
40 | if ( $data === [] ) { |
||
41 | return $result->addErrors( [ wfMessage( 'srf-warn-empy-chart' )->inContentLanguage()->text() ] ); |
||
42 | } else { |
||
43 | $options['sask'] = SRFUtils::htmlQueryResultLink( $this->getLink( $result, SMW_OUTPUT_HTML ) ); |
||
44 | return $this->getFormatOutput( $data, $options ); |
||
45 | } |
||
46 | } |
||
47 | |||
48 | /** |
||
49 | * Returns an array with numerical data |
||
50 | * |
||
51 | * @since 1.8 |
||
52 | * |
||
53 | * @param SMWQueryResult $result |
||
54 | * @param $outputMode |
||
55 | * |
||
56 | * @return array |
||
57 | */ |
||
58 | protected function getResultData( SMWQueryResult $result, $outputMode ) { |
||
59 | $aggregatedValues = []; |
||
60 | |||
61 | while ( $rows = $result->getNext() ) { // Objects (pages) |
||
62 | $annotation = []; |
||
63 | $dataSource = false; |
||
64 | |||
65 | /** |
||
66 | * @var SMWResultArray $field |
||
67 | * @var SMWDataValue $dataValue |
||
68 | */ |
||
69 | foreach ( $rows as $field ) { |
||
70 | |||
71 | // Use the subject marker to identify a possible data file |
||
72 | $subject = $field->getResultSubject(); |
||
73 | if ( $this->params['datasource'] === 'file' && $subject->getTitle()->getNamespace( |
||
74 | ) === NS_FILE && !$dataSource ) { |
||
75 | $aggregatedValues['subject'] = $this->makePageFromTitle( $subject->getTitle() )->getLongHTMLText( |
||
76 | $this->getLinker( $field->getResultSubject() ) |
||
77 | ); |
||
78 | $aggregatedValues['url'] = wfFindFile( $subject->getTitle() )->getUrl(); |
||
79 | $dataSource = true; |
||
80 | } |
||
81 | |||
82 | // Proceed only where a label is known otherwise items are of no use |
||
83 | // for being a potential object identifier |
||
84 | if ( $field->getPrintRequest()->getLabel() !== '' ) { |
||
85 | $propertyLabel = $field->getPrintRequest()->getLabel(); |
||
86 | } else { |
||
87 | continue; |
||
88 | } |
||
89 | |||
90 | while ( ( $dataValue = $field->getNextDataValue() ) !== false ) { // Data values |
||
91 | |||
92 | // Jump the column (indicated by continue) because we don't want the data source being part of the annotation array |
||
93 | if ( $dataValue->getDataItem()->getDIType( |
||
94 | ) == SMWDataItem::TYPE_WIKIPAGE && $this->params['datasource'] === 'raw' && !$dataSource ) { |
||
95 | // Support data source = raw which pulls the url from a wikipage in raw format |
||
96 | $aggregatedValues['subject'] = $this->makePageFromTitle( |
||
97 | $dataValue->getTitle() |
||
98 | )->getLongHTMLText( $this->getLinker( $field->getResultSubject() ) ); |
||
99 | $aggregatedValues['url'] = $dataValue->getTitle()->getLocalURL( 'action=raw' ); |
||
100 | $dataSource = true; |
||
101 | continue; |
||
102 | } elseif ( $dataValue->getDataItem()->getDIType( |
||
103 | ) == SMWDataItem::TYPE_WIKIPAGE && $this->params['datasource'] === 'file' && $dataValue->getTitle( |
||
104 | )->getNamespace() === NS_FILE && !$dataSource ) { |
||
105 | // Support data source = file which pulls the url from a uploaded file |
||
106 | $aggregatedValues['subject'] = $this->makePageFromTitle( |
||
107 | $dataValue->getTitle() |
||
108 | )->getLongHTMLText( $this->getLinker( $field->getResultSubject() ) ); |
||
109 | $aggregatedValues['url'] = wfFindFile( $dataValue->getTitle() )->getUrl(); |
||
110 | $dataSource = true; |
||
111 | continue; |
||
112 | } elseif ( $dataValue->getDataItem()->getDIType( |
||
113 | ) == SMWDataItem::TYPE_URI && $this->params['datasource'] === 'url' && !$dataSource ) { |
||
114 | // Support data source = url, pointing to an url data source |
||
115 | $aggregatedValues['link'] = $dataValue->getShortHTMLText( $this->getLinker( false ) ); |
||
116 | $aggregatedValues['url'] = $dataValue->getURL(); |
||
117 | $dataSource = true; |
||
118 | continue; |
||
119 | } |
||
120 | |||
121 | // The annotation should adhere outlined conventions as the label identifies the array object key |
||
122 | // series -> Required The name of the series to which the annotated point belongs |
||
123 | // x -> Required The x value of the point |
||
124 | // shortText -> Text that will appear as annotation flag |
||
125 | // text -> A longer description of the annotation |
||
126 | // @see http://dygraphs.com/annotations.html |
||
127 | if ( in_array( $propertyLabel, [ 'series', 'x', 'shortText', 'text' ] ) ) { |
||
128 | if ( $dataValue->getDataItem()->getDIType() == SMWDataItem::TYPE_NUMBER ) { |
||
129 | // Set unit if available |
||
130 | $dataValue->setOutputFormat( $this->params['unit'] ); |
||
131 | // Check if unit is available |
||
132 | $annotation[$propertyLabel] = $dataValue->getUnit() !== '' ? $dataValue->getShortWikiText( |
||
133 | ) : $dataValue->getNumber(); |
||
134 | } else { |
||
135 | $annotation[$propertyLabel] = $dataValue->getWikiValue(); |
||
136 | } |
||
137 | } |
||
138 | } |
||
139 | } |
||
140 | // Sum-up collected row items in a single array |
||
141 | if ( $annotation !== [] ) { |
||
142 | $aggregatedValues['annotation'][] = $annotation; |
||
143 | } |
||
144 | } |
||
145 | return $aggregatedValues; |
||
146 | } |
||
147 | |||
148 | private function makePageFromTitle( \Title $title ) { |
||
149 | $dataValue = new SMWWikiPageValue( '_wpg' ); |
||
150 | $dataItem = SMWDIWikiPage::newFromTitle( $title ); |
||
151 | $dataValue->setDataItem( $dataItem ); |
||
152 | return $dataValue; |
||
153 | } |
||
154 | |||
155 | /** |
||
156 | * Prepare data for the output |
||
157 | * |
||
158 | * @since 1.8 |
||
159 | * |
||
160 | * @param array $data |
||
161 | * |
||
162 | * @return string |
||
163 | */ |
||
164 | protected function getFormatOutput( $data, $options ) { |
||
165 | |||
166 | // Object count |
||
167 | static $statNr = 0; |
||
168 | $chartID = 'srf-dygraphs-' . ++$statNr; |
||
169 | |||
170 | $this->isHTML = true; |
||
171 | |||
172 | // Reorganize the raw data |
||
173 | if ( $this->params['datasource'] === 'page' ) { |
||
174 | foreach ( $data as $key => $values ) { |
||
175 | $dataObject[] = [ 'label' => $key, 'data' => $values ]; |
||
0 ignored issues
–
show
|
|||
176 | } |
||
177 | } else { |
||
178 | $dataObject['source'] = $data; |
||
179 | } |
||
180 | |||
181 | // Prepare transfer array |
||
182 | $chartData = [ |
||
183 | 'data' => $dataObject, |
||
184 | 'sask' => $options['sask'], |
||
185 | 'parameters' => [ |
||
186 | 'width' => $this->params['width'], |
||
187 | 'height' => $this->params['height'], |
||
188 | 'xlabel' => $this->params['xlabel'], |
||
189 | 'ylabel' => $this->params['ylabel'], |
||
190 | 'charttitle' => $this->params['charttitle'], |
||
191 | 'charttext' => $this->params['charttext'], |
||
192 | 'infotext' => $this->params['infotext'], |
||
193 | 'datasource' => $this->params['datasource'], |
||
194 | 'rollerperiod' => $this->params['mavg'], |
||
195 | 'gridview' => $this->params['gridview'], |
||
196 | 'errorbar' => $this->params['errorbar'], |
||
197 | ] |
||
198 | ]; |
||
199 | |||
200 | // Array encoding and output |
||
201 | $requireHeadItem = [ $chartID => FormatJson::encode( $chartData ) ]; |
||
202 | SMWOutputs::requireHeadItem( $chartID, Skin::makeVariablesScript( $requireHeadItem ) ); |
||
203 | |||
204 | SMWOutputs::requireResource( 'ext.srf.dygraphs' ); |
||
205 | |||
206 | if ( $this->params['gridview'] === 'tabs' ) { |
||
207 | SMWOutputs::requireResource( 'ext.srf.util.grid' ); |
||
208 | } |
||
209 | |||
210 | // Chart/graph placeholder |
||
211 | $chart = Html::rawElement( |
||
212 | 'div', |
||
213 | [ 'id' => $chartID, 'class' => 'container', 'style' => "display:none;" ], |
||
214 | null |
||
215 | ); |
||
216 | |||
217 | // Processing/loading image |
||
218 | $processing = SRFUtils::htmlProcessingElement( $this->isHTML ); |
||
219 | |||
220 | // Beautify class selector |
||
221 | $class = $this->params['class'] ? ' ' . $this->params['class'] : ' dygraphs-common'; |
||
222 | |||
223 | // General output marker |
||
224 | return Html::rawElement( |
||
225 | 'div', |
||
226 | [ 'class' => 'srf-dygraphs' . $class ], |
||
227 | $processing . $chart |
||
228 | ); |
||
229 | } |
||
230 | |||
231 | /** |
||
232 | * @see SMWResultPrinter::getParamDefinitions |
||
233 | * |
||
234 | * @since 1.8 |
||
235 | * |
||
236 | * @param $definitions array of IParamDefinition |
||
237 | * |
||
238 | * @return array of IParamDefinition|array |
||
239 | */ |
||
240 | public function getParamDefinitions( array $definitions ) { |
||
241 | $params = parent::getParamDefinitions( $definitions ); |
||
242 | |||
243 | $params['datasource'] = [ |
||
244 | 'message' => 'srf-paramdesc-datasource', |
||
245 | 'default' => 'file', |
||
246 | 'values' => [ 'file', 'raw', 'url' ], |
||
247 | ]; |
||
248 | |||
249 | $params['errorbar'] = [ |
||
250 | 'message' => 'srf-paramdesc-errorbar', |
||
251 | 'default' => '', |
||
252 | 'values' => [ 'fraction', 'sigma', 'range' ], |
||
253 | ]; |
||
254 | |||
255 | $params['min'] = [ |
||
256 | 'type' => 'integer', |
||
257 | 'message' => 'srf-paramdesc-minvalue', |
||
258 | 'default' => '', |
||
259 | ]; |
||
260 | |||
261 | $params['mavg'] = [ |
||
262 | 'type' => 'integer', |
||
263 | 'message' => 'srf-paramdesc-movingaverage', |
||
264 | 'default' => 14, |
||
265 | 'lowerbound' => 0, |
||
266 | ]; |
||
267 | |||
268 | $params['gridview'] = [ |
||
269 | 'message' => 'srf-paramdesc-gridview', |
||
270 | 'default' => 'none', |
||
271 | 'values' => [ 'none', 'tabs' ], |
||
272 | ]; |
||
273 | |||
274 | $params['infotext'] = [ |
||
275 | 'message' => 'srf-paramdesc-infotext', |
||
276 | 'default' => '', |
||
277 | ]; |
||
278 | |||
279 | $params['unit'] = [ |
||
280 | 'message' => 'srf-paramdesc-unit', |
||
281 | 'default' => '', |
||
282 | ]; |
||
283 | |||
284 | $params['height'] = [ |
||
285 | 'type' => 'integer', |
||
286 | 'message' => 'srf_paramdesc_chartheight', |
||
287 | 'default' => 400, |
||
288 | 'lowerbound' => 1, |
||
289 | ]; |
||
290 | |||
291 | $params['width'] = [ |
||
292 | 'message' => 'srf_paramdesc_chartwidth', |
||
293 | 'default' => '100%', |
||
294 | ]; |
||
295 | |||
296 | $params['charttitle'] = [ |
||
297 | 'message' => 'srf_paramdesc_charttitle', |
||
298 | 'default' => '', |
||
299 | ]; |
||
300 | |||
301 | $params['charttext'] = [ |
||
302 | 'message' => 'srf-paramdesc-charttext', |
||
303 | 'default' => '', |
||
304 | ]; |
||
305 | |||
306 | $params['infotext'] = [ |
||
307 | 'message' => 'srf-paramdesc-infotext', |
||
308 | 'default' => '', |
||
309 | ]; |
||
310 | |||
311 | $params['ylabel'] = [ |
||
312 | 'message' => 'srf-paramdesc-yaxislabel', |
||
313 | 'default' => '', |
||
314 | ]; |
||
315 | |||
316 | $params['xlabel'] = [ |
||
317 | 'message' => 'srf-paramdesc-xaxislabel', |
||
318 | 'default' => '', |
||
319 | ]; |
||
320 | |||
321 | $params['class'] = [ |
||
322 | 'message' => 'srf-paramdesc-class', |
||
323 | 'default' => '', |
||
324 | ]; |
||
325 | |||
326 | return $params; |
||
327 | } |
||
328 | } |
Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.
Let’s take a look at an example:
As you can see in this example, the array
$myArray
is initialized the first time when the foreach loop is entered. You can also see that the value of thebar
key is only written conditionally; thus, its value might result from a previous iteration.This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.