Filename | /var/www/foswikidev/core/lib/Foswiki/Plugins/ChartPlugin.pm |
Statements | Executed 340 statements in 2.91ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
108 | 1 | 1 | 353µs | 353µs | commonTagsHandler | Foswiki::Plugins::ChartPlugin::
1 | 1 | 1 | 17µs | 102µs | initPlugin | Foswiki::Plugins::ChartPlugin::
1 | 1 | 1 | 14µs | 28µs | BEGIN@39 | Foswiki::Plugins::ChartPlugin::
1 | 1 | 1 | 10µs | 141µs | BEGIN@42 | Foswiki::Plugins::ChartPlugin::
0 | 0 | 0 | 0s | 0s | ChartPlugin | Foswiki::Plugins::ChartPlugin::
0 | 0 | 0 | 0s | 0s | _Parameters | Foswiki::Plugins::ChartPlugin::
0 | 0 | 0 | 0s | 0s | _init_defaults | Foswiki::Plugins::ChartPlugin::
0 | 0 | 0 | 0s | 0s | _makeChart | Foswiki::Plugins::ChartPlugin::
0 | 0 | 0 | 0s | 0s | _make_error | Foswiki::Plugins::ChartPlugin::
0 | 0 | 0 | 0s | 0s | _make_filename | Foswiki::Plugins::ChartPlugin::
0 | 0 | 0 | 0s | 0s | _max | Foswiki::Plugins::ChartPlugin::
0 | 0 | 0 | 0s | 0s | _min | Foswiki::Plugins::ChartPlugin::
0 | 0 | 0 | 0s | 0s | _setParameters | Foswiki::Plugins::ChartPlugin::
0 | 0 | 0 | 0s | 0s | _setTables | Foswiki::Plugins::ChartPlugin::
0 | 0 | 0 | 0s | 0s | _setTopicContents | Foswiki::Plugins::ChartPlugin::
0 | 0 | 0 | 0s | 0s | _tables | Foswiki::Plugins::ChartPlugin::
0 | 0 | 0 | 0s | 0s | _timeit | Foswiki::Plugins::ChartPlugin::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # ChartPlugin for Foswiki - The Free and Open Source Wiki, http://foswiki.org/ | ||||
2 | # | ||||
3 | # Copyright (C) 2004-2006 Peter Thoeny, Peter@Thoeny.org | ||||
4 | # Plugin written by http://TWiki.org/cgi-bin/view/Main/TaitCyrus | ||||
5 | # Copyright (C) 2008-2011 Foswiki Contributors | ||||
6 | # | ||||
7 | # For licensing info read LICENSE file in the Foswiki root. | ||||
8 | # This program is free software; you can redistribute it and/or | ||||
9 | # modify it under the terms of the GNU General Public License | ||||
10 | # as published by the Free Software Foundation; either version 2 | ||||
11 | # of the License, or (at your option) any later version. | ||||
12 | # | ||||
13 | # This program is distributed in the hope that it will be useful, | ||||
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
16 | # GNU General Public License for more details, published at | ||||
17 | # http://www.gnu.org/copyleft/gpl.html | ||||
18 | # | ||||
19 | # As per the GPL, removal of this notice is prohibited. | ||||
20 | # | ||||
21 | # ========================= | ||||
22 | # | ||||
23 | # This file contains routines for producing PNG graphic files containing | ||||
24 | # chart information, useful for building dashboards. | ||||
25 | # NOTE: ONLY in the case where an old version of GD (1.19 or earlier) is | ||||
26 | # available will GIF's be created. If the GD version is > 1.19, then | ||||
27 | # PNG's are created. | ||||
28 | # | ||||
29 | # This plugin uses Perl object oriented programming. The ChartPlugin | ||||
30 | # object contains several other Perl objects: | ||||
31 | # Table | ||||
32 | # Parameters | ||||
33 | # Chart | ||||
34 | # In addition to having it's own getter/setters. | ||||
35 | |||||
36 | # ========================= | ||||
37 | package Foswiki::Plugins::ChartPlugin; | ||||
38 | |||||
39 | 2 | 44µs | 2 | 41µs | # spent 28µs (14+13) within Foswiki::Plugins::ChartPlugin::BEGIN@39 which was called:
# once (14µs+13µs) by Foswiki::Plugin::BEGIN@2.5 at line 39 # spent 28µs making 1 call to Foswiki::Plugins::ChartPlugin::BEGIN@39
# spent 13µs making 1 call to strict::import |
40 | |||||
41 | # ========================= | ||||
42 | 1 | 6µs | 1 | 131µs | # spent 141µs (10+131) within Foswiki::Plugins::ChartPlugin::BEGIN@42 which was called:
# once (10µs+131µs) by Foswiki::Plugin::BEGIN@2.5 at line 50 # spent 131µs making 1 call to vars::import |
43 | $installWeb $VERSION $RELEASE $debug | ||||
44 | $pluginInitialized $initError | ||||
45 | $defaultType @defaultAreaColors @defaultLineColors | ||||
46 | $defaultWidth $defaultHeight $defaultBGcolor $defaultNumYGrids | ||||
47 | $defaultDataValue $defaultScale $defaultGridColor $defaultPointSize | ||||
48 | $defaultLineWidth | ||||
49 | $defaultBarLeadingSpace $defaultBarTrailingSpace $defaultBarSpace | ||||
50 | 1 | 2.50ms | 1 | 141µs | ); # spent 141µs making 1 call to Foswiki::Plugins::ChartPlugin::BEGIN@42 |
51 | |||||
52 | 1 | 700ns | our $VERSION = '1.6.1'; | ||
53 | 1 | 200ns | our $RELEASE = '1.6.1'; | ||
54 | 1 | 300ns | our $SHORT_DESCRIPTION = | ||
55 | 'Create area, bar, line and scatter charts to visualize table data'; | ||||
56 | |||||
57 | 1 | 200ns | $pluginInitialized = 0; | ||
58 | 1 | 200ns | $initError = ''; | ||
59 | |||||
60 | # ========================= | ||||
61 | # spent 102µs (17+86) within Foswiki::Plugins::ChartPlugin::initPlugin which was called:
# once (17µs+86µs) by Foswiki::Plugin::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Plugin.pm:257] at line 250 of /var/www/foswikidev/core/lib/Foswiki/Plugin.pm | ||||
62 | 1 | 3µs | ( my $topic, my $web, my $user, $installWeb ) = @_; | ||
63 | |||||
64 | # check for Plugins.pm versions | ||||
65 | 1 | 11µs | 1 | 5µs | if ( $Foswiki::Plugins::VERSION < 1 ) { # spent 5µs making 1 call to version::vxs::VCMP |
66 | &Foswiki::Func::writeWarning( | ||||
67 | "Version mismatch between ChartPlugin and Plugins.pm"); | ||||
68 | return 0; | ||||
69 | } | ||||
70 | |||||
71 | # Get plugin debug flag | ||||
72 | 1 | 2µs | 1 | 80µs | $debug = &Foswiki::Func::getPreferencesFlag("CHARTPLUGIN_DEBUG") || 0; # spent 80µs making 1 call to Foswiki::Func::getPreferencesFlag |
73 | |||||
74 | 1 | 500ns | &Foswiki::Func::writeDebug( | ||
75 | "- Foswiki::Plugins::ChartPlugin::initPlugin($web.$topic) is OK") | ||||
76 | if $debug; | ||||
77 | |||||
78 | # Mark that we are not fully initialized yet. Only get the default | ||||
79 | # values from the plugin topic page iff a CHART is found in a topic | ||||
80 | 1 | 300ns | $pluginInitialized = 0; | ||
81 | 1 | 4µs | return 1; | ||
82 | } | ||||
83 | |||||
84 | # ========================= | ||||
85 | |||||
86 | # Initialize all default values from the plugin topic page. | ||||
87 | sub _init_defaults { | ||||
88 | return if $pluginInitialized; | ||||
89 | $pluginInitialized = 1; | ||||
90 | require Exporter; | ||||
91 | foreach my $module ( | ||||
92 | qw( GD POSIX | ||||
93 | Foswiki::Plugins::ChartPlugin::Chart | ||||
94 | Foswiki::Plugins::ChartPlugin::Parameters | ||||
95 | Foswiki::Plugins::ChartPlugin::Table) | ||||
96 | ) | ||||
97 | { | ||||
98 | eval "require $module"; | ||||
99 | if ($@) { | ||||
100 | $initError = "Required Perl module '$module' not found: $@"; | ||||
101 | return; | ||||
102 | } | ||||
103 | } | ||||
104 | |||||
105 | # Get default chart type | ||||
106 | $defaultType = Foswiki::Func::getPreferencesValue("CHARTPLUGIN_TYPE") | ||||
107 | || 'line'; | ||||
108 | |||||
109 | # Get default chart values | ||||
110 | $defaultWidth = &Foswiki::Func::getPreferencesValue("CHARTPLUGIN_WIDTH") | ||||
111 | || 60; | ||||
112 | $defaultHeight = &Foswiki::Func::getPreferencesValue("CHARTPLUGIN_HEIGHT") | ||||
113 | || 16; | ||||
114 | my $defaultAreaColors = | ||||
115 | &Foswiki::Func::getPreferencesValue("CHARTPLUGIN_AREA_COLORS") | ||||
116 | || "#FF0000 #FFFF00 #00FF00"; | ||||
117 | @defaultAreaColors = split( /[\s,]+/, $defaultAreaColors ); | ||||
118 | my $defaultLineColors = | ||||
119 | &Foswiki::Func::getPreferencesValue("CHARTPLUGIN_LINE_COLORS") | ||||
120 | || "#FFFF00 #FF00FF #00FFFF"; | ||||
121 | @defaultLineColors = split( /[\s,]+/, $defaultLineColors ); | ||||
122 | |||||
123 | # Get default chart bgcolor | ||||
124 | $defaultBGcolor = &Foswiki::Func::getPreferencesValue("CHARTPLUGIN_BGCOLOR") | ||||
125 | || '#FFFFFF #FFFFFF'; | ||||
126 | |||||
127 | # Get default number of Y axis grids | ||||
128 | $defaultNumYGrids = | ||||
129 | &Foswiki::Func::getPreferencesValue("CHARTPLUGIN_NUMYGRIDS") || 10; | ||||
130 | |||||
131 | # Get default value to use if there is no data seen in the table | ||||
132 | $defaultDataValue = | ||||
133 | &Foswiki::Func::getPreferencesValue("CHARTPLUGIN_DEFAULTDATA"); | ||||
134 | |||||
135 | # Get default value for the scale (linear/semilog) | ||||
136 | $defaultScale = &Foswiki::Func::getPreferencesValue("CHARTPLUGIN_SCALE"); | ||||
137 | |||||
138 | # Get default grid color. | ||||
139 | $defaultGridColor = | ||||
140 | &Foswiki::Func::getPreferencesValue("CHARTPLUGIN_GRIDCOLOR") || '#000000'; | ||||
141 | |||||
142 | # Get default value for the size, in pixels, of drawn data points | ||||
143 | $defaultPointSize = | ||||
144 | &Foswiki::Func::getPreferencesValue("CHARTPLUGIN_POINTSIZE") || 2; | ||||
145 | |||||
146 | # Get default value for the width, in pixels, of drawn lines | ||||
147 | $defaultLineWidth = | ||||
148 | &Foswiki::Func::getPreferencesValue("CHARTPLUGIN_LINEWIDTH") || 3; | ||||
149 | |||||
150 | # Get default value for the leading space before the first bar. | ||||
151 | $defaultBarLeadingSpace = | ||||
152 | &Foswiki::Func::getPreferencesValue("CHARTPLUGIN_BARLEADINGSPACE") || 0; | ||||
153 | |||||
154 | # Get default value for the trailing space after the last bar. | ||||
155 | $defaultBarTrailingSpace = | ||||
156 | &Foswiki::Func::getPreferencesValue("CHARTPLUGIN_BARTRAILINGSPACE") || 0; | ||||
157 | |||||
158 | # Get default value for the space between bars. | ||||
159 | $defaultBarSpace = | ||||
160 | &Foswiki::Func::getPreferencesValue("CHARTPLUGIN_BARSPACE") || 0; | ||||
161 | } | ||||
162 | |||||
163 | # Object constructor for creating a ChartPlugin Perl object. The object is | ||||
164 | # initialized with the current web.topic. | ||||
165 | sub ChartPlugin { | ||||
166 | my ( $currentTopic, $currentWeb, $currentTopicContents ) = @_; | ||||
167 | my $this = {}; | ||||
168 | bless $this; | ||||
169 | $this->{CURRENT_TOPIC} = $currentTopic; | ||||
170 | $this->{CURRENT_WEB} = $currentWeb; | ||||
171 | $this->{CURRENT_TOPICONTENTS} = $currentTopicContents; | ||||
172 | return $this; | ||||
173 | } | ||||
174 | |||||
175 | # Setter for storing the Table object | ||||
176 | sub _setTables { my ( $this, $table ) = @_; $this->{TABLES} = $table; } | ||||
177 | |||||
178 | # Getter for Table object | ||||
179 | sub _tables { my ($this) = @_; return $this->{TABLES}; } | ||||
180 | |||||
181 | # Setter for storing the Parameters object | ||||
182 | sub _setParameters { | ||||
183 | my ( $this, $args ) = @_; | ||||
184 | $this->{PARAMETERS} = Foswiki::Plugins::ChartPlugin::Parameters->new($args); | ||||
185 | } | ||||
186 | |||||
187 | # Getter for Parameters object | ||||
188 | sub _Parameters { my ($this) = @_; return $this->{PARAMETERS}; } | ||||
189 | |||||
190 | # This routine sets the specified web.topic as the location from where to | ||||
191 | # get the table information. If the specified web.topic happen to be the | ||||
192 | # same as the web.topic from which the %CHART% was found, then the | ||||
193 | # web.topic contents is already part of the ChartPlugin object so there is | ||||
194 | # nothing to do. Otherwise, this routine will read in the specified | ||||
195 | # web.topic getting its contents and using that as the source to parse out | ||||
196 | # table information. | ||||
197 | sub _setTopicContents { | ||||
198 | my ( $this, $inWeb, $inTopic ) = @_; | ||||
199 | my $topicContents; | ||||
200 | |||||
201 | # If $inWeb and $inTopic match the current web/topic, then we already | ||||
202 | # have the topic contents in the object so there is nothing to do. | ||||
203 | # Otherwise, we need to open the specified web/topic and read in its | ||||
204 | # contents. | ||||
205 | if ( ( $inWeb eq $this->{CURRENT_WEB} ) | ||||
206 | && ( $inTopic eq $this->{CURRENT_TOPIC} ) ) | ||||
207 | { | ||||
208 | $topicContents = $this->{CURRENT_TOPICONTENTS}; | ||||
209 | } | ||||
210 | else { | ||||
211 | |||||
212 | # A difference, so read in the topic. | ||||
213 | ( my $meta, $topicContents ) = | ||||
214 | Foswiki::Func::readTopic( $inWeb, $inTopic ); | ||||
215 | |||||
216 | # Check to make sure the web.topic actually exists. If not, return | ||||
217 | # undef so the caller can catch the error. | ||||
218 | return undef if ( !defined($topicContents) || $topicContents eq "" ); | ||||
219 | $topicContents = | ||||
220 | Foswiki::Func::expandCommonVariables( $topicContents, $inTopic, | ||||
221 | $inWeb ); | ||||
222 | } | ||||
223 | |||||
224 | # Lets parse the specified topic contents looking for tables. | ||||
225 | $this->_setTables( | ||||
226 | Foswiki::Plugins::ChartPlugin::Table->new($topicContents) ); | ||||
227 | return 1; | ||||
228 | } | ||||
229 | |||||
230 | # Return the maximum value of the two specified numbers. | ||||
231 | sub _max { | ||||
232 | my ( $v1, $v2 ) = @_; | ||||
233 | return $v1 if ( $v1 > $v2 ); | ||||
234 | return $v2; | ||||
235 | } | ||||
236 | |||||
237 | # Return the minimum value of the two specified numbers. | ||||
238 | sub _min { | ||||
239 | my ( $v1, $v2 ) = @_; | ||||
240 | return $v1 if ( $v1 < $v2 ); | ||||
241 | return $v2; | ||||
242 | } | ||||
243 | |||||
244 | # Generate the file name in which the graphic file will be placed. | ||||
245 | sub _make_filename { | ||||
246 | my ( $type, $name ) = @_; | ||||
247 | |||||
248 | # Generate the file name to be created | ||||
249 | my $fullname; | ||||
250 | |||||
251 | # If GD version 1.19 or earlier, then create gif files else png files. | ||||
252 | if ( $GD::VERSION > 1.19 ) { | ||||
253 | $fullname = "_ChartPlugin_${type}_${name}.png"; | ||||
254 | } | ||||
255 | else { | ||||
256 | $fullname = "_ChartPlugin_${type}_${name}.gif"; | ||||
257 | } | ||||
258 | |||||
259 | return $fullname; | ||||
260 | } | ||||
261 | |||||
262 | # This routine returns an red colored error message. | ||||
263 | sub _make_error { | ||||
264 | my ($msg) = @_; | ||||
265 | return "<font color=red>ChartPlugin error: $msg</font>"; | ||||
266 | } | ||||
267 | |||||
268 | # Actually construct the chart by parsing out each of the %CHART% | ||||
269 | # parameters, putting the parameters into the chart object, and then | ||||
270 | # creating the chart. | ||||
271 | sub _makeChart { | ||||
272 | my ( $this, $args, $topic, $web ) = @_; | ||||
273 | |||||
274 | # Check to see if the GD module was found. If not, then create an | ||||
275 | # error message to display back to the user. | ||||
276 | if ($initError) { | ||||
277 | |||||
278 | # It appears that a library wasn't found so we return a | ||||
279 | # different type of error that is just plain text. | ||||
280 | return _make_error($initError); | ||||
281 | } | ||||
282 | |||||
283 | # Set/parse the %CHART% parameters putting into the ChartPlugin object | ||||
284 | $this->_setParameters($args); | ||||
285 | |||||
286 | # Make a chart object in which we will place user specified parameters | ||||
287 | my $chart = Foswiki::Plugins::ChartPlugin::Chart->new(); | ||||
288 | |||||
289 | # See if the parameter 'type' is available. This is a required | ||||
290 | # parameter. If it is missing, then generate an error message. | ||||
291 | my $type = $this->_Parameters->getParameter( "type", $defaultType ); | ||||
292 | return _make_error("parameter *type* must be specified") | ||||
293 | if ( !defined $type ); | ||||
294 | my @unknownTypes = grep( !/area|line|bar|arealine|combo|scatter/, ($type) ); | ||||
295 | |||||
296 | # Check for a valid type | ||||
297 | return _make_error("Invalid value of *$type* for parameter *type* ") | ||||
298 | if (@unknownTypes); | ||||
299 | $chart->setType($type); | ||||
300 | |||||
301 | # See if the parameter 'subtype' (old name 'datatype') is available. | ||||
302 | my $dataType = $this->_Parameters->getParameter( "datatype", undef ); | ||||
303 | my $subType = $this->_Parameters->getParameter( "subtype", undef ); | ||||
304 | return _make_error( | ||||
305 | "paramters *datatype* and *subtype* can't both be specified") | ||||
306 | if ( defined $dataType && defined $subType ); | ||||
307 | $subType = $dataType if ( defined $dataType ); | ||||
308 | if ( defined $subType ) { | ||||
309 | my @subTypes = split( /[\s,]+/, $subType ); | ||||
310 | |||||
311 | # Check for valid subtypes | ||||
312 | my @unknownSubTypes = | ||||
313 | grep( !/area|line|point|pline|scatter|bar/, @subTypes ); | ||||
314 | return _make_error( | ||||
315 | "unknown subtypes: " . join( ", ", @unknownSubTypes ) ) | ||||
316 | if (@unknownSubTypes); | ||||
317 | |||||
318 | # Now check to make sure that the subtypes specified are valid for the | ||||
319 | # specified type. | ||||
320 | ### Check 'line' type | ||||
321 | if ( $type eq "line" ) { | ||||
322 | @unknownSubTypes = grep( !/line|point|pline/, @subTypes ); | ||||
323 | return _make_error( "unsupported subtypes: " | ||||
324 | . join( ", ", @unknownSubTypes ) | ||||
325 | . " for type line" ) | ||||
326 | if (@unknownSubTypes); | ||||
327 | } | ||||
328 | |||||
329 | ### Check 'area' type | ||||
330 | if ( $type eq "area" ) { | ||||
331 | @unknownSubTypes = grep( !/area/, @subTypes ); | ||||
332 | return _make_error( "unsupported subtypes: " | ||||
333 | . join( ", ", @unknownSubTypes ) | ||||
334 | . " for type area" ) | ||||
335 | if (@unknownSubTypes); | ||||
336 | } | ||||
337 | |||||
338 | ### Check 'scatter' type | ||||
339 | if ( $type eq "scatter" ) { | ||||
340 | @unknownSubTypes = grep( !/area|line|point|pline|bar/, @subTypes ); | ||||
341 | return _make_error( "unsupported subtypes: " | ||||
342 | . join( ", ", @unknownSubTypes ) | ||||
343 | . " for type scatter" ) | ||||
344 | if (@unknownSubTypes); | ||||
345 | } | ||||
346 | |||||
347 | ### Check 'combo' type | ||||
348 | if ( $type eq "combo" ) { | ||||
349 | @unknownSubTypes = grep( !/area|line|point|pline|bar/, @subTypes ); | ||||
350 | return _make_error( "unsupported subtypes: " | ||||
351 | . join( ", ", @unknownSubTypes ) | ||||
352 | . " for type combo" ) | ||||
353 | if (@unknownSubTypes); | ||||
354 | } | ||||
355 | |||||
356 | # All OK so set the subtype. | ||||
357 | $chart->setSubTypes(@subTypes); | ||||
358 | } | ||||
359 | |||||
360 | # See if the parameter 'scale' is available. | ||||
361 | my $scale = $this->_Parameters->getParameter( "scale", $defaultScale ); | ||||
362 | if ( $scale ne "base10" and $scale ne "linear" and $scale ne "semilog" ) { | ||||
363 | return _make_error("Invalid value of *$scale* for parameter *scale* "); | ||||
364 | } | ||||
365 | $chart->setScale($scale); | ||||
366 | |||||
367 | # See if the parameter 'name' is available. This is a required | ||||
368 | # parameter. If it is missing, then generate an error message. | ||||
369 | my $name = $this->_Parameters->getParameter( "name", undef ); | ||||
370 | return _make_error("parameter *name* must be specified") | ||||
371 | if ( !defined $name ); | ||||
372 | |||||
373 | # See if the parameter 'web' is available. If not, then default to | ||||
374 | # looking for tables in the current web. | ||||
375 | my $inWeb = $this->_Parameters->getParameter( "web", $web ); | ||||
376 | |||||
377 | # See if the parameter 'topic' is available. If not, then default to | ||||
378 | # looking for tables in the current topic. | ||||
379 | my $inTopic = $this->_Parameters->getParameter( "topic", $topic ); | ||||
380 | |||||
381 | # Before we parse any further parameters, lets get the contents of the | ||||
382 | # specified web/topic. | ||||
383 | if ( !$this->_setTopicContents( $inWeb, $inTopic ) ) { | ||||
384 | return _make_error( | ||||
385 | "Error retrieving Foswiki topic $inWeb<nop>.$inTopic"); | ||||
386 | } | ||||
387 | |||||
388 | # Determine which table the user wants to chart | ||||
389 | my $tableName = $this->_Parameters->getParameter( "table", 1 ); | ||||
390 | |||||
391 | # Verify that the table name is valid. | ||||
392 | if ( !$this->_tables->checkTableExists($tableName) ) { | ||||
393 | return _make_error( | ||||
394 | "parameter *table* is not valid table; the specified table '$tableName' does not exist." | ||||
395 | ); | ||||
396 | } | ||||
397 | |||||
398 | # See if the parameter 'title' is available. | ||||
399 | $chart->setTitle( $this->_Parameters->getParameter( "title", undef ) ); | ||||
400 | |||||
401 | # See if the parameter 'xlabel' is available. | ||||
402 | $chart->setXlabel( $this->_Parameters->getParameter( "xlabel", undef ) ); | ||||
403 | |||||
404 | # See if the parameter 'ylabel' is available. | ||||
405 | $chart->setYlabel( $this->_Parameters->getParameter( "ylabel", undef ) ); | ||||
406 | |||||
407 | # See if the parameter 'data' is available. This is a required | ||||
408 | # parameter. If it is missing, then generate an error message. | ||||
409 | my $data = $this->_Parameters->getParameter( "data", undef ); | ||||
410 | return _make_error("parameter *data* must be specified") | ||||
411 | if ( !defined $data ); | ||||
412 | |||||
413 | # See if the parameter 'xaxis' is available. | ||||
414 | my $xAxis = $this->_Parameters->getParameter( "xaxis", undef ); | ||||
415 | |||||
416 | # See if the parameter 'yaxis' is available. | ||||
417 | my $yAxis = $this->_Parameters->getParameter( "yaxis", "off" ); | ||||
418 | $chart->setYaxis($yAxis); | ||||
419 | |||||
420 | # See if the parameter 'ytic' is available. | ||||
421 | my $yTic = $this->_Parameters->getParameter( "ytics", -1 ); | ||||
422 | $chart->setNumYTics($yTic); | ||||
423 | |||||
424 | # See if the parameter 'xaxisangle' is available. | ||||
425 | my $xaxisangle = $this->_Parameters->getParameter( "xaxisangle", 0 ); | ||||
426 | $chart->setXaxisAngle($xaxisangle); | ||||
427 | |||||
428 | # See if the parameter 'ymin' is available. | ||||
429 | my $yMin = $this->_Parameters->getParameter( "ymin", undef ); | ||||
430 | if ( defined $yMin ) { | ||||
431 | if ( $scale eq "semilog" && $yMin <= 0 ) { | ||||
432 | return _make_error( | ||||
433 | "user set ymin=$yMin is <= 0 which is not valid when scale=semilog" | ||||
434 | ); | ||||
435 | } | ||||
436 | } | ||||
437 | $chart->setYmin($yMin); | ||||
438 | |||||
439 | # See if the parameter 'ymax' is available. | ||||
440 | my $yMax = $this->_Parameters->getParameter( "ymax", undef ); | ||||
441 | if ( defined $yMax ) { | ||||
442 | if ( $scale eq "semilog" && $yMax <= 0 ) { | ||||
443 | return _make_error( | ||||
444 | "user set ymax=$yMax is <= 0 which is not valid when scale=semilog" | ||||
445 | ); | ||||
446 | } | ||||
447 | } | ||||
448 | $chart->setYmax($yMax); | ||||
449 | |||||
450 | # See if the parameter 'numygrids' is available. | ||||
451 | $chart->setNumYGrids( | ||||
452 | $this->_Parameters->getParameter( "numygrids", $defaultNumYGrids ) ); | ||||
453 | |||||
454 | # See if the parameter 'numxgrids' is available. | ||||
455 | my $numxgrids = $this->_Parameters->getParameter( "numxgrids", 10 ); | ||||
456 | $chart->setNumXGrids($numxgrids); | ||||
457 | |||||
458 | # See if the parameter 'xgrid' is available. | ||||
459 | my $xGrid = $this->_Parameters->getParameter( "xgrid", "dot" ); | ||||
460 | $chart->setXgrid($xGrid); | ||||
461 | |||||
462 | # See if the parameter 'ygrid' is available. | ||||
463 | my $yGrid = $this->_Parameters->getParameter( "ygrid", "dot" ); | ||||
464 | $chart->setYgrid($yGrid); | ||||
465 | |||||
466 | # See if the parameter 'datalabel' is available. | ||||
467 | my $dataLabels = $this->_Parameters->getParameter( "datalabel", "off" ); | ||||
468 | $chart->setDataLabels( split( /[\s,]+/, $dataLabels ) ) | ||||
469 | if ( defined $dataLabels ); | ||||
470 | |||||
471 | # See if the parameter 'legend' is available. | ||||
472 | my $legend = $this->_Parameters->getParameter( "legend", undef ); | ||||
473 | |||||
474 | # Get the chart width and height | ||||
475 | $chart->setImageWidth( | ||||
476 | $this->_Parameters->getParameter( "width", $defaultWidth ) ); | ||||
477 | $chart->setImageHeight( | ||||
478 | $this->_Parameters->getParameter( "height", $defaultHeight ) ); | ||||
479 | |||||
480 | # Get the chart IMG 'alt' text. | ||||
481 | my $alt = $this->_Parameters->getParameter( "alt", "" ); | ||||
482 | |||||
483 | # Get the chart 'bgcolor' color. | ||||
484 | my $bgcolor = | ||||
485 | $this->_Parameters->getParameter( "bgcolor", $defaultBGcolor ); | ||||
486 | $chart->setBGcolor( split( /[\s,]+/, $bgcolor ) ); | ||||
487 | |||||
488 | # Set line/area colors. If the parameter 'colors' is defined, then the | ||||
489 | # chart will be made with the user specified colors. Otherwise the | ||||
490 | # chart will be made with the default colors, and then it will depend | ||||
491 | # on if an 'area' or 'line' is being drawn which will determine which | ||||
492 | # set of colors to use. | ||||
493 | $chart->setLineColors(@defaultLineColors); | ||||
494 | $chart->setAreaColors(@defaultAreaColors); | ||||
495 | |||||
496 | # See if the parameter 'colors' is available. | ||||
497 | my $colors = $this->_Parameters->getParameter( "colors", undef ); | ||||
498 | $chart->setColors( split( /[\s,]+/, $colors ) ) if ( defined $colors ); | ||||
499 | |||||
500 | # Get the chart grid color. | ||||
501 | my $gridColor = | ||||
502 | $this->_Parameters->getParameter( "gridcolor", $defaultGridColor ); | ||||
503 | $chart->setGridColor( split( /[\s,]+/, $gridColor ) ); | ||||
504 | |||||
505 | # See if the parameter 'defaultdata' is available. | ||||
506 | my $DataValueDefault = | ||||
507 | $this->_Parameters->getParameter( "defaultdata", $defaultDataValue ); | ||||
508 | $DataValueDefault = '' if ( $DataValueDefault eq "none" ); | ||||
509 | $chart->setDefaultDataValue($DataValueDefault); | ||||
510 | |||||
511 | # Get the filename in which to create the graphics file. | ||||
512 | my $filename = _make_filename( $type, $name ); | ||||
513 | $chart->setAttachmentName( $web, $topic, $filename ); | ||||
514 | |||||
515 | # Validate the legend data making sure it only specifies a single row | ||||
516 | # or a single column. | ||||
517 | my @legend; | ||||
518 | if ($legend) { | ||||
519 | my $cnt = my @d = $this->_tables->getData( $tableName, $legend ); | ||||
520 | if ( $cnt > 1 ) { | ||||
521 | @d = Foswiki::Plugins::ChartPlugin::Table::transpose(@d); | ||||
522 | $cnt = scalar(@d); | ||||
523 | } | ||||
524 | if ( $cnt > 1 ) { | ||||
525 | return _make_error( | ||||
526 | "parameter *legend* specifies multiple ($cnt) rows."); | ||||
527 | } | ||||
528 | if ( $cnt == 0 ) { | ||||
529 | return _make_error( | ||||
530 | "parameter *legend* contains an invalid value '$legend'."); | ||||
531 | } | ||||
532 | @legend = @{ $d[0] }; | ||||
533 | |||||
534 | #die Data::Dumper->Dump([\@legend]); | ||||
535 | $chart->setLegend(@legend); | ||||
536 | } | ||||
537 | |||||
538 | # If the user specified an X axis range, then extract from the X axis | ||||
539 | # data the starting and ending row/columns. This defines whether the | ||||
540 | # data is row ordered or column ordered. If there is no X axis | ||||
541 | # information specified, then assume that the data is in column order. | ||||
542 | my $columnOrdered = 0; | ||||
543 | my $rowOrdered = 0; | ||||
544 | if ( defined($xAxis) ) { | ||||
545 | my ( $xAxisRows, $xAxisColumns ) = | ||||
546 | $this->_tables->getRowColumnCount( $tableName, $xAxis ); | ||||
547 | return _make_error("parameter *xaxis* value of '$xAxis' is not valid") | ||||
548 | if ( !defined($xAxisRows) ); | ||||
549 | if ( abs($xAxisRows) > 1 ) { | ||||
550 | if ( $xAxisColumns > 1 ) { | ||||
551 | return _make_error( | ||||
552 | "parameter *xaxis* specifies multiple (${xAxisRows}X$xAxisColumns) rows and columns." | ||||
553 | ); | ||||
554 | } | ||||
555 | $columnOrdered = 1; | ||||
556 | } | ||||
557 | else { | ||||
558 | $rowOrdered = 1; | ||||
559 | } | ||||
560 | my @d = $this->_tables->getData( $tableName, $xAxis, $columnOrdered ); | ||||
561 | return _make_error( | ||||
562 | "no X axis data found in specified area of table [$xAxis]") | ||||
563 | if ( !@d ); | ||||
564 | $chart->setXaxis( @{ $d[0] } ); | ||||
565 | } | ||||
566 | else { | ||||
567 | $columnOrdered = 1; | ||||
568 | } | ||||
569 | |||||
570 | # Validate the data range as valid | ||||
571 | #my ($dataRows, $dataColumns) = | ||||
572 | # $this->_tables->getRowColumnCount($tableName, $data); | ||||
573 | #return _make_error("parameter *data* value of '$data' is not valid") if (! defined($dataRows)); | ||||
574 | |||||
575 | # Get the actual area data. | ||||
576 | my @data = (); | ||||
577 | @data = $this->_tables->getData( $tableName, $data, $columnOrdered ); | ||||
578 | |||||
579 | # Validate that there is real data returned. | ||||
580 | return _make_error("no data found in specified area of table [$data]") | ||||
581 | if ( !@data ); | ||||
582 | |||||
583 | #my @ranges = $this->_tables->getTableRanges($tableName, $data); | ||||
584 | #die $data, ' ', Data::Dumper->Dump([\@ranges]); | ||||
585 | $yMin = $chart->setData(@data); | ||||
586 | |||||
587 | # If scale=semilog and any data is <= 0, then error | ||||
588 | if ( $scale eq "semilog" && $yMin <= 0 ) { | ||||
589 | return _make_error("data ($yMin) <= 0 not valid when scale=semilog"); | ||||
590 | } | ||||
591 | |||||
592 | # Make sure that there are enough legends to go with all specified | ||||
593 | # data sets (if legends were specified) | ||||
594 | if ($legend) { | ||||
595 | my $numLegends = @legend; | ||||
596 | my $numDataSets = @data; | ||||
597 | if ( $numDataSets != $numLegends ) { | ||||
598 | return _make_error( | ||||
599 | "parameter *legend* contains an invalid value '$legend' since it specifies $numLegends legends and there are $numDataSets data sets." | ||||
600 | ); | ||||
601 | } | ||||
602 | } | ||||
603 | |||||
604 | # Set the default point size | ||||
605 | $chart->setPointSize( | ||||
606 | $this->_Parameters->getParameter( "pointsize", $defaultPointSize ) ); | ||||
607 | |||||
608 | # Set the default line width | ||||
609 | $chart->setLineWidth( | ||||
610 | $this->_Parameters->getParameter( "linewidth", $defaultLineWidth ) ); | ||||
611 | |||||
612 | # Set default bar graph values | ||||
613 | $chart->setBarLeadingSpace($defaultBarLeadingSpace); | ||||
614 | $chart->setBarTrailingSpace($defaultBarTrailingSpace); | ||||
615 | $chart->setBarSpace($defaultBarSpace); | ||||
616 | |||||
617 | # Create the actual chart. | ||||
618 | my $err = $chart->makeChart(); | ||||
619 | return _make_error("chart error: name=$name: $err") if ($err); | ||||
620 | |||||
621 | # Get remaining parameters and pass to <img ... /> | ||||
622 | my $options = ""; | ||||
623 | my %parameters = $this->_Parameters->getAllParameters(); | ||||
624 | foreach my $k ( keys %parameters ) { | ||||
625 | $options .= "$k=\"$parameters{$k}\" "; | ||||
626 | } | ||||
627 | |||||
628 | # Make a unique value to append to the image name that forces a web | ||||
629 | # browser to reload the image each time the image is viewed. This is | ||||
630 | # done so changes to the values used to generate the chart, or the | ||||
631 | # chart layout specifications, are seen immediately and not ignored | ||||
632 | # because the browser has cached the image. Eventually a hash value | ||||
633 | # should be used such that the user's browser CAN cache the image iff | ||||
634 | # none of the values/parameters used in creating the chart have changed. | ||||
635 | my $timestamp = time(); | ||||
636 | return | ||||
637 | "<img src=\"%ATTACHURL%/$filename?t=$timestamp\" alt=\"$alt\" $options />"; | ||||
638 | } | ||||
639 | |||||
640 | # The following is really for debugging and timing purposes and is not an | ||||
641 | # advertised interface. This routine basically creates a number of charts | ||||
642 | # and (roughly) times how long it took to create them. | ||||
643 | # Usage: %CHART_TIMER{### <parameters>}% | ||||
644 | # where ### is the number of charts to create and <parameters> are valid | ||||
645 | # %CHART% parameters ('name' is overridden by the timer so is ignored if | ||||
646 | # specified in <parameters> | ||||
647 | sub _timeit { | ||||
648 | my ( $this, $loops, $params, $topic, $web ) = @_; | ||||
649 | my $removeFiles = 0; # Flag on whether to remove the test graphics or not | ||||
650 | my $start_time = time(); | ||||
651 | for ( my $i = 0 ; $i < $loops ; $i++ ) { | ||||
652 | my $str = "$params name=\"timeit_$i\""; | ||||
653 | $this->_makeChart( $str, $topic, $web ); | ||||
654 | } | ||||
655 | my $finish_time = time(); | ||||
656 | my $diff = $finish_time - $start_time; | ||||
657 | |||||
658 | # Remove the just created test files. | ||||
659 | if ($removeFiles) { | ||||
660 | for ( my $i = 0 ; $i < $loops ; $i++ ) { | ||||
661 | my ( $dir, $filename ) = | ||||
662 | _make_filename( "area", "timeit_$i", $topic, $web ); | ||||
663 | unlink("$dir/$filename"); | ||||
664 | } | ||||
665 | } | ||||
666 | return "To make $loops charts it (roughly) took $diff seconds.<BR>"; | ||||
667 | } | ||||
668 | |||||
669 | # ========================= | ||||
670 | # spent 353µs within Foswiki::Plugins::ChartPlugin::commonTagsHandler which was called 108 times, avg 3µs/call:
# 108 times (353µs+0s) by Foswiki::Plugin::invoke at line 310 of /var/www/foswikidev/core/lib/Foswiki/Plugin.pm, avg 3µs/call | ||||
671 | ### my ( $text ) = @_; # do not uncomment, use $_[0] instead | ||||
672 | 108 | 32µs | my $topic = $_[1]; | ||
673 | 108 | 18µs | my $web = $_[2]; | ||
674 | |||||
675 | # If no %CHART%s on this page, then there is nothing to do so just | ||||
676 | # return. | ||||
677 | 108 | 279µs | if ( $_[0] !~ m/%CHART.*{.*}%/ ) { | ||
678 | |||||
679 | # nothing to do | ||||
680 | return; | ||||
681 | } | ||||
682 | _init_defaults(); | ||||
683 | my $chart = ChartPlugin( $topic, $web, $_[0] ); | ||||
684 | $_[0] =~ s/%CHART{(.*?)}%/$chart->_makeChart($1, $topic, $web)/eog; | ||||
685 | $_[0] =~ | ||||
686 | s/%CHART_TIMER{(\d+) (.*)}%/$chart->_timeit($1, $2, $topic, $web)/eog; | ||||
687 | } | ||||
688 | |||||
689 | 1 | 4µs | 1; |