jsanders-ruport 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +48 -0
- data/LICENSE +59 -0
- data/README +114 -0
- data/Rakefile +93 -0
- data/examples/RWEmerson.jpg +0 -0
- data/examples/anon.rb +43 -0
- data/examples/btree/commaleon/commaleon.rb +263 -0
- data/examples/btree/commaleon/sample_data/ticket_count.csv +124 -0
- data/examples/btree/commaleon/sample_data/ticket_count2.csv +119 -0
- data/examples/centered_pdf_text_box.rb +83 -0
- data/examples/data/tattle.dump +82 -0
- data/examples/example.csv +3 -0
- data/examples/line_plotter.rb +61 -0
- data/examples/pdf_report_with_common_base.rb +72 -0
- data/examples/png_embed.rb +54 -0
- data/examples/roadmap.png +0 -0
- data/examples/row_renderer.rb +39 -0
- data/examples/simple_pdf_lines.rb +25 -0
- data/examples/simple_templating_example.rb +34 -0
- data/examples/tattle_ruby_version.rb +39 -0
- data/examples/tattle_rubygems_version.rb +37 -0
- data/examples/trac_ticket_status.rb +59 -0
- data/lib/ruport.rb +127 -0
- data/lib/ruport/controller.rb +616 -0
- data/lib/ruport/controller/grouping.rb +71 -0
- data/lib/ruport/controller/table.rb +54 -0
- data/lib/ruport/data.rb +4 -0
- data/lib/ruport/data/feeder.rb +111 -0
- data/lib/ruport/data/grouping.rb +399 -0
- data/lib/ruport/data/record.rb +297 -0
- data/lib/ruport/data/table.rb +950 -0
- data/lib/ruport/extensions.rb +4 -0
- data/lib/ruport/formatter.rb +254 -0
- data/lib/ruport/formatter/csv.rb +149 -0
- data/lib/ruport/formatter/html.rb +161 -0
- data/lib/ruport/formatter/pdf.rb +591 -0
- data/lib/ruport/formatter/template.rb +187 -0
- data/lib/ruport/formatter/text.rb +231 -0
- data/lib/uport.rb +1 -0
- data/test/controller_test.rb +743 -0
- data/test/csv_formatter_test.rb +164 -0
- data/test/data_feeder_test.rb +88 -0
- data/test/grouping_test.rb +410 -0
- data/test/helpers.rb +11 -0
- data/test/html_formatter_test.rb +201 -0
- data/test/pdf_formatter_test.rb +354 -0
- data/test/record_test.rb +332 -0
- data/test/samples/addressbook.csv +6 -0
- data/test/samples/data.csv +3 -0
- data/test/samples/data.tsv +3 -0
- data/test/samples/dates.csv +1409 -0
- data/test/samples/erb_test.sql +1 -0
- data/test/samples/query_test.sql +1 -0
- data/test/samples/ruport_test.sql +8 -0
- data/test/samples/test.sql +2 -0
- data/test/samples/test.yaml +3 -0
- data/test/samples/ticket_count.csv +124 -0
- data/test/table_pivot_test.rb +134 -0
- data/test/table_test.rb +838 -0
- data/test/template_test.rb +48 -0
- data/test/text_formatter_test.rb +258 -0
- data/util/bench/data/record/bench_as_vs_to.rb +18 -0
- data/util/bench/data/record/bench_constructor.rb +46 -0
- data/util/bench/data/record/bench_indexing.rb +65 -0
- data/util/bench/data/record/bench_reorder.rb +35 -0
- data/util/bench/data/record/bench_to_a.rb +19 -0
- data/util/bench/data/table/bench_column_manip.rb +103 -0
- data/util/bench/data/table/bench_dup.rb +24 -0
- data/util/bench/data/table/bench_init.rb +67 -0
- data/util/bench/data/table/bench_manip.rb +125 -0
- data/util/bench/formatter/bench_csv.rb +14 -0
- data/util/bench/formatter/bench_html.rb +14 -0
- data/util/bench/formatter/bench_pdf.rb +14 -0
- data/util/bench/formatter/bench_text.rb +14 -0
- data/util/bench/samples/tattle.csv +1237 -0
- metadata +176 -0
data/AUTHORS
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
= Developers
|
2
|
+
|
3
|
+
- {Gregory Brown}[mailto:gregory.t.brown@gmail.com]
|
4
|
+
- {Dudley Flanders}[mailto:dudley@misnomer.us]
|
5
|
+
- {James Healy}[mailto:jimmy@deefa.com]
|
6
|
+
- Dinko Mehinovic
|
7
|
+
- {Michael Milner}[mailto:mikem836@gmail.com]
|
8
|
+
|
9
|
+
= Contributors / People we've (legally) stolen from:
|
10
|
+
|
11
|
+
Iain Broadfoot:
|
12
|
+
- RuportDay 2006 Participant
|
13
|
+
|
14
|
+
Eric Pugh:
|
15
|
+
- RuportDay 2006 Participant
|
16
|
+
|
17
|
+
James Edward Gray II:
|
18
|
+
- Original inspiration via query.rb
|
19
|
+
- system_extensions.rb
|
20
|
+
|
21
|
+
Mathijs Mohlmann:
|
22
|
+
- Performance Enhancement
|
23
|
+
TextPlugin r264-r265
|
24
|
+
HTMLPlugin r266
|
25
|
+
|
26
|
+
Francis Hwang:
|
27
|
+
- SQLSplit
|
28
|
+
|
29
|
+
Simon Claret:
|
30
|
+
- Initial PDF table support (now deprecated)
|
31
|
+
|
32
|
+
Daniel Berger:
|
33
|
+
- we vendored and modified attempt.rb to support it directly in Report.
|
34
|
+
Original website: http://raa.ruby-lang.org/project/attempt/
|
35
|
+
|
36
|
+
Marshall T. Vandegrift:
|
37
|
+
- Fixed a bug in Record's struct-like accessors (method_missing)
|
38
|
+
- Provided performance enhancements and tests for query.rb
|
39
|
+
- Improved alignment for Text Tables.
|
40
|
+
|
41
|
+
Stefan Mahlitz:
|
42
|
+
- Table#sort_rows_by
|
43
|
+
|
44
|
+
Chris Carter
|
45
|
+
- Table#remove_columns (r440)
|
46
|
+
|
47
|
+
Dave Nelson
|
48
|
+
- Grouping#sigma (r1131)
|
data/LICENSE
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
Ruport is copyrighted free software originally produced by Gregory Brown
|
2
|
+
<gregory.t.brown@gmail.com> which now contains a number of community
|
3
|
+
contributions and is actively developed by Michael Milner <mikem836@gmail.com>.
|
4
|
+
|
5
|
+
See the AUTHORS file for a complete list of contributors.
|
6
|
+
|
7
|
+
Licensing terms follow (License of Ruby 1.8):
|
8
|
+
|
9
|
+
You can redistribute Ruport and/or modify it under either the terms of the GPL
|
10
|
+
(see COPYING file), or the conditions below:
|
11
|
+
|
12
|
+
1. You may make and give away verbatim copies of the source form of the
|
13
|
+
software without restriction, provided that you duplicate all of the
|
14
|
+
original copyright notices and associated disclaimers.
|
15
|
+
|
16
|
+
2. You may modify your copy of the software in any way, provided that
|
17
|
+
you do at least ONE of the following:
|
18
|
+
|
19
|
+
a) place your modifications in the Public Domain or otherwise
|
20
|
+
make them Freely Available, such as by posting said
|
21
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
22
|
+
the author to include your modifications in the software.
|
23
|
+
|
24
|
+
b) use the modified software only within your corporation or
|
25
|
+
organization.
|
26
|
+
|
27
|
+
c) rename any non-standard executables so the names do not conflict
|
28
|
+
with standard executables, which must also be provided.
|
29
|
+
|
30
|
+
d) make other distribution arrangements with the author.
|
31
|
+
|
32
|
+
3. You may distribute the software in object code or executable
|
33
|
+
form, provided that you do at least ONE of the following:
|
34
|
+
|
35
|
+
a) distribute the executables and library files of the software,
|
36
|
+
together with instructions (in the manual page or equivalent)
|
37
|
+
on where to get the original distribution.
|
38
|
+
|
39
|
+
b) accompany the distribution with the machine-readable source of
|
40
|
+
the software.
|
41
|
+
|
42
|
+
c) give non-standard executables non-standard names, with
|
43
|
+
instructions on where to get the original software distribution.
|
44
|
+
|
45
|
+
d) make other distribution arrangements with the author.
|
46
|
+
|
47
|
+
4. You may modify and include the part of the software into any other
|
48
|
+
software (possibly commercial).
|
49
|
+
|
50
|
+
5. The scripts and library files supplied as input to or produced as
|
51
|
+
output from the software do not automatically fall under the
|
52
|
+
copyright of the software, but belong to whomever generated them,
|
53
|
+
and may be sold commercially, and may be aggregated with this
|
54
|
+
software.
|
55
|
+
|
56
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
57
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
58
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
59
|
+
PURPOSE.
|
data/README
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
# -----------------------------------------------------------------
|
2
|
+
# Contents:
|
3
|
+
#
|
4
|
+
# + What Ruport Is
|
5
|
+
# + Installation
|
6
|
+
# + Resources
|
7
|
+
# + Hacking
|
8
|
+
#
|
9
|
+
# = What Ruport Is
|
10
|
+
#
|
11
|
+
# Ruby Reports (Ruport) is an extensible reporting system.
|
12
|
+
#
|
13
|
+
# It aims to be as lightweight as possible while still providing core support
|
14
|
+
# for data aggregation and manipulation as well as multi-format rendering
|
15
|
+
# of reports.
|
16
|
+
#
|
17
|
+
# Ruport provides tools for using a number of data sources, including CSV files,
|
18
|
+
# ActiveRecord models, and raw SQL connections via RubyDBI (through ruport-util).
|
19
|
+
#
|
20
|
+
# Data manipulation is easy as there are standard structures that support
|
21
|
+
# record, table, and grouping operations. These all can be extended to
|
22
|
+
# implement custom behavior as needed.
|
23
|
+
#
|
24
|
+
# For common tasks, Ruport provides formatters for CSV, HTML, PDF, and text-
|
25
|
+
# based reports. However, the real power lies in building custom report
|
26
|
+
# controllers and formatters. The base formatting libraries provide a number
|
27
|
+
# of helper functions that will let you build complex reports while maintaining
|
28
|
+
# a DRY and consistent interface.
|
29
|
+
#
|
30
|
+
# To get a quick feel for what you can accomplish with Ruport, take a look at
|
31
|
+
# a few simple examples provided on our web site.
|
32
|
+
#
|
33
|
+
# http://rubyreports.org/examples.html
|
34
|
+
#
|
35
|
+
# Since Ruport's core support is intentionally minimalistic, you may be looking
|
36
|
+
# for some higher level support for specific needs such as graphing, invoices,
|
37
|
+
# report mailing support, etc. For this, you may wish to take a look at the
|
38
|
+
# ruport-util package, which contains some generally useful tools and libraries
|
39
|
+
# to extend Ruport's capabilities.
|
40
|
+
#
|
41
|
+
# = Installation
|
42
|
+
#
|
43
|
+
# To install ruport via rubygems:
|
44
|
+
#
|
45
|
+
# sudo gem install ruport
|
46
|
+
#
|
47
|
+
# Check to see if it installed properly:
|
48
|
+
#
|
49
|
+
# ruby -rubygems -e "require 'ruport'; puts Ruport::VERSION"
|
50
|
+
#
|
51
|
+
# If you get an error, please let us know on our mailing list.
|
52
|
+
#
|
53
|
+
# Dependencies Details:
|
54
|
+
#
|
55
|
+
# -- formatting
|
56
|
+
#
|
57
|
+
# Ruport relies on PDF::Writer and FasterCSV for its formatting support.
|
58
|
+
# If you want to make use of textile helpers, you'll also need RedCloth.
|
59
|
+
#
|
60
|
+
# -- database interaction
|
61
|
+
#
|
62
|
+
# If you wish to use Ruport to report against a rails project,
|
63
|
+
# a camping project, or do standalone acts_as_reportable reports, you'll need
|
64
|
+
# ActiveRecord and the acts_as_reportable gem.
|
65
|
+
#
|
66
|
+
# If you want to use Ruport::Query for raw SQL support, you'll need to
|
67
|
+
# install ruport-util, RubyDBI and whatever database drivers you might need.
|
68
|
+
#
|
69
|
+
# = Resources
|
70
|
+
#
|
71
|
+
# Our developers have published a free-content book about all things
|
72
|
+
# Ruport, including complete coverage of acts_as_reportable and some of
|
73
|
+
# ruport-util's features. This book serves as the definitive guide to
|
74
|
+
# Ruport, so all users should become acquainted with it:
|
75
|
+
#
|
76
|
+
# http://ruportbook.com
|
77
|
+
#
|
78
|
+
# The next best way to get help and make suggestions is the Ruport mailing list.
|
79
|
+
# This software is on the move, so the list is the most reliable way of getting
|
80
|
+
# up to date information.
|
81
|
+
#
|
82
|
+
# - You can sign up and/or view the archives here:
|
83
|
+
# http://groups.google.com/group/ruby-reports
|
84
|
+
#
|
85
|
+
# If you are looking to dig a little deeper, there are a couple more resources
|
86
|
+
# that may be helpful to you.
|
87
|
+
#
|
88
|
+
# - The latest stable API documentation is available at:
|
89
|
+
# http://api.rubyreports.org
|
90
|
+
#
|
91
|
+
# - Our Trac is at: http://code.rubyreports.org/ruport
|
92
|
+
# You may use the username ruport and password blinky to file tickets.
|
93
|
+
#
|
94
|
+
# = Hacking
|
95
|
+
#
|
96
|
+
# If you'd like to contribute code to Ruport, please join our development
|
97
|
+
# mailing list, and let us know what you'd like to do!
|
98
|
+
#
|
99
|
+
# http://groups.google.com/group/ruport-dev
|
100
|
+
#
|
101
|
+
# It also may be worthwhile to join this list if you plan on running edge
|
102
|
+
# versions of Ruport, as this is where we make announcements about major
|
103
|
+
# breakage in trunk.
|
104
|
+
#
|
105
|
+
# We are very responsive to contributors, and review every patch we receive
|
106
|
+
# fairly quickly. Most contributors who successfully get a patch or two applied
|
107
|
+
# are given write access to the repositories and invited to join Ruport's
|
108
|
+
# development team. Since we view every user as potential contributor, this
|
109
|
+
# approach works well for us.
|
110
|
+
#
|
111
|
+
# So if you want to help out with Ruport, we'll happy accept your efforts!
|
112
|
+
|
113
|
+
|
114
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require "rake/rdoctask"
|
2
|
+
require "rake/testtask"
|
3
|
+
|
4
|
+
RUPORT_VERSION = "1.7.0"
|
5
|
+
|
6
|
+
begin
|
7
|
+
require "rubygems"
|
8
|
+
rescue LoadError
|
9
|
+
nil
|
10
|
+
end
|
11
|
+
|
12
|
+
task :default => [:test]
|
13
|
+
|
14
|
+
Rake::TestTask.new do |test|
|
15
|
+
test.libs << "test"
|
16
|
+
test.test_files = Dir[ "test/*_test.rb" ]
|
17
|
+
test.verbose = true
|
18
|
+
end
|
19
|
+
|
20
|
+
Rake::RDocTask.new do |rdoc|
|
21
|
+
rdoc.rdoc_files.include( "README",
|
22
|
+
#"CHANGELOG",
|
23
|
+
"AUTHORS", "COPYING",
|
24
|
+
"LICENSE", "lib/" )
|
25
|
+
rdoc.main = "README"
|
26
|
+
rdoc.rdoc_dir = "doc/html"
|
27
|
+
rdoc.title = "Ruport Documentation"
|
28
|
+
end
|
29
|
+
|
30
|
+
task :build_archives => [:package,:rcov,:rdoc] do
|
31
|
+
mv "pkg/ruport-#{RUPORT_VERSION}.tgz", "pkg/ruport-#{RUPORT_VERSION}.tar.gz"
|
32
|
+
sh "tar cjvf pkg/ruport_coverage-#{RUPORT_VERSION}.tar.bz2 coverage"
|
33
|
+
sh "tar cjvf pkg/ruport_doc-#{RUPORT_VERSION}.tar.bz2 doc/html"
|
34
|
+
cd "pkg"
|
35
|
+
sh "tar cjvf ruport-#{RUPORT_VERSION}.tar.bz2 ruport-#{RUPORT_VERSION}"
|
36
|
+
end
|
37
|
+
|
38
|
+
task :run_benchmarks do
|
39
|
+
files = FileList["util/bench/**/**/*.rb"]
|
40
|
+
files.sort!
|
41
|
+
files.uniq!
|
42
|
+
names = files.map { |r| r.sub("util/bench","").split("/").map { |e| e.capitalize } }
|
43
|
+
names.map! { |e| e[1..-2].join("::") + " <BENCH: #{e[-1].sub('Bench_','').sub('.rb','')}>" }
|
44
|
+
start_time = Time.now
|
45
|
+
files.zip(names).each { |f,n|
|
46
|
+
puts "\n#{n}\n\n"
|
47
|
+
sh "ruby -Ilib #{f}"
|
48
|
+
puts "\n"
|
49
|
+
}
|
50
|
+
end_time = Time.now
|
51
|
+
puts "\n** Total Run Time: #{end_time-start_time}s **"
|
52
|
+
end
|
53
|
+
|
54
|
+
begin
|
55
|
+
require 'rcov/rcovtask'
|
56
|
+
Rcov::RcovTask.new do |t|
|
57
|
+
t.test_files = Dir[ "test/*_test.rb" ]
|
58
|
+
end
|
59
|
+
rescue LoadError
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
|
63
|
+
begin
|
64
|
+
require 'jeweler'
|
65
|
+
Jeweler::Tasks.new do |s|
|
66
|
+
s.name = "ruport"
|
67
|
+
s.version = RUPORT_VERSION
|
68
|
+
s.platform = Gem::Platform::RUBY
|
69
|
+
s.summary = "A generalized Ruby report generation and templating engine."
|
70
|
+
s.files = Dir.glob("{examples,lib,test,bin,util/bench}/**/**/*") +
|
71
|
+
["Rakefile"]
|
72
|
+
s.require_path = "lib"
|
73
|
+
|
74
|
+
s.test_files = Dir[ "test/*_test.rb" ]
|
75
|
+
s.has_rdoc = true
|
76
|
+
s.extra_rdoc_files = %w{README LICENSE AUTHORS}
|
77
|
+
s.rdoc_options << '--title' << 'Ruport Documentation' <<
|
78
|
+
'--main' << 'README' << '-q'
|
79
|
+
s.add_dependency('fastercsv')
|
80
|
+
s.add_dependency('pdf-writer','= 1.1.8')
|
81
|
+
s.author = "Gregory Brown"
|
82
|
+
s.email = " gregory.t.brown@gmail.com"
|
83
|
+
s.rubyforge_project = "ruport"
|
84
|
+
s.homepage = "http://rubyreports.org"
|
85
|
+
s.description = <<-END_DESC
|
86
|
+
Ruby Reports is a software library that aims to make the task of reporting
|
87
|
+
less tedious and painful. It provides tools for data acquisition,
|
88
|
+
database interaction, formatting, and parsing/munging.
|
89
|
+
END_DESC
|
90
|
+
end
|
91
|
+
rescue LoadError
|
92
|
+
puts 'Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com'
|
93
|
+
end
|
Binary file
|
data/examples/anon.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Demonstrates building a parent controller which provides additional 'built in'
|
2
|
+
# formats, allowing anonymous formatter support to use the simple interface
|
3
|
+
# rather than the :format => FormatterClass approach.
|
4
|
+
|
5
|
+
require "ruport"
|
6
|
+
module FooCorp
|
7
|
+
class Controller < Ruport::Controller
|
8
|
+
def self.built_in_formats
|
9
|
+
super.merge(:xml => FooCorp::Formatter::XML)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Formatter
|
14
|
+
class XML < Ruport::Formatter
|
15
|
+
|
16
|
+
def xmlify(stuff)
|
17
|
+
output << "Wouldn't you like to see #{stuff} in XML?"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class MyController < FooCorp::Controller
|
23
|
+
stage :foo
|
24
|
+
|
25
|
+
formatter :xml do
|
26
|
+
build :foo do
|
27
|
+
xmlify "Red Snapper"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
formatter :text do
|
32
|
+
build :foo do
|
33
|
+
output << "Red Snapper"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
puts "XML:"
|
40
|
+
puts FooCorp::MyController.render_xml
|
41
|
+
|
42
|
+
puts "Text:"
|
43
|
+
puts FooCorp::MyController.render_text
|
@@ -0,0 +1,263 @@
|
|
1
|
+
# This example is a simplified version of a tool actually used at BTree.
|
2
|
+
# It does a very basic task:
|
3
|
+
#
|
4
|
+
# Given a master CSV file and a key column, it will compare the file
|
5
|
+
# to another CSV, and report back what is missing in the second CSV
|
6
|
+
# that was in the first, as well as what is changed in the second CSV
|
7
|
+
# based on the first.
|
8
|
+
#
|
9
|
+
# It is not bidirectional, and is mostly meant to compare snapshots
|
10
|
+
# of CSV dumps to see what has been removed or altered (we don't care
|
11
|
+
# about new records )
|
12
|
+
#
|
13
|
+
# It's a camping app, but the core of it is a controller/formatter combo.
|
14
|
+
# (Marked by %%%%%%%%%%% below)
|
15
|
+
#
|
16
|
+
# You'll need the camping omnibus and the F() ruport plugin to run this app.
|
17
|
+
#
|
18
|
+
# gem install camping-omnibus --source http://code.whytheluckystiff.net -y
|
19
|
+
# gem install f --source http://gems.rubyreports.org
|
20
|
+
#
|
21
|
+
# Once you have them, just run camping commaleon.rb and browse to
|
22
|
+
# http://localhost:3301
|
23
|
+
#
|
24
|
+
# Use ticket_count.csv as your master file and ticket_count2.csv as your
|
25
|
+
# comparison file. Use title as your key.
|
26
|
+
#
|
27
|
+
# Try out the different outputs, and tweak the app if you'd like to play
|
28
|
+
# with it.
|
29
|
+
#
|
30
|
+
# If your company has a need for tiny hackish camping/ruport amalgams,
|
31
|
+
# you can always ask Gregory if he's looking for work:
|
32
|
+
# <gregory.t.brown at gmail.com>
|
33
|
+
#
|
34
|
+
require "rubygems"
|
35
|
+
require "camping"
|
36
|
+
require "camping/session"
|
37
|
+
require "ruport"
|
38
|
+
require "ruport/extensions"
|
39
|
+
|
40
|
+
Camping.goes :Commaleon
|
41
|
+
|
42
|
+
module Commaleon
|
43
|
+
include Camping::Session
|
44
|
+
end
|
45
|
+
|
46
|
+
def Commaleon.create
|
47
|
+
Camping::Models::Session.create_schema
|
48
|
+
end
|
49
|
+
|
50
|
+
module Commaleon::Helpers
|
51
|
+
|
52
|
+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
53
|
+
# This is the bulk of the Ruport code in this app
|
54
|
+
# (CSVDiffController and CSVDiffFormatter)
|
55
|
+
# The rest is just camping. The interesting thing here is that
|
56
|
+
# you could easily define these in another file and just require
|
57
|
+
# them here, and use them standalone outside of your web app.
|
58
|
+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
59
|
+
|
60
|
+
class CSVDiffController < Ruport::Controller
|
61
|
+
stage :diff_report
|
62
|
+
option :key, :mcsv, :ccsv
|
63
|
+
|
64
|
+
# This setup() idiom has become the default way of doing some
|
65
|
+
# manipulations on the data and options before handing off the
|
66
|
+
# rendering task to the formatters.
|
67
|
+
#
|
68
|
+
# We're using grouping mainly for the controller support,
|
69
|
+
# and rather than reducing a table, we're building up the
|
70
|
+
# group objects via the helper methods missing_from_compare
|
71
|
+
# and different_from_compare
|
72
|
+
def setup
|
73
|
+
@master_table = Table(:string => mcsv)
|
74
|
+
@compare_table = Table(:string => ccsv)
|
75
|
+
options.diff_report = Grouping(:by => "issue")
|
76
|
+
options.diff_report << missing_from_compare
|
77
|
+
options.diff_report << different_from_compare
|
78
|
+
end
|
79
|
+
|
80
|
+
# pulls the rows that are present in the master csv but not
|
81
|
+
# in the comparison csv and returns a group.
|
82
|
+
def missing_from_compare
|
83
|
+
missing_data = @master_table.rows_with(key) do |k|
|
84
|
+
missing_keys.include?(k)
|
85
|
+
end
|
86
|
+
|
87
|
+
Group("missing from main csv",
|
88
|
+
:data => missing_data,
|
89
|
+
:column_names => @master_table.column_names )
|
90
|
+
end
|
91
|
+
|
92
|
+
# pulls the rows that are present in the master csv but match
|
93
|
+
# a row with the same key in the compare csv that do not have
|
94
|
+
# identical attributes. Returns a group
|
95
|
+
def different_from_compare
|
96
|
+
shared = master_keys & compare_keys
|
97
|
+
m = @master_table.rows_with(key) do |k|
|
98
|
+
shared.include?(k)
|
99
|
+
end
|
100
|
+
|
101
|
+
diff = m.reject do |r|
|
102
|
+
@compare_table.any? { |s| s == r }
|
103
|
+
end
|
104
|
+
|
105
|
+
diff.each do |r|
|
106
|
+
comp = @compare_table.rows_with(key => r[key])[0]
|
107
|
+
r.to_hash.each do |k,v|
|
108
|
+
v << " ## " << comp[k] unless v == comp[k]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
Group("different from main csv",
|
113
|
+
:data => diff, :column_names => @master_table.column_names)
|
114
|
+
end
|
115
|
+
|
116
|
+
def missing_keys
|
117
|
+
master_keys - compare_keys
|
118
|
+
end
|
119
|
+
|
120
|
+
def master_keys
|
121
|
+
@master_table.column(key)
|
122
|
+
end
|
123
|
+
|
124
|
+
def compare_keys
|
125
|
+
@compare_table.column(key)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# This is using the F plugin which saves us a line of code and just
|
130
|
+
# looks neat, IMO
|
131
|
+
#
|
132
|
+
# For more details:
|
133
|
+
#
|
134
|
+
# http://stonecode.svnrepository.com/ruport/trac.cgi/wiki/F
|
135
|
+
#
|
136
|
+
class CSVDiffFormatter < F([:html,:text,:csv,:pdf], :for => CSVDiffController)
|
137
|
+
def build_diff_report
|
138
|
+
# this is using the selective blocks for formatters that implement
|
139
|
+
# more than one format. The block below will only be called when this
|
140
|
+
# formatter is rendering HTML
|
141
|
+
html { gussy_up_html }
|
142
|
+
|
143
|
+
render_grouping( options.diff_report,
|
144
|
+
:style => options.style || :inline )
|
145
|
+
end
|
146
|
+
|
147
|
+
# adds headers to group name to make the output a little prettier
|
148
|
+
def gussy_up_html
|
149
|
+
options.diff_report.each do |n,g|
|
150
|
+
g.send(:name=, "<h4>#{n}</h4>")
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
module Commaleon::Controllers
|
158
|
+
|
159
|
+
class Index < R "/"
|
160
|
+
def get
|
161
|
+
redirect R(CSVDifference)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
class CSVDifference < R '/csv_diff'
|
166
|
+
def get
|
167
|
+
render :get_diff_files
|
168
|
+
end
|
169
|
+
|
170
|
+
def post
|
171
|
+
@state.mfile = @input.mfile.tempfile.read
|
172
|
+
@state.cfile = @input.cfile.tempfile.read
|
173
|
+
redirect R(GenerateDiffReport)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
class GenerateDiffReport < R '/csv_diff/report'
|
178
|
+
def get
|
179
|
+
@id_fields = Table(:string=>@state.mfile).column_names &
|
180
|
+
Table(:string=>@state.cfile).column_names
|
181
|
+
render :csv_get_id
|
182
|
+
end
|
183
|
+
|
184
|
+
def post
|
185
|
+
@state.key = @input.csv_id
|
186
|
+
@table = CSVDiffController.render_html(:key => @state.key,
|
187
|
+
:mcsv => @state.mfile,
|
188
|
+
:ccsv => @state.cfile )
|
189
|
+
render :html_diff
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
class CSVDiffReportFormatted < R '/csv_diff/report.(.*)'
|
194
|
+
def set_headers(format)
|
195
|
+
types = { "csv" => "application/vnd.ms-excel",
|
196
|
+
"pdf" => "application/pdf",
|
197
|
+
"txt" => "text/plain" }
|
198
|
+
@headers["Content-Type"] = types[format]
|
199
|
+
@headers["Content-Disposition"] = "attachment; filename=diff.#{format}"
|
200
|
+
end
|
201
|
+
|
202
|
+
def get(format)
|
203
|
+
options = { :key => @state.key,
|
204
|
+
:mcsv => @state.mfile,
|
205
|
+
:ccsv => @state.cfile }
|
206
|
+
|
207
|
+
set_headers(format)
|
208
|
+
case(format)
|
209
|
+
when "csv"
|
210
|
+
text CSVDiffController.render_csv(options)
|
211
|
+
when "pdf"
|
212
|
+
text CSVDiffController.render_pdf(options.merge(:style => :justified))
|
213
|
+
when "txt"
|
214
|
+
text CSVDiffController.render_text(options)
|
215
|
+
else
|
216
|
+
text "no format!"
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
module Commaleon::Views
|
224
|
+
def get_diff_files
|
225
|
+
form :action => "?upload_id=#{Time.now.to_f}", :method => 'post',
|
226
|
+
:enctype => 'multipart/form-data' do
|
227
|
+
p do
|
228
|
+
label "Master File: ", :for => "mfile"
|
229
|
+
input({:name => "mfile", :type => 'file'})
|
230
|
+
end
|
231
|
+
p do
|
232
|
+
label "Comparison File: ", :for => "cfile"
|
233
|
+
input({:name => "cfile", :type => 'file'})
|
234
|
+
end
|
235
|
+
p do
|
236
|
+
input.newfile! :type => "submit", :value => "Upload"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def csv_get_id
|
242
|
+
form :method => "post" do
|
243
|
+
label "ID column: ", :for => "csv_id"
|
244
|
+
select(:name => "csv_id") do
|
245
|
+
@id_fields.each { |f| option(f) }
|
246
|
+
end
|
247
|
+
input :type => "submit", :value => "Set ID"
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def html_diff
|
252
|
+
text @table
|
253
|
+
hr
|
254
|
+
ul do
|
255
|
+
li { a "New Diff", :href => R(CSVDifference) }
|
256
|
+
li { a "New Key for Diff", :href => R(GenerateDiffReport) }
|
257
|
+
li { a "CSV Download", :href => R(CSVDiffReportFormatted,"csv") }
|
258
|
+
li { a "Text Download", :href => R(CSVDiffReportFormatted,"txt") }
|
259
|
+
li { a "PDF Download", :href => R(CSVDiffReportFormatted,"pdf") }
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|