PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 4.0.2
Visualizer – Tables & Charts Manager with Built-in AI Generator v4.0.2
4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 3.10.4 All 148 releases
visualizer / classes / Visualizer / Source.php

Source.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 4.0.2, at classes/Visualizer/Source.php

478 lines 11.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // +----------------------------------------------------------------------+
3 // | Copyright 2013 Madpixels (email : visualizer@madpixels.net) |
4 // +----------------------------------------------------------------------+
5 // | This program is free software; you can redistribute it and/or modify |
6 // | it under the terms of the GNU General Public License, version 2, as |
7 // | published by the Free Software Foundation. |
8 // | |
9 // | This program is distributed in the hope that it will be useful, |
10 // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 // | GNU General Public License for more details. |
13 // | |
14 // | You should have received a copy of the GNU General Public License |
15 // | along with this program; if not, write to the Free Software |
16 // | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, |
17 // | MA 02110-1301 USA |
18 // +----------------------------------------------------------------------+
19 // | Author: Eugene Manuilov <eugene@manuilov.org> |
20 // +----------------------------------------------------------------------+
21 /**
22 * The abstract class for source managers.
23 *
24 * @category Visualizer
25 * @package Source
26 *
27 * @since 1.0.0
28 * @abstract
29 */
30 abstract class Visualizer_Source {
31
32 /**
33 * The array of allowed types.
34 *
35 * @since 1.0.0
36 *
37 * @access protected
38 * @var array
39 */
40 protected static $allowed_types = array( 'string', 'number', 'boolean', 'date', 'datetime', 'timeofday' );
41 /**
42 * The array of data.
43 *
44 * @since 1.0.0
45 *
46 * @access protected
47 * @var array
48 */
49 protected $_data = array();
50 /**
51 * The array of series.
52 *
53 * @since 1.0.0
54 *
55 * @access protected
56 * @var array
57 */
58 protected $_series = array();
59
60 /**
61 * The error message.
62 *
63 * @access protected
64 * @var string
65 */
66 protected $_error;
67
68 /**
69 * Return allowed types
70 *
71 * @since 1.0.1
72 *
73 * @static
74 * @access public
75 * @return array the allowed types
76 */
77 public static function getAllowedTypes() {
78 return self::$allowed_types;
79 }
80
81 /**
82 * Validates series tyeps.
83 *
84 * @since 1.0.1
85 *
86 * @static
87 * @access protected
88 *
89 * @param array $types The icoming series types.
90 *
91 * @return boolean TRUE if sereis types are valid, otherwise FALSE.
92 */
93 protected static function _validateTypes( $types ) {
94 foreach ( $types as $type ) {
95 if ( ! in_array( $type, self::$allowed_types, true ) ) {
96 return false;
97 }
98 }
99
100 return true;
101 }
102
103 /**
104 * Returns source name.
105 *
106 * @since 1.0.0
107 *
108 * @abstract
109 * @access public
110 * @return string The name of source.
111 */
112 abstract public function getSourceName();
113
114 /**
115 * Fetches information from source, parses it and builds series and data arrays.
116 *
117 * @since 1.0.0
118 *
119 * @abstract
120 * @access public
121 */
122 abstract public function fetch();
123
124 /**
125 * Returns series parsed from source.
126 *
127 * @since 1.0.0
128 *
129 * @access public
130 * @return array The array of series.
131 */
132 public function getSeries() {
133 return $this->_series;
134 }
135
136 /**
137 * Returns data parsed from source.
138 *
139 * @since 1.0.0
140 *
141 * @access public
142 * @return string The serialized array of data.
143 */
144 public function getData( $fetch_from_editable_table = false ) {
145 if ( $fetch_from_editable_table ) {
146 $this->_fetchDataFromEditableTable();
147 }
148 return serialize( $this->_data );
149 }
150
151 /**
152 * Returns raw data array.
153 *
154 * @since 1.1.0
155 *
156 * @access public
157 * @return array
158 */
159 public function getRawData( $fetch_from_editable_table = false ) {
160 if ( $fetch_from_editable_table ) {
161 $this->_fetchDataFromEditableTable();
162 }
163 return $this->_data;
164 }
165
166 /**
167 * Re populates series if the source is dynamic.
168 *
169 * @since 1.1.0
170 *
171 * @access public
172 *
173 * @param array $series The actual array of series.
174 * @param int $chart_id The chart id.
175 *
176 * @return array The re populated array of series or old one.
177 */
178 public function repopulateSeries( $series, $chart_id ) {
179 return $series;
180 }
181
182 /**
183 * Re populates data if the source is dynamic.
184 *
185 * @since 1.1.0
186 *
187 * @access public
188 *
189 * @param array $data The actual array of data.
190 * @param int $chart_id The chart id.
191 *
192 * @return array The re populated array of data or old one.
193 */
194 public function repopulateData( $data, $chart_id ) {
195 return $data;
196 }
197
198 /**
199 * Normalizes values according to series' type.
200 *
201 * @since 1.0.0
202 *
203 * @access protected
204 *
205 * @param array $data The row of data.
206 *
207 * @return array Normalized row of data.
208 */
209 protected function _normalizeData( $data ) {
210 // normalize values
211 foreach ( $this->_series as $i => $series ) {
212 // if no value exists for the seires, then add null
213 if ( ! isset( $data[ $i ] ) ) {
214 $data[ $i ] = null;
215 }
216 if ( is_null( $data[ $i ] ) ) {
217 continue;
218 }
219 switch ( $series['type'] ) {
220 case 'number':
221 $data[ $i ] = preg_replace( '/[\x{200B}-\x{200D}\x{FEFF}]/u', '', $data[ $i ] );
222 $data[ $i ] = ( is_numeric( $data[ $i ] ) ) ? floatval( $data[ $i ] ) : ( is_numeric( str_replace( ',', '', $data[ $i ] ) ) ? floatval( str_replace( ',', '', $data[ $i ] ) ) : null );
223 break;
224 case 'boolean':
225 $datum = trim( strval( $data[ $i ] ) );
226 $data[ $i ] = in_array( $datum, array( 'true', 'yes', '1' ), true ) ? 'true' : 'false';
227 break;
228 case 'timeofday':
229 $date = new DateTime( '1984-03-16T' . $data[ $i ] );
230 if ( $date ) {
231 $data[ $i ] = array(
232 intval( $date->format( 'H' ) ),
233 intval( $date->format( 'i' ) ),
234 intval( $date->format( 's' ) ),
235 0,
236 );
237 }
238 break;
239 case 'datetime':
240 // let's check if the date is a Unix epoch
241 $value = DateTime::createFromFormat( 'U', $data[ $i ] );
242 if ( $value !== false && ! is_wp_error( $value ) ) {
243 $data[ $i ] = $value->format( 'Y-m-d H:i:s' );
244 }
245 break;
246 case 'string':
247 // if a ' is provided, strip the backslash
248 $data[ $i ] = stripslashes( $this->toUTF8( $data[ $i ] ) );
249 break;
250 }
251 }
252
253 return apply_filters( 'visualizer_format_data', $data, $this->_series );
254 }
255
256
257 /**
258 * Converts values to UTF8, if required.
259 *
260 * @access protected
261 *
262 * @param string $datum The data to convert.
263 *
264 * @return string The converted data.
265 */
266 final protected function toUTF8( $datum ) {
267 if ( ! function_exists( 'mb_detect_encoding' ) || mb_detect_encoding( $datum ) !== 'ASCII' ) {
268 $datum = \ForceUTF8\Encoding::toUTF8( $datum );
269 }
270 return $datum;
271 }
272
273 /**
274 * Determines the formats of date/time columns.
275 *
276 * @access public
277 *
278 * @param array $series The actual array of series.
279 * @param array $data The actual array of data.
280 *
281 * @return array
282 */
283 final public static function get_date_formats_if_exists( $series, $data ) {
284 $date_formats = array();
285 $types = array();
286 $index = 0;
287 foreach ( $series as $column ) {
288 if ( in_array( $column['type'], array( 'date', 'datetime', 'timeofday' ), true ) ) {
289 $types[] = array( 'index' => $index, 'type' => $column['type'] );
290 }
291 ++$index;
292 }
293
294 if ( ! $types ) {
295 return $date_formats;
296 }
297
298 $random = $data;
299 // let's randomly pick 5 data points instead of cycling through the entire data set.
300 if ( count( $data ) > 5 ) {
301 $random = array();
302 for ( $x = 0; $x < 5; $x++ ) {
303 $random[] = $data[ rand( 0, count( $data ) - 1 ) ];
304 }
305 }
306
307 foreach ( $types as $type ) {
308 $formats = array();
309 foreach ( $random as $datum ) {
310 $f = self::determine_date_format( $datum[ $type['index'] ], $type['type'] );
311 if ( $f ) {
312 $formats[] = $f;
313 }
314 }
315 // if there are multiple formats, use the most frequent format.
316 $formats = array_filter( $formats );
317 if ( $formats ) {
318 $formats = array_count_values( $formats );
319 arsort( $formats );
320 $formats = array_keys( $formats );
321 $final_format = reset( $formats );
322 // we have determined the PHP format; now we have to change this into the JS format where m = MM, d = DD etc.
323 $date_formats[] = array( 'index' => $type['index'], 'format' => str_replace( array( 'Y', 'm', 'd', 'H', 'i', 's' ), array( 'YYYY', 'MM', 'DD', 'HH', 'mm', 'ss' ), $final_format ) );
324 }
325 }
326 return $date_formats;
327 }
328
329 /**
330 * Determines the date/time format of the given string.
331 *
332 * @access private
333 *
334 * @param string $value The string.
335 * @param string $type 'date', 'timeofday' or 'datetime'.
336 *
337 * @return string|null
338 */
339 private static function determine_date_format( $value, $type ) {
340 if ( version_compare( phpversion(), '5.3.0', '<' ) ) {
341 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'PHP version %s not supported', phpversion() ), 'error', __FILE__, __LINE__ );
342 return null;
343 }
344
345 $formats = array(
346 'Y/m/d',
347 'Y-m-d',
348 'm/d/Y',
349 'm-d-Y',
350 'd-m-Y',
351 'd/m/Y',
352 );
353
354 switch ( $type ) {
355 case 'datetime':
356 $formats = array_merge(
357 $formats, array(
358 'U',
359 'Y/m/d H:i:s',
360 'Y-m-d H:i:s',
361 'm/d/Y H:i:s',
362 'm-d-Y H:i:s',
363 )
364 );
365 break;
366 case 'timeofday':
367 $formats = array_merge(
368 $formats, array(
369 'H:i:s',
370 'H:i',
371 )
372 );
373 break;
374 }
375
376 $formats = apply_filters( 'visualizer_date_formats', $formats, $type );
377
378 foreach ( $formats as $format ) {
379 $return = DateTime::createFromFormat( $format, $value );
380 if ( $return !== false && ! is_wp_error( $return ) ) {
381 return $format;
382 }
383 }
384 // invalid format
385 return null;
386 }
387
388 /**
389 * Returns the error, if any.
390 *
391 * @access public
392 * @return string
393 */
394 public function get_error() {
395 return $this->_error;
396 }
397
398 /**
399 * Fetches information from the editable table and parses it to build series and data arrays.
400 *
401 * @since ?
402 *
403 * @access public
404 * @return boolean TRUE on success, otherwise FALSE.
405 */
406 public function fetchFromEditableTable() {
407 if ( empty( $this->_args ) ) {
408 return false;
409 }
410
411 $this->_fetchSeriesFromEditableTable();
412 $this->_fetchDataFromEditableTable();
413 return true;
414 }
415
416 /**
417 * Fetches series information from the editable table. This is fetched only through the UI and not while refreshing the chart data.
418 *
419 * @since 1.0.0
420 *
421 * @access private
422 */
423 private function _fetchSeriesFromEditableTable() {
424 $params = $this->_args;
425 $headers = array_filter( $params['header'] );
426 $types = array_filter( $params['type'] );
427 $header_row = $type_row = array();
428 if ( $headers ) {
429 foreach ( $headers as $header ) {
430 if ( ! empty( $types[ $header ] ) ) {
431 $this->_series[] = array(
432 'label' => sanitize_text_field( wp_strip_all_tags( $header ) ),
433 'type' => $types[ $header ],
434 );
435 }
436 }
437 }
438
439 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Series found for %s = %s', print_r( $this->_args, true ), print_r( $this->_series, true ) ), 'debug', __FILE__, __LINE__ );
440
441 return true;
442 }
443
444
445 /**
446 * Fetches data information from the editable table.
447 *
448 * @since 1.0.0
449 *
450 * @access private
451 */
452 private function _fetchDataFromEditableTable() {
453 $headers = wp_list_pluck( $this->_series, 'label' );
454 $this->fetch();
455
456 $data = $this->_data;
457 $this->_data = array();
458
459 foreach ( $data as $line ) {
460 $data_row = array();
461 // we have to make sure we are fetching the data in the right order
462 // in case the columns have been reordered
463 foreach ( $headers as $header ) {
464 $value = $line[ $header ];
465 // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
466 if ( in_array( $header, $headers ) ) {
467 $data_row[] = $value;
468 }
469 }
470 $this->_data[] = $this->_normalizeData( $data_row );
471 }
472
473 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Data found for %s = %s', print_r( $this->_args, true ), print_r( $this->_data, true ) ), 'debug', __FILE__, __LINE__ );
474
475 return true;
476 }
477 }
478