extract 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. data/.document +5 -0
  2. data/.lre +1 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +27 -0
  5. data/Gemfile.lock +108 -0
  6. data/Guardfile +27 -0
  7. data/LICENSE.txt +20 -0
  8. data/README.rdoc +19 -0
  9. data/Rakefile +49 -0
  10. data/VERSION +1 -0
  11. data/extract.gemspec +128 -0
  12. data/lib/extract.rb +39 -0
  13. data/lib/extract/excel_formulas.rb +44 -0
  14. data/lib/extract/formula.treetop +66 -0
  15. data/lib/extract/math.treetop +33 -0
  16. data/lib/extract/math_calc.rb +111 -0
  17. data/lib/extract/parser.rb +90 -0
  18. data/lib/extract/persist/sheet.rb +26 -0
  19. data/lib/extract/sheet.rb +85 -0
  20. data/lib/extract/sheet_comp.rb +7 -0
  21. data/lib/extract/sheet_definition.rb +127 -0
  22. data/lib/extract/tree/base.rb +7 -0
  23. data/lib/extract/tree/cell.rb +33 -0
  24. data/lib/extract/tree/cond_exp.rb +25 -0
  25. data/lib/extract/tree/formula.rb +24 -0
  26. data/lib/extract/tree/formula_args.rb +30 -0
  27. data/lib/extract/tree/math.rb +106 -0
  28. data/lib/extract/tree/num.rb +18 -0
  29. data/lib/extract/tree/operator.rb +9 -0
  30. data/lib/extract/tree/range.rb +58 -0
  31. data/lib/extract/tree/string.rb +12 -0
  32. data/samples/baseball.xlsx +0 -0
  33. data/samples/div.xlsx +0 -0
  34. data/spec/config/mongoid.yml +6 -0
  35. data/spec/deps_spec.rb +48 -0
  36. data/spec/extract_spec.rb +44 -0
  37. data/spec/math_spec.rb +52 -0
  38. data/spec/parser_spec.rb +145 -0
  39. data/spec/persist_spec.rb +34 -0
  40. data/spec/sheet_definition_spec.rb +46 -0
  41. data/spec/sheet_spec.rb +51 -0
  42. data/spec/spec_helper.rb +68 -0
  43. data/vol/excel_test.rb +55 -0
  44. data/vol/parse_test.rb +8 -0
  45. data/vol/scratch.rb +61 -0
  46. data/vol/web.rb +0 -0
  47. data/vol/yaml_test.rb +4 -0
  48. data/web/file.tmp +0 -0
  49. data/web/file.xlsx +0 -0
  50. data/web/main.rb +59 -0
  51. data/web/mongoid.yml +6 -0
  52. data/web/views/index.haml +39 -0
  53. data/web/views/upload.haml +13 -0
  54. metadata +311 -0
data/spec/math_spec.rb ADDED
@@ -0,0 +1,52 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ def should_parse_math(str,val=nil)
4
+ it "math parse #{str}" do
5
+ old_str = str
6
+ str = str.gsub(" ","")#.gsub(/[\(\)]/,"")
7
+ res = MathMyParser.new.parse(str)
8
+ #puts res.inspect
9
+
10
+ res.should be
11
+ if true
12
+ if val
13
+ res.eval.should == val
14
+ else
15
+ raise res.inspect unless res.respond_to?(:eval)
16
+ res.eval.to_f.should == eval(old_str.gsub("^","**")).to_f
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ def should_calc_to(str,val)
23
+ Extract::MathCalc.parse_eval(str).should == val
24
+ end
25
+
26
+ describe "Math" do
27
+ should_parse_math "23 + 45 + 67 + 89"
28
+
29
+ #should_parse_math "(2)"
30
+ should_parse_math "2+3"
31
+ should_parse_math "2 + (3 + 4)"
32
+ should_parse_math "(2 + 3) + 4"
33
+ should_parse_math "(2 + (3 + 4)) + 5"
34
+
35
+ should_parse_math "3 + 4 * 5"
36
+ should_parse_math "3 * 4 + 5"
37
+
38
+
39
+
40
+ should_parse_math "(3 + 4) * 5"
41
+
42
+ should_parse_math "1.5 + 2.4"
43
+
44
+ should_parse_math "-2 + -3"
45
+
46
+ should_parse_math "2^3"
47
+ should_parse_math "2^3 * 3^2^(1+1)"
48
+ end
49
+
50
+ describe "Math" do
51
+ #should_calc_to "2 + 3",5
52
+ end
@@ -0,0 +1,145 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ def should_parse(str)
4
+ it "#{str} parses" do
5
+ begin
6
+ str.should parse
7
+ rescue => exp
8
+ res = FormulaParser.new().parse(str, :consume_all_input => false)
9
+ raise res.inspect
10
+ raise exp
11
+ end
12
+ end
13
+ end
14
+
15
+ def should_parse_to(str,val)
16
+ it "#{str} parses to #{val}" do
17
+ result = Extract::Parser.new(:str => str, :sheet => sheet).result
18
+ begin
19
+ res = Extract::Parser.new(:str => str, :sheet => sheet).excel_value
20
+ #puts result.inspect unless res == val
21
+ res.should == val
22
+ rescue => exp
23
+ puts result.inspect
24
+ raise exp
25
+ end
26
+ end
27
+ end
28
+
29
+ describe "Extract" do
30
+
31
+ it 'smoke' do
32
+ 2.should == 2
33
+ end
34
+
35
+ it 'number' do
36
+ "=4".should parse
37
+ end
38
+
39
+ let(:sheet) do
40
+ res = Extract::Sheet.new
41
+ {"A1" => 1, "A2" => 2, "A3" => 3, "B1" => 4, "B2" => 5, "B3" => 6}.each do |k,v|
42
+ res[k] = v
43
+ end
44
+ res
45
+ end
46
+
47
+ should_parse "=4"
48
+ should_parse "=4+5"
49
+ should_parse "=4 + 5"
50
+
51
+ should_parse "=A1"
52
+ should_parse "=THING(A1)"
53
+ should_parse "=THING(A1:A9)"
54
+ should_parse "=THING(A1,A2)"
55
+ should_parse "=THING(A1, A2)"
56
+
57
+ should_parse "=THING(THING(A1))"
58
+ should_parse "=THING(THING(A1:A9))"
59
+ should_parse "=THING(THING(A1,A2:A9))"
60
+
61
+ should_parse_to "=A1",1
62
+ should_parse_to "=A2",2
63
+
64
+ should_parse_to "=DOUBLE(A1)",2
65
+ should_parse_to "=DOUBLE(DOUBLE(A1))",4
66
+ should_parse_to "=DOUBLE(A2)",4
67
+ should_parse_to "=DOUBLE(3)",6
68
+ should_parse_to "=DOUBLE(DOUBLE(3))",12
69
+
70
+ should_parse_to "=SUM(A1,A2)",3
71
+ should_parse_to "=SUM(A1,A2,3)",6
72
+ should_parse_to "=SUM(A1,A2,A2)",5
73
+
74
+ should_parse_to "=3 + 4",7
75
+ should_parse_to "=3 + 4 + 5",12
76
+
77
+
78
+ should_parse "= 3 + 4 + A2"
79
+
80
+
81
+ should_parse_to "=3 + 4 + A2",9
82
+
83
+
84
+ should_parse_to "=3 + A2 + 4",9
85
+ should_parse_to "=3 + DOUBLE(5)",13
86
+
87
+ should_parse_to "=2 = 3",false
88
+ should_parse_to "=2 = 2",true
89
+
90
+ should_parse_to "=IF(2=2,5,6)",5
91
+ should_parse_to "=IF(2=3,5,6)",6
92
+ should_parse_to "=IF(2 > 1,5,6)",5
93
+ should_parse_to "=IF(2 > 3,5,6)",6
94
+ should_parse_to "=IF(TRUE,5,6)",5
95
+ should_parse_to "=IF(FALSE,5,6)",6
96
+
97
+ should_parse_to "=IF(2=2,DOUBLE(2),DOUBLE(3))",4
98
+ should_parse_to "=IF(2=3,DOUBLE(2),DOUBLE(3))",6
99
+
100
+ should_parse_to "=SUM(A1:A2)",3
101
+ should_parse_to "=SUM(A1:A4)",6
102
+ should_parse_to "=SUM(A1:B4)",21
103
+ should_parse_to "=MAX(A1:B4)",6
104
+
105
+ should_parse_to "=IF(A2>A1,5,6)",5
106
+ should_parse_to "=IF(A1>A2,5,6)",6
107
+
108
+ should_parse_to "=VLOOKUP(2,A1:B6,1)",2
109
+ should_parse_to "=VLOOKUP(2,A1:B6,2)",5
110
+
111
+ should_parse_to "=A1:B2",[[1,4],[2,5]]
112
+
113
+ should_parse_to "=-2 * -3",6
114
+ should_parse_to "=-2 * A2",-4
115
+ should_parse_to "=A2 * -2",-4
116
+
117
+ should_parse_to "=-A2",-2
118
+
119
+ should_parse_to "=DOUBLE(3 * 4)",24
120
+
121
+ should_parse_to '="abc"="abc"',true
122
+ should_parse_to '="abc"="abd"',false
123
+ should_parse_to "=DOUBLE(2)=4",true
124
+ should_parse_to "=DOUBLE(2)=5",false
125
+ should_parse_to "=(2+3)*2=10",true
126
+ should_parse_to "=(2+3)*2=11",false
127
+ #should_parse_to '=A1="N/A'
128
+
129
+ should_parse_to '=IF(A1="N/A",0,A1*A2)',2
130
+
131
+ should_parse_to "=$A1 + A2",3
132
+
133
+ should_parse_to "=3^2",9
134
+ should_parse_to "=IF(A1<=$A$2,3,4)",3
135
+
136
+ should_parse_to "=COMBIN($A$2*2,A1*2)",6
137
+ should_parse "=COMBIN(A2,A1)*B3"
138
+ should_parse "=COMBIN($A$2,A1)*$B$31^C35"
139
+ should_parse '=IF(C35<=$B$30,COMBIN($B$30,C35)*$B$31^C35*(1-$B$31)^($B$30-C35),17)'
140
+ should_parse '=IF(C35<=$B$30,COMBIN($B$30,C35)*$B$31^C35*(1-$B$31)^($B$30-C35),"abc")'
141
+ #should_parse_to "=COMBIN($A$2,A1)*$B$31^C35*(1-$B$31)^($B$30-C35)",99
142
+
143
+ should_parse_to '=IF(2<1,14,"N/A")',"N/A"
144
+ should_parse_to '=IF(2<3,"ABC","N/A")',"ABC"
145
+ end
@@ -0,0 +1,34 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'Persist' do
4
+ let(:sheet) do
5
+ res = Extract::Sheet.new
6
+ res["A1"] = 1
7
+ res["A2"] = "=DOUBLE(A1)"
8
+ res["A3"] = "=DOUBLE(A2)"
9
+ res["A4"] = "=IF(2>3,A2,A3)"
10
+
11
+ res["B1"] = 1
12
+ res["B2"] = 2
13
+ res["B3"] = "=IF(B1>B2,5,6)"
14
+ res
15
+ end
16
+
17
+ let(:sheet_def) do
18
+ Extract::SheetDefinition.new(:sheet => sheet, :output_cells => %w(B3))
19
+ end
20
+
21
+ before do
22
+ Extract::Persist::Sheet.destroy_all
23
+ end
24
+
25
+
26
+ it 'smoke' do
27
+ res = sheet_def.save!
28
+ res.should be
29
+ Extract::Persist::Sheet.count.should == 1
30
+
31
+ s = Extract::Persist::Sheet.first.sheet_def
32
+ s["B3"].should == 6
33
+ end
34
+ end
@@ -0,0 +1,46 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "SheetDefinition" do
4
+ let(:sheet) do
5
+ res = Extract::Sheet.new
6
+ {"A1" => 1, "A2" => 2, "A3" => 3, "B1" => 4, "B2" => 5, "B3" => 6}.each do |k,v|
7
+ res[k] = v
8
+ end
9
+ res["C1"] = "=A1"
10
+ res['D1'] = "=C1"
11
+ res['E1'] = "=D1"
12
+
13
+ res['F1'] = "=A1+A2"
14
+ res['G1'] = "=A3"
15
+ res
16
+ end
17
+ let(:sheet_def) do
18
+ Extract::SheetDefinition.new(:sheet => sheet, :output_cells => output_cells)
19
+ end
20
+
21
+ describe "simple" do
22
+ let(:output_cells) { %w(F1) }
23
+ it 'has inputs' do
24
+ sheet_def.input_cells.should == %w(A1 A2)
25
+ end
26
+ end
27
+
28
+ describe "output cell without formula becomes input" do
29
+ let(:output_cells) { %w(F1 A3) }
30
+ it 'has inputs' do
31
+ sheet_def.input_cells.sort.should == %w(A1 A2 A3)
32
+ end
33
+ end
34
+
35
+ describe "output range" do
36
+ let(:output_cells) { %w(D1:G1) }
37
+
38
+ it 'sets output correctly' do
39
+ sheet_def.output_cells.should == %w(D1 E1 F1 G1)
40
+ end
41
+
42
+ it 'has input' do
43
+ sheet_def.input_cells.sort.should == %w(A1 A2 A3)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,51 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ def should_eval_to(formula,val)
4
+ it "thing" do
5
+
6
+ sheet["Z99"] = formula
7
+ sheet["Z99"].should == val
8
+ end
9
+ end
10
+
11
+ describe "Sheet" do
12
+ let(:sheet) do
13
+ res = Extract::Sheet.new
14
+ res["A1"] = 1
15
+ res["A2"] = "=DOUBLE(A1)"
16
+ res["A3"] = "=DOUBLE(A2)"
17
+ res["A4"] = "=IF(2>3,A2,A3)"
18
+
19
+ res["B1"] = 1
20
+ res["B2"] = 2
21
+ res["B3"] = "=IF(B1>B2,5,6)"
22
+ res
23
+ end
24
+
25
+ it 'A2 works' do
26
+ sheet["A2"].should == 2
27
+ end
28
+
29
+ it 'A3 works' do
30
+ sheet["A3"].should == 4
31
+ end
32
+
33
+ it 'A4 works' do
34
+ sheet["A4"].should == 4
35
+ end
36
+
37
+ it 'B3' do
38
+ sheet['B3'].should == 6
39
+ end
40
+
41
+ it 'B3 changed' do
42
+ sheet['B1'] = 99
43
+ sheet['B3'].should == 5
44
+ end
45
+
46
+ should_eval_to "=2 + 3",5
47
+ should_eval_to "=B1 + 2",3
48
+ #should_eval_to "=-1*B1",-1
49
+
50
+
51
+ end
@@ -0,0 +1,68 @@
1
+ require 'rubygems'
2
+ require 'spork'
3
+ #uncomment the following line to use spork with the debugger
4
+ #require 'spork/ext/ruby-debug'
5
+
6
+ Spork.prefork do
7
+ # Loading more in this block will cause your tests to run faster. However,
8
+ # if you change any configuration or code from libraries loaded here, you'll
9
+ # need to restart spork for it take effect.
10
+
11
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
12
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
13
+ require 'rspec'
14
+ require 'extract'
15
+
16
+ # Requires supporting files with custom matchers and macros, etc,
17
+ # in ./support/ and its subdirectories.
18
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
19
+
20
+ RSpec.configure do |config|
21
+
22
+ end
23
+
24
+ RSpec::Matchers.define :parse do
25
+ match do |actual|
26
+ !!Extract::Parser.new(:str => actual).result
27
+ end
28
+ end
29
+
30
+ f = File.expand_path(File.dirname(__FILE__)) + "/config/mongoid.yml"
31
+ Mongoid.load! f,'development'
32
+ end
33
+
34
+ Spork.each_run do
35
+ load File.expand_path(File.dirname(__FILE__)) + "/../lib/extract.rb"
36
+ # This code will be run each time you run your specs.
37
+
38
+ end
39
+
40
+ # --- Instructions ---
41
+ # Sort the contents of this file into a Spork.prefork and a Spork.each_run
42
+ # block.
43
+ #
44
+ # The Spork.prefork block is run only once when the spork server is started.
45
+ # You typically want to place most of your (slow) initializer code in here, in
46
+ # particular, require'ing any 3rd-party gems that you don't normally modify
47
+ # during development.
48
+ #
49
+ # The Spork.each_run block is run each time you run your specs. In case you
50
+ # need to load files that tend to change during development, require them here.
51
+ # With Rails, your application modules are loaded automatically, so sometimes
52
+ # this block can remain empty.
53
+ #
54
+ # Note: You can modify files loaded *from* the Spork.each_run block without
55
+ # restarting the spork server. However, this file itself will not be reloaded,
56
+ # so if you change any of the code inside the each_run block, you still need to
57
+ # restart the server. In general, if you have non-trivial code in this file,
58
+ # it's advisable to move it into a separate file so you can easily edit it
59
+ # without restarting spork. (For example, with RSpec, you could move
60
+ # non-trivial code into a file spec/support/my_helper.rb, making sure that the
61
+ # spec/support/* files are require'd from inside the each_run block.)
62
+ #
63
+ # Any code that is left outside the two blocks will be run during preforking
64
+ # *and* during each_run -- that's probably not what you want.
65
+ #
66
+
67
+
68
+
data/vol/excel_test.rb ADDED
@@ -0,0 +1,55 @@
1
+
2
+
3
+
4
+
5
+ sheet = Extract::Sheet.load("/users/mharris717/code/orig/extract/samples/div.xlsx")
6
+
7
+ #sheet["A1"] = 999
8
+ #puts sheet["B42"]
9
+
10
+ h = {}
11
+ sheet.each_value_comp do |k,form,calc,act|
12
+ h[k] = [form,calc,act] unless calc == act
13
+ end
14
+
15
+ h.each do |k,v|
16
+ puts "#{k} #{v.inspect}"
17
+ end
18
+
19
+ #puts sheet["E38"]
20
+
21
+ #IF(C35<=$B$30,COMBIN($B$30,C35)*$B$31^C35*(1-$B$31)^($B$30-C35)
22
+
23
+
24
+ if false
25
+ h = {}
26
+
27
+ res = []
28
+ res << "C35<=$B$30"
29
+ res << "COMBIN($B$30,C35)"
30
+ res << "$B$31^C35"
31
+ res << "(1-$B$31)^($B$30-C35)"
32
+ res.each do |f|
33
+ val = sheet.eval("=#{f}")
34
+ h[f] = val
35
+ end
36
+ h.each { |k,v| puts "#{k}: #{v}"}
37
+
38
+ h = {}
39
+ ("A".."M").each do |col|
40
+ (1..60).each do |i|
41
+ k = "#{col}#{i}"
42
+ val = sheet[k]
43
+ h[k] = val if val.present? && !(val.to_s =~ /[a-z]/i)
44
+ end
45
+ end
46
+
47
+ h.each do |k,v|
48
+ puts "#{k}: #{v}"
49
+ end
50
+
51
+ puts sheet["E38"]
52
+ #puts sheet.deps("B42").inspect
53
+ end
54
+
55
+ %w(B38 B41 B51 B52)