ruport 0.4.23 → 0.4.99

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/AUTHORS +16 -8
  2. data/CHANGELOG +30 -1
  3. data/README +144 -114
  4. data/Rakefile +12 -4
  5. data/TODO +4 -7
  6. data/bin/rope +21 -28
  7. data/examples/line_graph.rb +36 -0
  8. data/examples/sample_invoice_report.rb +1 -1
  9. data/examples/simple_graph.rb +8 -0
  10. data/lib/SVG/Graph/Bar.rb +137 -0
  11. data/lib/SVG/Graph/BarBase.rb +140 -0
  12. data/lib/SVG/Graph/BarHorizontal.rb +136 -0
  13. data/lib/SVG/Graph/Graph.rb +977 -0
  14. data/lib/SVG/Graph/Line.rb +444 -0
  15. data/lib/SVG/Graph/Pie.rb +394 -0
  16. data/lib/SVG/Graph/Plot.rb +494 -0
  17. data/lib/SVG/Graph/Schedule.rb +373 -0
  18. data/lib/SVG/Graph/TimeSeries.rb +241 -0
  19. data/lib/ruport.rb +2 -2
  20. data/lib/ruport/config.rb +47 -3
  21. data/lib/ruport/data/collection.rb +17 -1
  22. data/lib/ruport/data/record.rb +101 -8
  23. data/lib/ruport/data/set.rb +81 -2
  24. data/lib/ruport/data/set.rb.rej +147 -0
  25. data/lib/ruport/data/set.rb~ +73 -0
  26. data/lib/ruport/data/table.rb +127 -2
  27. data/lib/ruport/data/taggable.rb +21 -2
  28. data/lib/ruport/format.rb +36 -44
  29. data/lib/ruport/format/engine.rb +21 -1
  30. data/lib/ruport/format/plugin.rb +64 -1
  31. data/lib/ruport/mailer.rb +70 -36
  32. data/lib/ruport/meta_tools.rb +15 -6
  33. data/lib/ruport/query.rb +1 -1
  34. data/lib/ruport/rails/reportable.rb +23 -1
  35. data/lib/ruport/report.rb +11 -11
  36. data/lib/ruport/report/invoice.rb +16 -0
  37. data/lib/ruport/system_extensions.rb +3 -55
  38. data/test/{tc_database.rb → _test_database.rb} +0 -0
  39. data/test/{tc_config.rb → test_config.rb} +0 -0
  40. data/test/{tc_format.rb → test_format.rb} +1 -0
  41. data/test/{tc_format_engine.rb → test_format_engine.rb} +14 -2
  42. data/test/test_graph.rb +101 -0
  43. data/test/{tc_invoice.rb → test_invoice.rb} +7 -1
  44. data/test/test_mailer.rb +108 -0
  45. data/test/test_meta_tools.rb +14 -0
  46. data/test/{tc_plugin.rb → test_plugin.rb} +12 -1
  47. data/test/{tc_query.rb → test_query.rb} +0 -0
  48. data/test/{tc_record.rb → test_record.rb} +9 -0
  49. data/test/{tc_report.rb → test_report.rb} +2 -1
  50. data/test/{tc_ruport.rb → test_ruport.rb} +0 -0
  51. data/test/test_set.rb +118 -0
  52. data/test/test_set.rb.rej +16 -0
  53. data/test/{tc_set.rb → test_set.rb~} +17 -0
  54. data/test/{tc_sql_split.rb → test_sql_split.rb} +0 -0
  55. data/test/{tc_table.rb → test_table.rb} +15 -0
  56. data/test/{tc_taggable.rb → test_taggable.rb} +0 -0
  57. data/test/unit.log +361 -0
  58. metadata +52 -30
  59. data/examples/bar.pdf +0 -193
  60. data/examples/f.log +0 -5
  61. data/examples/foo.pdf +0 -193
  62. data/lib/ruport/format/document.rb +0 -78
  63. data/lib/ruport/format/open_node.rb +0 -38
  64. data/test/tc_data_row.rb +0 -132
  65. data/test/tc_data_set.rb +0 -386
  66. data/test/tc_document.rb +0 -42
  67. data/test/tc_element.rb +0 -18
  68. data/test/tc_page.rb +0 -42
  69. data/test/tc_section.rb +0 -45
  70. data/test/ts_all.rb +0 -12
  71. data/test/ts_format.rb +0 -7
@@ -0,0 +1,494 @@
1
+ require 'SVG/Graph/Graph'
2
+
3
+ module SVG
4
+ module Graph
5
+ # === For creating SVG plots of scalar data
6
+ #
7
+ # = Synopsis
8
+ #
9
+ # require 'SVG/Graph/Plot'
10
+ #
11
+ # # Data sets are x,y pairs
12
+ # # Note that multiple data sets can differ in length, and that the
13
+ # # data in the datasets needn't be in order; they will be ordered
14
+ # # by the plot along the X-axis.
15
+ # projection = [
16
+ # 6, 11, 0, 5, 18, 7, 1, 11, 13, 9, 1, 2, 19, 0, 3, 13,
17
+ # 7, 9
18
+ # ]
19
+ # actual = [
20
+ # 0, 18, 8, 15, 9, 4, 18, 14, 10, 2, 11, 6, 14, 12,
21
+ # 15, 6, 4, 17, 2, 12
22
+ # ]
23
+ #
24
+ # graph = SVG::Graph::Plot.new({
25
+ # :height => 500,
26
+ # :width => 300,
27
+ # :key => true,
28
+ # :scale_x_integers => true,
29
+ # :scale_y_integerrs => true,
30
+ # })
31
+ #
32
+ # graph.add_data({
33
+ # :data => projection
34
+ # :title => 'Projected',
35
+ # })
36
+ #
37
+ # graph.add_data({
38
+ # :data => actual,
39
+ # :title => 'Actual',
40
+ # })
41
+ #
42
+ # print graph.burn()
43
+ #
44
+ # = Description
45
+ #
46
+ # Produces a graph of scalar data.
47
+ #
48
+ # This object aims to allow you to easily create high quality
49
+ # SVG[http://www.w3c.org/tr/svg] scalar plots. You can either use the
50
+ # default style sheet or supply your own. Either way there are many options
51
+ # which can be configured to give you control over how the graph is
52
+ # generated - with or without a key, data elements at each point, title,
53
+ # subtitle etc.
54
+ #
55
+ # = Examples
56
+ #
57
+ # http://www.germane-software/repositories/public/SVG/test/plot.rb
58
+ #
59
+ # = Notes
60
+ #
61
+ # The default stylesheet handles upto 10 data sets, if you
62
+ # use more you must create your own stylesheet and add the
63
+ # additional settings for the extra data sets. You will know
64
+ # if you go over 10 data sets as they will have no style and
65
+ # be in black.
66
+ #
67
+ # Unlike the other types of charts, data sets must contain x,y pairs:
68
+ #
69
+ # [ 1, 2 ] # A data set with 1 point: (1,2)
70
+ # [ 1,2, 5,6] # A data set with 2 points: (1,2) and (5,6)
71
+ #
72
+ # = See also
73
+ #
74
+ # * SVG::Graph::Graph
75
+ # * SVG::Graph::BarHorizontal
76
+ # * SVG::Graph::Bar
77
+ # * SVG::Graph::Line
78
+ # * SVG::Graph::Pie
79
+ # * SVG::Graph::TimeSeries
80
+ #
81
+ # == Author
82
+ #
83
+ # Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
84
+ #
85
+ # Copyright 2004 Sean E. Russell
86
+ # This software is available under the Ruby license[LICENSE.txt]
87
+ #
88
+ class Plot < Graph
89
+
90
+ # In addition to the defaults set by Graph::initialize, sets
91
+ # [show_data_points] true
92
+ # [area_fill] false
93
+ # [stacked] false
94
+ def set_defaults
95
+ init_with(
96
+ :show_data_points => true,
97
+ :area_fill => false,
98
+ :stacked => false
99
+ )
100
+ self.top_align = self.right_align = self.top_font = self.right_font = 1
101
+ end
102
+
103
+ # Determines the scaling for the X axis divisions.
104
+ #
105
+ # graph.scale_x_divisions = 2
106
+ #
107
+ # would cause the graph to attempt to generate labels stepped by 2; EG:
108
+ # 0,2,4,6,8...
109
+ attr_accessor :scale_x_divisions
110
+ # Determines the scaling for the Y axis divisions.
111
+ #
112
+ # graph.scale_y_divisions = 0.5
113
+ #
114
+ # would cause the graph to attempt to generate labels stepped by 0.5; EG:
115
+ # 0, 0.5, 1, 1.5, 2, ...
116
+ attr_accessor :scale_y_divisions
117
+ # Make the X axis labels integers
118
+ attr_accessor :scale_x_integers
119
+ # Make the Y axis labels integers
120
+ attr_accessor :scale_y_integers
121
+ # Fill the area under the line
122
+ attr_accessor :area_fill
123
+ # Show a small circle on the graph where the line
124
+ # goes from one point to the next.
125
+ attr_accessor :show_data_points
126
+ # Set the minimum value of the X axis
127
+ attr_accessor :min_x_value
128
+ # Set the minimum value of the Y axis
129
+ attr_accessor :min_y_value
130
+
131
+
132
+ # Adds data to the plot. The data must be in X,Y pairs; EG
133
+ # [ 1, 2 ] # A data set with 1 point: (1,2)
134
+ # [ 1,2, 5,6] # A data set with 2 points: (1,2) and (5,6)
135
+ def add_data data
136
+ @data = [] unless @data
137
+
138
+ raise "No data provided by #{conf.inspect}" unless data[:data] and
139
+ data[:data].kind_of? Array
140
+ raise "Data supplied must be x,y pairs! "+
141
+ "The data provided contained an odd set of "+
142
+ "data points" unless data[:data].length % 2 == 0
143
+ return if data[:data].length == 0
144
+
145
+ x = []
146
+ y = []
147
+ data[:data].each_index {|i|
148
+ (i%2 == 0 ? x : y) << data[:data][i]
149
+ }
150
+ sort( x, y )
151
+ data[:data] = [x,y]
152
+ @data << data
153
+ end
154
+
155
+
156
+ protected
157
+
158
+ def keys
159
+ @data.collect{ |x| x[:title] }
160
+ end
161
+
162
+ def calculate_left_margin
163
+ super
164
+ label_left = get_x_labels[0].to_s.length / 2 * font_size * 0.6
165
+ @border_left = label_left if label_left > @border_left
166
+ end
167
+
168
+ def calculate_right_margin
169
+ super
170
+ label_right = get_x_labels[-1].to_s.length / 2 * font_size * 0.6
171
+ @border_right = label_right if label_right > @border_right
172
+ end
173
+
174
+
175
+ X = 0
176
+ Y = 1
177
+ def x_range
178
+ max_value = @data.collect{|x| x[:data][X][-1] }.max
179
+ min_value = @data.collect{|x| x[:data][X][0] }.min
180
+ min_value = min_value<min_x_value ? min_value : min_x_value if min_x_value
181
+
182
+ range = max_value - min_value
183
+ right_pad = range == 0 ? 10 : range / 20.0
184
+ scale_range = (max_value + right_pad) - min_value
185
+
186
+ scale_division = scale_x_divisions || (scale_range / 10.0)
187
+
188
+ if scale_x_integers
189
+ scale_division = scale_division < 1 ? 1 : scale_division.round
190
+ end
191
+
192
+ [min_value, max_value, scale_division]
193
+ end
194
+
195
+ def get_x_values
196
+ min_value, max_value, scale_division = x_range
197
+ rv = []
198
+ min_value.step( max_value, scale_division ) {|v| rv << v}
199
+ return rv
200
+ end
201
+ alias :get_x_labels :get_x_values
202
+
203
+ def field_width
204
+ values = get_x_values
205
+ max = @data.collect{|x| x[:data][X][-1]}.max
206
+ dx = (max - values[-1]).to_f / (values[-1] - values[-2])
207
+ (@graph_width.to_f - font_size*2*right_font) /
208
+ (values.length + dx - right_align)
209
+ end
210
+
211
+
212
+ def y_range
213
+ max_value = @data.collect{|x| x[:data][Y].max }.max
214
+ min_value = @data.collect{|x| x[:data][Y].min }.min
215
+ min_value = min_value<min_y_value ? min_value : min_y_value if min_y_value
216
+
217
+ range = max_value - min_value
218
+ top_pad = range == 0 ? 10 : range / 20.0
219
+ scale_range = (max_value + top_pad) - min_value
220
+
221
+ scale_division = scale_y_divisions || (scale_range / 10.0)
222
+
223
+ if scale_y_integers
224
+ scale_division = scale_division < 1 ? 1 : scale_division.round
225
+ end
226
+
227
+ return [min_value, max_value, scale_division]
228
+ end
229
+
230
+ def get_y_values
231
+ min_value, max_value, scale_division = y_range
232
+ rv = []
233
+ min_value.step( max_value, scale_division ) {|v| rv << v}
234
+ return rv
235
+ end
236
+ alias :get_y_labels :get_y_values
237
+
238
+ def field_height
239
+ values = get_y_values
240
+ max = @data.collect{|x| x[:data][Y].max }.max
241
+ dx = (max - values[-1]).to_f / (values[-1] - values[-2])
242
+ (@graph_height.to_f - font_size*2*top_font) /
243
+ (values.length + dx - top_align)
244
+ end
245
+
246
+ def draw_data
247
+ line = 1
248
+
249
+ x_min, x_max, x_div = x_range
250
+ y_min, y_max, y_div = y_range
251
+ x_step = (@graph_width.to_f - font_size*2) / (x_max-x_min)
252
+ y_step = (@graph_height.to_f - font_size*2) / (y_max-y_min)
253
+
254
+ for data in @data
255
+ x_points = data[:data][X]
256
+ y_points = data[:data][Y]
257
+
258
+ lpath = "L"
259
+ x_start = 0
260
+ y_start = 0
261
+ x_points.each_index { |idx|
262
+ x = (x_points[idx] - x_min) * x_step
263
+ y = @graph_height - (y_points[idx] - y_min) * y_step
264
+ x_start, y_start = x,y if idx == 0
265
+ lpath << "#{x} #{y} "
266
+ }
267
+
268
+ if area_fill
269
+ @graph.add_element( "path", {
270
+ "d" => "M#{x_start} #@graph_height #{lpath} V#@graph_height Z",
271
+ "class" => "fill#{line}"
272
+ })
273
+ end
274
+
275
+ @graph.add_element( "path", {
276
+ "d" => "M#{x_start} #{y_start} #{lpath}",
277
+ "class" => "line#{line}"
278
+ })
279
+
280
+ if show_data_points || show_data_values
281
+ x_points.each_index { |idx|
282
+ x = (x_points[idx] - x_min) * x_step
283
+ y = @graph_height - (y_points[idx] - y_min) * y_step
284
+ if show_data_points
285
+ @graph.add_element( "circle", {
286
+ "cx" => x.to_s,
287
+ "cy" => y.to_s,
288
+ "r" => "2.5",
289
+ "class" => "dataPoint#{line}"
290
+ })
291
+ add_popup(x, y, format( x_points[idx], y_points[idx] )) if add_popups
292
+ end
293
+ make_datapoint_text( x, y-6, y_points[idx] )
294
+ }
295
+ end
296
+ line += 1
297
+ end
298
+ end
299
+
300
+ def format x, y
301
+ "(#{(x * 100).to_i / 100}, #{(y * 100).to_i / 100})"
302
+ end
303
+
304
+ def get_css
305
+ return <<EOL
306
+ /* default line styles */
307
+ .line1{
308
+ fill: none;
309
+ stroke: #ff0000;
310
+ stroke-width: 1px;
311
+ }
312
+ .line2{
313
+ fill: none;
314
+ stroke: #0000ff;
315
+ stroke-width: 1px;
316
+ }
317
+ .line3{
318
+ fill: none;
319
+ stroke: #00ff00;
320
+ stroke-width: 1px;
321
+ }
322
+ .line4{
323
+ fill: none;
324
+ stroke: #ffcc00;
325
+ stroke-width: 1px;
326
+ }
327
+ .line5{
328
+ fill: none;
329
+ stroke: #00ccff;
330
+ stroke-width: 1px;
331
+ }
332
+ .line6{
333
+ fill: none;
334
+ stroke: #ff00ff;
335
+ stroke-width: 1px;
336
+ }
337
+ .line7{
338
+ fill: none;
339
+ stroke: #00ffff;
340
+ stroke-width: 1px;
341
+ }
342
+ .line8{
343
+ fill: none;
344
+ stroke: #ffff00;
345
+ stroke-width: 1px;
346
+ }
347
+ .line9{
348
+ fill: none;
349
+ stroke: #ccc6666;
350
+ stroke-width: 1px;
351
+ }
352
+ .line10{
353
+ fill: none;
354
+ stroke: #663399;
355
+ stroke-width: 1px;
356
+ }
357
+ .line11{
358
+ fill: none;
359
+ stroke: #339900;
360
+ stroke-width: 1px;
361
+ }
362
+ .line12{
363
+ fill: none;
364
+ stroke: #9966FF;
365
+ stroke-width: 1px;
366
+ }
367
+ /* default fill styles */
368
+ .fill1{
369
+ fill: #cc0000;
370
+ fill-opacity: 0.2;
371
+ stroke: none;
372
+ }
373
+ .fill2{
374
+ fill: #0000cc;
375
+ fill-opacity: 0.2;
376
+ stroke: none;
377
+ }
378
+ .fill3{
379
+ fill: #00cc00;
380
+ fill-opacity: 0.2;
381
+ stroke: none;
382
+ }
383
+ .fill4{
384
+ fill: #ffcc00;
385
+ fill-opacity: 0.2;
386
+ stroke: none;
387
+ }
388
+ .fill5{
389
+ fill: #00ccff;
390
+ fill-opacity: 0.2;
391
+ stroke: none;
392
+ }
393
+ .fill6{
394
+ fill: #ff00ff;
395
+ fill-opacity: 0.2;
396
+ stroke: none;
397
+ }
398
+ .fill7{
399
+ fill: #00ffff;
400
+ fill-opacity: 0.2;
401
+ stroke: none;
402
+ }
403
+ .fill8{
404
+ fill: #ffff00;
405
+ fill-opacity: 0.2;
406
+ stroke: none;
407
+ }
408
+ .fill9{
409
+ fill: #cc6666;
410
+ fill-opacity: 0.2;
411
+ stroke: none;
412
+ }
413
+ .fill10{
414
+ fill: #663399;
415
+ fill-opacity: 0.2;
416
+ stroke: none;
417
+ }
418
+ .fill11{
419
+ fill: #339900;
420
+ fill-opacity: 0.2;
421
+ stroke: none;
422
+ }
423
+ .fill12{
424
+ fill: #9966FF;
425
+ fill-opacity: 0.2;
426
+ stroke: none;
427
+ }
428
+ /* default line styles */
429
+ .key1,.dataPoint1{
430
+ fill: #ff0000;
431
+ stroke: none;
432
+ stroke-width: 1px;
433
+ }
434
+ .key2,.dataPoint2{
435
+ fill: #0000ff;
436
+ stroke: none;
437
+ stroke-width: 1px;
438
+ }
439
+ .key3,.dataPoint3{
440
+ fill: #00ff00;
441
+ stroke: none;
442
+ stroke-width: 1px;
443
+ }
444
+ .key4,.dataPoint4{
445
+ fill: #ffcc00;
446
+ stroke: none;
447
+ stroke-width: 1px;
448
+ }
449
+ .key5,.dataPoint5{
450
+ fill: #00ccff;
451
+ stroke: none;
452
+ stroke-width: 1px;
453
+ }
454
+ .key6,.dataPoint6{
455
+ fill: #ff00ff;
456
+ stroke: none;
457
+ stroke-width: 1px;
458
+ }
459
+ .key7,.dataPoint7{
460
+ fill: #00ffff;
461
+ stroke: none;
462
+ stroke-width: 1px;
463
+ }
464
+ .key8,.dataPoint8{
465
+ fill: #ffff00;
466
+ stroke: none;
467
+ stroke-width: 1px;
468
+ }
469
+ .key9,.dataPoint9{
470
+ fill: #cc6666;
471
+ stroke: none;
472
+ stroke-width: 1px;
473
+ }
474
+ .key10,.dataPoint10{
475
+ fill: #663399;
476
+ stroke: none;
477
+ stroke-width: 1px;
478
+ }
479
+ .key11,.dataPoint11{
480
+ fill: #339900;
481
+ stroke: none;
482
+ stroke-width: 1px;
483
+ }
484
+ .key12,.dataPoint12{
485
+ fill: #9966FF;
486
+ stroke: none;
487
+ stroke-width: 1px;
488
+ }
489
+ EOL
490
+ end
491
+
492
+ end
493
+ end
494
+ end