extract 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.lre +1 -0
- data/.rspec +1 -0
- data/Gemfile +27 -0
- data/Gemfile.lock +108 -0
- data/Guardfile +27 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/extract.gemspec +128 -0
- data/lib/extract.rb +39 -0
- data/lib/extract/excel_formulas.rb +44 -0
- data/lib/extract/formula.treetop +66 -0
- data/lib/extract/math.treetop +33 -0
- data/lib/extract/math_calc.rb +111 -0
- data/lib/extract/parser.rb +90 -0
- data/lib/extract/persist/sheet.rb +26 -0
- data/lib/extract/sheet.rb +85 -0
- data/lib/extract/sheet_comp.rb +7 -0
- data/lib/extract/sheet_definition.rb +127 -0
- data/lib/extract/tree/base.rb +7 -0
- data/lib/extract/tree/cell.rb +33 -0
- data/lib/extract/tree/cond_exp.rb +25 -0
- data/lib/extract/tree/formula.rb +24 -0
- data/lib/extract/tree/formula_args.rb +30 -0
- data/lib/extract/tree/math.rb +106 -0
- data/lib/extract/tree/num.rb +18 -0
- data/lib/extract/tree/operator.rb +9 -0
- data/lib/extract/tree/range.rb +58 -0
- data/lib/extract/tree/string.rb +12 -0
- data/samples/baseball.xlsx +0 -0
- data/samples/div.xlsx +0 -0
- data/spec/config/mongoid.yml +6 -0
- data/spec/deps_spec.rb +48 -0
- data/spec/extract_spec.rb +44 -0
- data/spec/math_spec.rb +52 -0
- data/spec/parser_spec.rb +145 -0
- data/spec/persist_spec.rb +34 -0
- data/spec/sheet_definition_spec.rb +46 -0
- data/spec/sheet_spec.rb +51 -0
- data/spec/spec_helper.rb +68 -0
- data/vol/excel_test.rb +55 -0
- data/vol/parse_test.rb +8 -0
- data/vol/scratch.rb +61 -0
- data/vol/web.rb +0 -0
- data/vol/yaml_test.rb +4 -0
- data/web/file.tmp +0 -0
- data/web/file.xlsx +0 -0
- data/web/main.rb +59 -0
- data/web/mongoid.yml +6 -0
- data/web/views/index.haml +39 -0
- data/web/views/upload.haml +13 -0
- metadata +311 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
module Extract
|
2
|
+
module Tree
|
3
|
+
module Cell
|
4
|
+
def proper_cell
|
5
|
+
text_value.gsub("-","").gsub("$","")
|
6
|
+
end
|
7
|
+
def leading_neg?
|
8
|
+
text_value[0..0] == '-'
|
9
|
+
end
|
10
|
+
def excel_value
|
11
|
+
res = find_sheet[proper_cell]
|
12
|
+
#raise proper_cell if text_value == "-A2"
|
13
|
+
if res.present?
|
14
|
+
leading_neg? ? res * -1 : res
|
15
|
+
else
|
16
|
+
res
|
17
|
+
end
|
18
|
+
end
|
19
|
+
def row
|
20
|
+
r.text_value.to_i
|
21
|
+
end
|
22
|
+
def col
|
23
|
+
c.text_value
|
24
|
+
end
|
25
|
+
def deps
|
26
|
+
[proper_cell]
|
27
|
+
end
|
28
|
+
def tt
|
29
|
+
:cell
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Extract
|
2
|
+
module Tree
|
3
|
+
module CondExp
|
4
|
+
def excel_value
|
5
|
+
if op.text_value == "="
|
6
|
+
a.excel_value == b.excel_value
|
7
|
+
elsif op.text_value == ">"
|
8
|
+
a.excel_value > b.excel_value
|
9
|
+
elsif op.text_value == "<"
|
10
|
+
a.excel_value < b.excel_value
|
11
|
+
elsif op.text_value == ">="
|
12
|
+
a.excel_value >= b.excel_value
|
13
|
+
elsif op.text_value == "<="
|
14
|
+
a.excel_value <= b.excel_value
|
15
|
+
else
|
16
|
+
raise "bad"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def deps
|
21
|
+
[a.text_value,b.text_value].uniq
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Extract
|
2
|
+
module Tree
|
3
|
+
module Formula
|
4
|
+
def excel_value
|
5
|
+
args = formula_args.excel_values
|
6
|
+
ExcelFormulas.send(formula_name.text_value.downcase,*args)
|
7
|
+
end
|
8
|
+
def deps
|
9
|
+
#raise "foo"
|
10
|
+
formula_args.deps.flatten.map do |f|
|
11
|
+
if f =~ /^-/
|
12
|
+
raise 'foo'
|
13
|
+
f[1..-1]
|
14
|
+
else
|
15
|
+
f
|
16
|
+
end
|
17
|
+
end.uniq
|
18
|
+
end
|
19
|
+
def tt
|
20
|
+
:formula
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Extract
|
2
|
+
module Tree
|
3
|
+
module FormulaArgs
|
4
|
+
def excel_values
|
5
|
+
res = []
|
6
|
+
res << formula_arg.excel_value
|
7
|
+
|
8
|
+
rest.elements.each do |e|
|
9
|
+
arg = e.elements[1]
|
10
|
+
res << arg.excel_value
|
11
|
+
end
|
12
|
+
|
13
|
+
res
|
14
|
+
end
|
15
|
+
|
16
|
+
def deps
|
17
|
+
res = []
|
18
|
+
res << formula_arg.deps
|
19
|
+
|
20
|
+
rest.elements.each do |e|
|
21
|
+
arg = e.elements[1]
|
22
|
+
raise "no deps for arg #{arg.inspect}" unless arg.respond_to?(:deps)
|
23
|
+
res << arg.deps
|
24
|
+
end
|
25
|
+
|
26
|
+
res.flatten
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module Extract
|
2
|
+
module Tree
|
3
|
+
module Math
|
4
|
+
def get_math_exp
|
5
|
+
if respond_to?(:math_exp)
|
6
|
+
math_exp
|
7
|
+
elsif respond_to?(:math_exp_full)
|
8
|
+
math_exp_full
|
9
|
+
elsif respond_to?(:num)
|
10
|
+
num
|
11
|
+
elsif respond_to?(:primary)
|
12
|
+
primary
|
13
|
+
else
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
def excel_value
|
18
|
+
return eval
|
19
|
+
raise 'foo'
|
20
|
+
unless get_math_exp
|
21
|
+
str = %w(math_exp naked_exp cell).map { |x| "#{x} #{respond_to?(x)}" }.join(", ")
|
22
|
+
#raise str + "\n" + inspect
|
23
|
+
#raise (methods - 7.methods).inspect + "\n" + inspect
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
res = 0
|
28
|
+
#res = math_exp.excel_value if respond_to?(:math_exp)
|
29
|
+
|
30
|
+
rest.elements.each do |e|
|
31
|
+
#arg = e.elements[1]
|
32
|
+
#res += arg.excel_value
|
33
|
+
#res +=
|
34
|
+
end
|
35
|
+
|
36
|
+
res
|
37
|
+
end
|
38
|
+
|
39
|
+
def deps(start=self)
|
40
|
+
res = []
|
41
|
+
#res << get_math_exp.deps
|
42
|
+
|
43
|
+
return [] unless start.elements
|
44
|
+
|
45
|
+
start.elements.each do |e|
|
46
|
+
#arg = e.elements[1]
|
47
|
+
if e.respond_to?(:deps)
|
48
|
+
res << e.deps
|
49
|
+
else
|
50
|
+
res << deps(e)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
res.flatten.select { |x| x }
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
def tokens(start=self)
|
59
|
+
if start.respond_to?("paren?")
|
60
|
+
res = start.math_exp.eval
|
61
|
+
return [OpenStruct.new(:text_value => res.to_s)]
|
62
|
+
#return [start.exp.eval]
|
63
|
+
end
|
64
|
+
#puts "parsing #{start.text_value} #{start.class}"
|
65
|
+
res = []
|
66
|
+
|
67
|
+
return res unless start.elements
|
68
|
+
start.elements.each do |el|
|
69
|
+
if el.respond_to?(:tt)
|
70
|
+
t = el.tt
|
71
|
+
if t == :num
|
72
|
+
res << el
|
73
|
+
elsif t == :operator
|
74
|
+
res << el
|
75
|
+
elsif t == :cell
|
76
|
+
res << el
|
77
|
+
elsif t == :formula
|
78
|
+
res << el
|
79
|
+
elsif t == :math
|
80
|
+
res += el.tokens
|
81
|
+
else
|
82
|
+
raise "unknown"
|
83
|
+
end
|
84
|
+
else
|
85
|
+
res += tokens(el)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
res
|
89
|
+
end
|
90
|
+
|
91
|
+
def eval
|
92
|
+
#puts "evaling #{text_value}"
|
93
|
+
#raise tokens.map { |x| x.text_value }.inspect + "\n" + inspect
|
94
|
+
MathCalc.parse_eval(tokens)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
module ParenMath
|
99
|
+
include Math
|
100
|
+
|
101
|
+
def paren?
|
102
|
+
true
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Extract
|
2
|
+
module Tree
|
3
|
+
module Range
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def cells_in_range_nodes(a,b)
|
7
|
+
res = []
|
8
|
+
((a.row)..(b.row)).each do |r|
|
9
|
+
tmp = []
|
10
|
+
((a.col)..(b.col)).each do |c|
|
11
|
+
tmp << "#{c}#{r}"
|
12
|
+
end
|
13
|
+
res << tmp
|
14
|
+
end
|
15
|
+
res
|
16
|
+
end
|
17
|
+
|
18
|
+
def cells_in_range(str)
|
19
|
+
arr = str.split(":").tap { |x| raise "bad" unless x.size == 2 }.map do |c|
|
20
|
+
raise "bad" unless c =~ /^([A-Z])([0-9]+)$/
|
21
|
+
OpenStruct.new(:row => $2, :col => $1)
|
22
|
+
end
|
23
|
+
cells_in_range_nodes(*arr)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def excel_value_old
|
28
|
+
res = []
|
29
|
+
((a.row)..(b.row)).each do |r|
|
30
|
+
tmp = []
|
31
|
+
((a.col)..(b.col)).each do |c|
|
32
|
+
tmp << find_sheet["#{c}#{r}"]
|
33
|
+
end
|
34
|
+
res << tmp
|
35
|
+
end
|
36
|
+
res
|
37
|
+
end
|
38
|
+
|
39
|
+
def excel_value
|
40
|
+
Extract::Tree::Range.cells_in_range_nodes(a,b).map do |arr|
|
41
|
+
arr.map do |c|
|
42
|
+
find_sheet[c]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
def deps
|
47
|
+
res = []
|
48
|
+
((a.row)..(b.row)).each do |r|
|
49
|
+
tmp = []
|
50
|
+
((a.col)..(b.col)).each do |c|
|
51
|
+
res << "#{c}#{r}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
res.flatten.select { |x| x }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
Binary file
|
data/samples/div.xlsx
ADDED
Binary file
|
data/spec/deps_spec.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
def should_have_deps(cell,*deps)
|
4
|
+
it "#{cell} should have deps #{deps.inspect}" do
|
5
|
+
if cell =~ /^=/
|
6
|
+
sheet["X99"] = cell
|
7
|
+
sheet.deps("X99").uniq.sort.should == deps.flatten.select { |x| x }.sort
|
8
|
+
else
|
9
|
+
sheet.deps(cell).should == deps.flatten.select { |x| x }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "Deps" do
|
15
|
+
let(:sheet) do
|
16
|
+
res = Extract::Sheet.new
|
17
|
+
{"A1" => 1, "A2" => 2, "A3" => 3, "B1" => 4, "B2" => 5, "B3" => 6}.each do |k,v|
|
18
|
+
res[k] = v
|
19
|
+
end
|
20
|
+
res["C1"] = "=A1"
|
21
|
+
res['D1'] = "=C1"
|
22
|
+
res['E1'] = "=D1"
|
23
|
+
|
24
|
+
res['F1'] = "=A1+A2"
|
25
|
+
res
|
26
|
+
end
|
27
|
+
it 'smoke' do
|
28
|
+
sheet['C1'].should == 1
|
29
|
+
end
|
30
|
+
|
31
|
+
should_have_deps "C1","A1"
|
32
|
+
should_have_deps "14",[]
|
33
|
+
should_have_deps "D1","A1"
|
34
|
+
should_have_deps "E1","A1"
|
35
|
+
|
36
|
+
should_have_deps "F1","A1","A2"
|
37
|
+
|
38
|
+
should_have_deps "=A1+4","A1"
|
39
|
+
|
40
|
+
should_have_deps "=A1 = A2","A1","A2"
|
41
|
+
|
42
|
+
should_have_deps "=DOUBLE(A1)","A1"
|
43
|
+
should_have_deps "=SUM(A1,A2)","A1","A2"
|
44
|
+
should_have_deps "=SUM(A1:B2)","A1","A2","B1","B2"
|
45
|
+
should_have_deps "=SUM(A1,C1)","A1"
|
46
|
+
|
47
|
+
should_have_deps "=SUM(2+3+A1,A2)","A1","A2"
|
48
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
if false
|
3
|
+
describe "Extract" do
|
4
|
+
it 'smoke' do
|
5
|
+
2.should == 2
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'cash flow' do
|
9
|
+
sheet = Extract::Sheet.load("/users/mharris717/documents/cashflow.xlsx")
|
10
|
+
|
11
|
+
#sheet["A1"] = 999
|
12
|
+
sheet["B42"].to_i.should == 14888
|
13
|
+
end
|
14
|
+
|
15
|
+
if true
|
16
|
+
it 'dev' do
|
17
|
+
sheet = Extract::Sheet.load("/users/mharris717/code/orig/extract/samples/div.xlsx")
|
18
|
+
|
19
|
+
#sheet["A1"] = 999
|
20
|
+
sheet["B52"].to_i.should == 10
|
21
|
+
%w(B38 B41 B51 B52).each do |c|
|
22
|
+
sheet[c]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
describe 'Dev Sheet' do
|
31
|
+
let(:sheet_def) do
|
32
|
+
Extract::SheetDefinition.load("/users/mharris717/code/orig/extract/samples/div.xlsx",output_cells)
|
33
|
+
end
|
34
|
+
let(:output_cells) do
|
35
|
+
%w(B38 B41 B51 B52)
|
36
|
+
end
|
37
|
+
it 'input cells' do
|
38
|
+
exp = ["B30", "B29", "B30", "B31", "B46", "B47", "C35", "D35", "E35", "F35", "G35", "H35", "I35", "J35", "K35", "L35", "M35"].sort.uniq
|
39
|
+
sheet_def.input_cells.sort.should == exp
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|