Completed
Push — master ( 249923...ac15b4 )
by Jeroen De
135:11 queued 115:11
created

formats/jqplot/SRF_jqPlotSeries.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

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 for charts series using the jqPlot JavaScript library.
5
 *
6
 * @since 1.8
7
 * @licence GNU GPL v2 or later
8
 *
9
 * @author mwjames 
10
 */
11
class SRFjqPlotSeries extends SMWResultPrinter {
12
	/**
13
	 * @see SMWResultPrinter::getName
14
	 */
15
	public function getName() {
16
		return wfMessage( 'srf-printername-jqplotseries' )->text();
17
	}
18
19
	/**
20
	 * Returns an array with the numerical data in the query result.
21
	 *
22
	 *
23
	 * @param SMWQueryResult $result
24
	 * @param $outputMode
25
	 *
26
	 * @return string
27
	 */
28
	protected function getResultText( SMWQueryResult $result, $outputMode ) {
29
30
		// Get data set
31
		$data = $this->getResultData( $result, $outputMode );
32
33
		// Check data availability
34
		if ( $data['series'] === [] ) {
35
			return $result->addErrors( [ wfMessage( 'srf-warn-empy-chart' )
36
					->inContentLanguage()->text() ] );
37
		} else {
38
			$options['sask'] = SRFUtils::htmlQueryResultLink( $this->getLink( $result, SMW_OUTPUT_HTML ) );
39
			return $this->getFormatOutput( $this->getFormatSettings( $this->getNumbersTicks( $data ), $options ) );
40
		}
41
	}
42
43
	/**
44
	 * Returns an array with the numerical data
45
	 *
46
	 * @since 1.8
47
	 *
48
	 * @param SMWQueryResult $result
49
	 * @param $outputMode
50
	 *
51
	 * @return array
52
	 */
53
	protected function getResultData( SMWQueryResult $res, $outputMode ) {
54
		$data = [];
55
		$data['series'] = [];
56
57
		while ( $row = $res->getNext() ) {
58
			// Loop over their fields (properties)
59
			$label = '';
60
			$i = 0;
61
62
			foreach ( $row as /* SMWResultArray */ $field ) {
63
				$i++;
64
				$rowNumbers = [];
65
66
				// Grouping by subject (page object) or property
67
				if ( $this->params['group'] === 'subject' ){
68
					$groupedBy = $field->getResultSubject()->getTitle()->getText();
69
				} else {
70
					$groupedBy = $field->getPrintRequest()->getLabel();
71
				}
72
73
				// Property label
74
				$property = $field->getPrintRequest()->getLabel();
75
76
				// First column property typeid
77
				$i == 1 ? $data['fcolumntypeid'] = $field->getPrintRequest()->getTypeID() : '';
78
79
				// Loop over all values for the property.
80
				while ( ( /* SMWDataValue */ $object = $field->getNextDataValue() ) !== false ) {
81
82
					if ( $object->getDataItem()->getDIType() == SMWDataItem::TYPE_NUMBER ) {
83
						$number =  $object->getNumber();
84
85
						// Checking against the row and in case the first column is a numeric
86
						// value it is handled as label with the remaining steps continue to work
87
						// as it were a text label
88
						// The first column container will not be part of the series container
89
						if ( $i == 1 ){
90
							$label = $number;
91
							continue;
92
						}
93
94
						if ( $label !== '' && $number >= $this->params['min'] ){
95
96
							// Reference array summarize all items per row
97
							$rowNumbers +=  [ 'subject' => $label, 'value' => $number, 'property' => $property ];
98
99
							// Store plain numbers for simpler handling
100
							$data['series'][$groupedBy][] = $number;
101
						}
102
					}elseif ( $object->getDataItem()->getDIType() == SMWDataItem::TYPE_TIME ){
103
						$label = $object->getShortWikiText();
104
					}else{
105
						$label = $object->getWikiValue();
106
					}
107
				}
108
				// Only for array's with numbers 
109
				if ( count( $rowNumbers ) > 0 ) {
110
111
					// For cases where mainlabel=- we assume that the subject should not be
112
					// used as identifier and therefore we try to match the groupby
113
					// with the first available text label
114
					if ( $this->params['mainlabel'] == '-' && $this->params['group'] === 'subject' ){
115
						$data[$this->params['group']][$label][]= $rowNumbers;
116
					} else {
117
						$data[$this->params['group']][$groupedBy][]= $rowNumbers;
118
					}
119
				}
120
			}
121
		}
122
		return $data;
123
	}
124
125
	/**
126
	 * Data set sorting
127
	 *
128
	 * @since 1.8
129
	 *
130
	 * @param array $data label => value
131
	 *
132
	 *@return array
133
	 */
134
	private function getFormatSettings( $data, $options ) {
135
136
		// Init
137
		$dataSet =  [];
138
		$grid =  [];
0 ignored issues
show
$grid is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
139
		$options['mode'] = 'series';
140
		$options['autoscale'] = false;
141
142
		// Available markers
143
		$marker =  [ 'circle', 'diamond', 'square', 'filledCircle', 'filledDiamond', 'filledSquare' ];
144
145
		// Series colour(has to be null otherwise jqplot runs with a type error)
146
		$seriescolors = $this->params['chartcolor'] !== '' ? array_filter( explode( "," , $this->params['chartcolor'] ) ) : null;
147
148
		// Re-grouping
149
		foreach ( $data[$this->params['group']] as $rowKey => $row ) {
150
			$values= [];
151
152
			foreach ( $row as $key => $value ) {
153
				// Switch labels according to the group parameter
154
				$label = $this->params['grouplabel'] === 'property' ? $value['property'] : $value['subject'];
155
				$values[] =  [ $label , $value['value'] ];
156
			}
157
			$dataSet[] = $values;
158
		}
159
160
		// Series plotting parameters
161
		foreach ( $data[$this->params['group']] as $key => $row ) {
162
			$series[] =  ['label' => $key,
163
			'xaxis' => 'xaxis', // xaxis could also be xaxis2 or ...
164
			'yaxis' => 'yaxis',
165
			'fill'  => $this->params['stackseries'],
166
			'showLine' => $this->params['charttype'] !== 'scatter',
167
			'showMarker' => true,
168
			'trendline' =>  [
169
				'show' => in_array( $this->params['trendline'], [ 'exp', 'linear' ] ),
170
				'shadow' => $this->params['theme'] !== 'simple',
171
				'type' => $this->params['trendline'],
172
			],
173
			'markerOptions' =>  [
174
				'style' => $marker[array_rand( $marker )],
175
				'shadow' => $this->params['theme'] !== 'simple'
176
			],
177
			'rendererOptions' =>  ['barDirection' => $this->params['direction'] ]
178
			];
179
		};
180
181
		// Basic parameters
182
		$parameters =  [
183
			'numbersaxislabel' => $this->params['numbersaxislabel'],
184
			'labelaxislabel'   => $this->params['labelaxislabel'],
185
			'charttitle'   => $this->params['charttitle'],
186
			'charttext'    => $this->params['charttext'],
187
			'infotext'     => $this->params['infotext'],
188
			'theme'        => $this->params['theme'] ? $this->params['theme'] : null,
189
			'valueformat'  => $this->params['datalabels'] === 'label' ? '' : $this->params['valueformat'],
190
			'ticklabels'   => $this->params['ticklabels'],
191
			'highlighter'  => $this->params['highlighter'],
192
			'autoscale'    => $options['autoscale'],
193
			'gridview'     => $this->params['gridview'],
194
			'direction'    => $this->params['direction'],
195
			'smoothlines'  => $this->params['smoothlines'],
196
			'cursor'       => $this->params['cursor'],
197
			'chartlegend'  => $this->params['chartlegend'] !== '' ? $this->params['chartlegend'] : 'none',
198
			'colorscheme'  => $this->params['colorscheme'] !== '' ? $this->params['colorscheme'] : null,
199
			'pointlabels'  => $this->params['datalabels'] === 'none' ? false : $this->params['datalabels'],
200
			'datalabels'   => $this->params['datalabels'],
201
			'stackseries'  => $this->params['stackseries'],
202
			'grid'         => $this->params['theme'] === 'vector' ?  [ 'borderColor' => '#a7d7f9' ] : ( $this->params['theme'] === 'simple' ?  [ 'borderColor' => '#ddd' ] : null ),
203
			'seriescolors' => $seriescolors
204
		];
205
206
		return  [
207
			'data'          => $dataSet,
208
			//'rawdata'      => $data , // control array
209
			'series'        => $series,
210
			'ticks'         => $data['numbersticks'],
211
			'total'         => $data['total'],
212
			'fcolumntypeid' => $data['fcolumntypeid'],
213
			'sask'          => $options['sask'],
214
			'mode'          => $options['mode'],
215
			'renderer'      => $this->params['charttype'],
216
			'parameters'    => $parameters
217
		];
218
	}
219
220
	/**
221
	 * Fetch numbers ticks
222
	 *
223
	 * @since 1.8
224
	 *
225
	 * @param array $data
226
	 */
227
	protected function getNumbersTicks( array $data ) {
228
229
		// Only look for numeric values that have been stored
230
		$numerics = array_values( $data['series'] );
231
232
		// Find min and max values to determine the graphs axis parameter
233
		$maxValue = count( $numerics ) == 0 ? 0 : max( array_map( "max", $numerics ) );
234
235
		if ( $this->params['min'] === false ) {
236
			$minValue = count( $numerics ) == 0 ? 0 : min( array_map( "min", $numerics ) );
237
		} else {
238
			$minValue = $this->params['min'];
239
		}
240
241
		// Get ticks info
242
		$data['numbersticks'] = SRFjqPlot::getNumbersTicks( $minValue, $maxValue );
243
		$data['total'] = array_sum( array_map( "array_sum", $numerics ) );
244
245
		return $data;
246
	}
247
248
	/**
249
	 * Add resource definitions
250
	 *
251
	 * @since 1.8
252
	 *
253
	 * @param array $data
254
	 *
255
	 * @return string
256
	 */
257
	protected function addResources() {
258
		// RL module
259
		switch ( $this->params['charttype'] ) {
260
			case 'bubble':
261
				SMWOutputs::requireResource( 'ext.srf.jqplot.bubble' );
262
				break;
263
			case 'donut':
264
				SMWOutputs::requireResource( 'ext.srf.jqplot.donut' );
265
				break;
266
			case 'scatter':
267
			case 'line':
268
			case 'bar':
269
				SMWOutputs::requireResource( 'ext.srf.jqplot.bar' );
270
				break;
271
		}
272
273
		// Trendline plugin
274
		if ( in_array( $this->params['trendline'], [ 'exp', 'linear' ] ) ) {
275
			SMWOutputs::requireResource( 'ext.srf.jqplot.trendline' );
276
		}
277
278
		// Cursor plugin
279
		if ( in_array( $this->params['cursor'], [ 'zoom', 'tooltip' ] ) ) {
280
			SMWOutputs::requireResource( 'ext.srf.jqplot.cursor' );
281
		}
282
283
		// Highlighter plugin
284
		if ( $this->params['highlighter'] ) {
285
			SMWOutputs::requireResource( 'ext.srf.jqplot.highlighter' );
286
		}
287
288
		// Enhancedlegend plugin
289
		if ( $this->params['chartlegend'] ) {
290
			SMWOutputs::requireResource( 'ext.srf.jqplot.enhancedlegend' );
291
		}
292
293
		// gridview plugin
294
		if ( in_array( $this->params['gridview'], [ 'tabs' ] ) ) {
295
			SMWOutputs::requireResource( 'ext.srf.util.grid' );
296
		}
297
298
		// Pointlabels plugin
299
		if ( in_array( $this->params['datalabels'], [ 'value', 'label', 'percent' ] ) ) {
300
			SMWOutputs::requireResource( 'ext.srf.jqplot.pointlabels' );
301
		}
302
	}
303
304
	/**
305
	 * Prepare data for the output
306
	 *
307
	 * @since 1.8
308
	 *
309
	 * @param array $data
310
	 *
311
	 * @return string
312
	 */
313
	protected function getFormatOutput( array $data ) {
314
315
		$this->isHTML = true;
316
317
		static $statNr = 0;
318
		$chartID = 'jqplot-series-' . ++$statNr;
319
320
		// Encoding
321
		$requireHeadItem =  [ $chartID => FormatJson::encode( $data )  ];
322
		SMWOutputs::requireHeadItem( $chartID, Skin::makeVariablesScript( $requireHeadItem ) );
323
324
		// Add RL resources
325
		$this->addResources();
326
327
		// Processing placeholder
328
		$processing = SRFUtils::htmlProcessingElement( $this->isHTML );
329
330
		// Conversion due to a string as value that can contain %
331
		$width = strstr( $this->params['width'] ,"%") ? $this->params['width'] : $this->params['width'] . 'px';
332
333
		// Chart/graph placeholder
334
		$chart = Html::rawElement( 'div', [
335
			'id'    => $chartID,
336
			'class' => 'container',
337
			'style' => "display:none; width: {$width}; height: {$this->params['height']}px;"
338
			], null
339
		);
340
341
		// Beautify class selector
342
		$class = $this->params['charttype'] ?  '-' . $this->params['charttype'] : '';
343
		$class = $this->params['class'] ? $class . ' ' . $this->params['class'] : $class . ' jqplot-common';
344
345
		// Chart/graph wrappper
346
		return Html::rawElement( 'div', [
347
			'class' => 'srf-jqplot' . $class,
348
			], $processing . $chart
349
		);
350
	}
351
352
	/**
353
	 * @see SMWResultPrinter::getParamDefinitions
354
	 *
355
	 * @since 1.8
356
	 *
357
	 * @param $definitions array of IParamDefinition
358
	 *
359
	 * @return array of IParamDefinition|array
360
	 */
361
	public function getParamDefinitions( array $definitions ) {
362
		$params = array_merge( parent::getParamDefinitions( $definitions ), SRFjqPlot::getCommonParams() );
363
364
		$params['infotext'] = [
365
			'message' => 'srf-paramdesc-infotext',
366
			'default' => '',
367
		];
368
369
		$params['stackseries'] = [
370
			'type' => 'boolean',
371
			'message' => 'srf-paramdesc-stackseries',
372
			'default' => false,
373
		];
374
375
		$params['group'] = [
376
			'message' => 'srf-paramdesc-group',
377
			'default' => 'subject',
378
			'values' => [ 'property' , 'subject' ],
379
		];
380
381
		$params['grouplabel'] = [
382
			'message' => 'srf-paramdesc-grouplabel',
383
			'default' => 'subject',
384
			'values' => [ 'property' , 'subject' ],
385
		];
386
387
		$params['charttype'] = [
388
			'message' => 'srf-paramdesc-charttype',
389
			'default' => 'bar',
390
			'values' => [ 'bar', 'line', 'donut', 'bubble', 'scatter' ],
391
		];
392
393
		$params['trendline'] = [
394
			'message' => 'srf-paramdesc-trendline',
395
			'default' => 'none',
396
			'values' => [ 'none', 'exp', 'linear' ],
397
		];
398
399
		$params['cursor'] = [
400
			'message' => 'srf-paramdesc-chartcursor',
401
			'default' => 'none',
402
			'values' => [ 'none', 'zoom', 'tooltip' ],
403
		];
404
405
		$params['gridview'] = [
406
			'message' => 'srf-paramdesc-gridview',
407
			'default' => 'none',
408
			'values' => [ 'none' , 'tabs' ],
409
		];
410
411
		return $params;
412
	}	
413
}