Completed
Pull Request — master (#467)
by
unknown
16:58
created

GanttPrinter::getResultText()   F

Complexity

Conditions 24
Paths 87

Size

Total Lines 117

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 117
rs 3.3333
c 0
b 0
f 0
cc 24
nc 87
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * SMW result printer for Gantt Diagrams using mermaidjs.
4
 * https://github.com/knsv/mermaid
5
 *
6
 * In order to use this printer you need to have
7
 * the Mermaid MediaWiki extension installed.
8
 * https://www.mediawiki.org/wiki/Extension:Mermaid
9
 *
10
 * @file Gantt.php
11
 * @ingroup SemanticResultFormats
12
 *
13
 * @licence GNU GPL v2+
14
 * @author Sebastian Schmid
15
 */
16
17
namespace SRF\Gantt;
18
19
use SMWOutputs;
20
use SMWQueryResult;
21
use SMWResultPrinter;
22
use SMWDITime;
23
use SMWDIBlob;
24
use Html;
25
26
class GanttPrinter extends SMWResultPrinter {
27
28
	protected $mParams = [];
29
	protected $mGantt = null;
30
	protected $mErrors = [];
31
32
	public function getName() {
33
		// Give grep a chance to find the usage
34
		return wfMessage( 'srf-printername-gantt' )->text();
35
	}
36
37
	public function getParamDefinitions( array $definitions ) {
38
		$params = parent::getParamDefinitions( $definitions );
39
40
		$params[] = [
41
			'name'    => 'diagramtitle',
42
			'message' => 'srf-paramdesc-diagramtitle',
43
			'default' => ''
44
		];
45
46
		$params[] = [
47
			'name'    => 'theme',
48
			'message' => 'srf-paramdesc-diagramtheme',
49
			'default' => 'default'
50
		];
51
52
		$params[] = [
53
			'name'    => 'axisformat',
54
			'message' => 'srf-paramdesc-axisformat',
55
			'default' => '%m/%d/%Y'
56
		];
57
58
		$params[] = [
59
			'name'    => 'sortkey',
60
			'message' => 'srf-paramdesc-sortkey',
61
			'default' => 'startdate'
62
		];
63
64
		$params[] = [
65
			'name'    => 'statusmapping',
66
			'message' => 'srf-paramdesc-statusmapping',
67
			'default' => ''
68
		];
69
70
		$params[] = [
71
			'name'    => 'prioritymapping',
72
			'message' => 'srf-paramdesc-prioritymapping',
73
			'default' => ''
74
		];
75
76
		$params[] = [
77
			'name'    => 'titletopmargin',
78
			'message' => 'srf-paramdesc-titletopmargin',
79
			'default' => 25
80
		];
81
82
		$params[] = [
83
			'name'    => 'barheight',
84
			'message' => 'srf-paramdesc-barheight',
85
			'default' => 20
86
		];
87
88
		$params[] = [
89
			'name'    => 'leftpadding',
90
			'message' => 'srf-paramdesc-leftpadding',
91
			'default' => 75
92
		];
93
94
		$params[] = [
95
			'name'    => 'bargap',
96
			'message' => 'srf-paramdesc-bargap',
97
			'default' => 4
98
		];
99
100
		return $params;
101
	}
102
103
	/**
104
	 * Handle (set) the result format parameters
105
	 *
106
	 * @see SMWResultPrinter::handleParameters()
107
	 */
108
	protected function handleParameters( array $params, $outputmode ) {
109
110
		parent::handleParameters( $params, $outputmode );
111
112
		//Set header params
113
		$this->mParams['title'] = trim( $params['diagramtitle'] );
114
		$this->mParams['axisformat'] = trim( $params['axisformat'] );
115
		$this->mParams['sortkey'] = trim( $params['sortkey'] );
116
		$this->mParams['statusmapping'] = trim( $params['statusmapping'] );
117
		$this->mParams['prioritymapping'] = trim( $params['prioritymapping'] );
118
		$this->mParams['theme'] = trim( $params['theme'] );
119
120
		//Validate Theme
121
		if ( !in_array( $this->params['theme'], [ "default", "neutral", "dark", "forest" ] ) ) {
122
			array_push( $this->mErrors, wfMessage( 'srf-error-theme' )->text() );
123
		}
124
125
		//Validate sortkey
126
		if ( !in_array( strtolower( $this->params['sortkey'] ), [ "title", "startdate", "enddate" ] ) ) {
127
			array_push( $this->mErrors, wfMessage( 'srf-error-sortkey' )->text() );
128
		}
129
130
		//Validate mapping
131
		if ( !empty( trim( $params['statusmapping'] ) ) ) {
132
133
			$paramMapping = explode( ';', trim( $params['statusmapping'] ) );
134
135
			foreach ( $paramMapping as $pm ) {
136
137
				// if no "=>" pattern was found
138
				if ( !strpos( $pm, '=>' ) ) {
139
					array_push( $this->mErrors, wfMessage( 'srf-error-mapping-assignment', 'statusmapping' )->text() );
140
				} else {
141
					$pmKeyVal = explode( '=>', $pm );
142
					// if no key value pair
143
					if ( count( $pmKeyVal ) % 2 != 0 ) {
144
						array_push( $this->mErrors,
145
							wfMessage( 'srf-error-mapping-assignment', 'statusmapping' )->text() );
146
					} else {
147
						$mapping[trim( $pmKeyVal[0] )] = trim( $pmKeyVal[1] );
0 ignored issues
show
Coding Style Comprehensibility introduced by
$mapping was never initialized. Although not strictly required by PHP, it is generally a good practice to add $mapping = array(); before regardless.

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:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

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 the bar 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.

Loading history...
148
						// check if the common status keys are used
149
						if ( trim( $pmKeyVal[1] ) != "active" && trim( $pmKeyVal[1] ) != "done" ) {
150
							array_push( $this->mErrors, wfMessage( 'srf-error-mapping-keywords' )->text() );
151
						}
152
					}
153
				}
154
			}
155
		}
156
		if ( !empty( trim( $params['prioritymapping'] ) ) ) {
157
158
			$paramMapping = explode( ';', trim( $params['prioritymapping'] ) );
159
160
			foreach ( $paramMapping as $pm ) {
161
162
				// if no "=>" pattern was found
163
				if ( !strpos( $pm, '=>' ) ) {
164
					array_push( $this->mErrors,
165
						wfMessage( 'srf-error-mapping-assignment', 'prioritymapping' )->text() );
166
				} else {
167
					$pmKeyVal = explode( '=>', $pm );
168
					// if no key value pair
169
					if ( count( $pmKeyVal ) % 2 != 0 ) {
170
						array_push( $this->mErrors,
171
							wfMessage( 'srf-error-mapping-assignment', 'statusmapping' )->text() );
172
					} else {
173
						$mapping[trim( $pmKeyVal[0] )] = trim( $pmKeyVal[1] );
0 ignored issues
show
Bug introduced by
The variable $mapping does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
174
						// check if the common status keys are used
175
						if ( trim( $pmKeyVal[1] ) != "crit" ) {
176
							array_push( $this->mErrors, wfMessage( 'srf-error-mapping-keywords' )->text() );
177
						}
178
					}
179
				}
180
			}
181
		}
182
183
		$this->mGantt = new Gantt( $this->mParams );
184
	}
185
186
	/**
187
	 * Return serialised results in specified format.
188
	 * @param SMWQueryResult $queryResult
189
	 * @param $outputmode
190
	 * @return string
191
	 */
192
	protected function getResultText( SMWQueryResult $queryResult, $outputmode ) {
193
194
		// Show warning if Extension:Mermaid is not available
195
		if ( !class_exists( 'Mermaid' ) && !class_exists( 'Mermaid\\MermaidParserFunction' ) ) {
196
			//wfWarn( 'The SRF Mermaid format needs the Mermaid extension to be installed.' );
197
		//catch ( Exception $exception ) {
198
				$queryResult->addErrors( ["Error: Mermaid Extension needs to be installed."] );
199
			//}
200
			return '';
201
		}
202
203
		// Load general Modules
204
		// First load the dependent modules of Mermaid ext
205
		SMWOutputs::requireResource( 'ext.mermaid' );
206
		SMWOutputs::requireResource( 'ext.mermaid.styles' );
207
		SMWOutputs::requireResource( 'ext.srf.gantt' );
208
209
		//Add Tasks & Sections
210
		while ( $row = $queryResult->getNext() ) {
211
212
			$status = [];
213
			$priority = [];
214
			$startDate = "";
215
			$endDate = "";
216
			$taskID = "";
217
			$taskTitle = "";
218
			$sections = [];
219
220
			// Loop through all field of a row
221
			foreach ( $row as $field ) {
222
223
				$fieldLabel = $field->getPrintRequest()->getLabel();
224
225
				//get values
226
				foreach ( $field->getContent() as $dataItem ) {
227
228
					switch ( $fieldLabel ) {
229
						case "section":
230
							$sections[$dataItem->getTitle()->getPrefixedDBKey()] = $dataItem->getSortKey();
231
							break;
232
						case "task":
233
							if ( $dataItem instanceof SMWDIBlob ) {
234
								$taskTitle = $dataItem->getString();
235
								$taskID = $field->getResultSubject()->getTitle()->getPrefixedDBKey();
236
							}
237
							break;
238
						case "startdate":
239
							if ( $dataItem instanceof SMWDITime ) {
240
								$startDate = $dataItem->getMwTimestamp();
241
							}
242
							break;
243
						case "enddate":
244
							if ( $dataItem instanceof SMWDITime ) {
245
								$endDate = $dataItem->getMwTimestamp();
246
							}
247
							break;
248
						case "status":
249
							if ( $dataItem instanceof SMWDIBlob ) {
250
								array_push( $status, $dataItem->getString() );
251
							}
252
							break;
253
						case "priority":
254
							if ( $dataItem instanceof SMWDIBlob ) {
255
								array_push( $priority, $dataItem->getString() );
256
							}
257
							break;
258
					}
259
				}
260
			}
261
262
			// Add section/Task
263
			// Title, TaskID, StartDate and EndDate are required
264
			if ( $taskID != "" && $taskTitle != "" && $startDate != "" && $endDate != "" ) {
265
				$this->mGantt->addTask( $taskID, $taskTitle, $status, $priority, $startDate, $endDate );
266
267
				// If no section was found, put task into a dummy section object
268
				// "gantt-no-section#21780240" is used to identify Tasks that with no section (dummy section)
269
				if ( count( $sections ) == 0 ) {
270
					$this->mGantt->addSection( "gantt-no-section#21780240", "", $startDate, $endDate, $taskID );
271
				} else {
272
					foreach ( $sections as $sectionID => $sectionTitle ) {
273
						$this->mGantt->addSection( $sectionID, $sectionTitle, $startDate, $endDate, $taskID );
274
					}
275
				}
276
			}
277
		}
278
279
		// Improve unique id by adding a random number
280
		$id = uniqid( 'srf-gantt-' . rand( 1, 10000 ) );
281
282
		// Add gantt configurations
283
		$config = [
284
			'theme' => $this->params['theme'],
285
			'gantt' => [
286
				'leftPadding'    => intval( $this->params['leftpadding'] ),
287
				'titleTopMargin' => intval( $this->params['titletopmargin'] ),
288
				'barHeight'      => intval( $this->params['barheight'] ),
289
				'barGap'         => intval( $this->params['bargap'] )
290
			]
291
		];
292
293
		// Manage Output
294
		if ( !empty( $this->mErrors ) ) {
295
			return $queryResult->addErrors( $this->mErrors );
296
		} else {
297
			return Html::rawElement( 'div', [
298
				'id'           => $id,
299
				'class'        => 'srf-gantt',
300
				'data-mermaid' => json_encode( [
301
					'content' => $this->mGantt->getGanttOutput(),
302
					'config'  => $config
303
				], JSON_UNESCAPED_UNICODE )
304
			], Html::rawElement( 'div', [
305
				'class' => 'mermaid-dots',
306
			] ) );
307
		}
308
	}
309
}