extract 0.1.1 → 0.1.3
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.
- checksums.yaml +7 -0
- data/Gemfile +8 -4
- data/Gemfile.lock +116 -59
- data/VERSION +1 -1
- data/extract.gemspec +29 -17
- data/lib/extract.rb +33 -7
- data/lib/extract/cell.rb +34 -0
- data/lib/extract/excel_formulas.rb +1 -1
- data/lib/extract/export/ddl.rb +8 -0
- data/lib/extract/export/table.rb +48 -0
- data/lib/extract/formula.treetop +3 -0
- data/lib/extract/inline_def.rb +38 -0
- data/lib/extract/sheet.rb +12 -4
- data/lib/extract/sheet_definition.rb +37 -12
- data/lib/extract/table.rb +67 -0
- data/lib/extract/tables.rb +39 -0
- data/samples/salescalls.xlsx +0 -0
- data/spec/cell_spec.rb +31 -0
- data/spec/config/mongoid.yml +8 -1
- data/spec/export/table_spec.rb +45 -0
- data/spec/inline_def_spec.rb +27 -0
- data/spec/persist_spec.rb +2 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/table_spec.rb +92 -0
- data/web/mongoid.yml +8 -1
- metadata +77 -97
@@ -0,0 +1,48 @@
|
|
1
|
+
module Extract
|
2
|
+
module Export
|
3
|
+
class Table
|
4
|
+
include FromHash
|
5
|
+
attr_accessor :name
|
6
|
+
fattr(:rows) { [] }
|
7
|
+
|
8
|
+
def cols
|
9
|
+
rows.map { |x| x.keys }.flatten.uniq
|
10
|
+
end
|
11
|
+
|
12
|
+
def quoted_col(col)
|
13
|
+
return col
|
14
|
+
if col.to_s[0..0] =~ /\d/
|
15
|
+
"\"#{col}\""
|
16
|
+
else
|
17
|
+
col
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_table_sql
|
22
|
+
col_str = cols.map { |x| " #{quoted_col(x)} varchar(255)" }.join(",\n")
|
23
|
+
"CREATE TABLE #{name} (
|
24
|
+
#{col_str}
|
25
|
+
);"
|
26
|
+
end
|
27
|
+
|
28
|
+
def inserts
|
29
|
+
res = []
|
30
|
+
rows.each do |row|
|
31
|
+
col_str = row.keys.map { |x| quoted_col(x) }.join(",")
|
32
|
+
val_str = row.values.map { |x| "'#{x}'" }.join(',')
|
33
|
+
str = "INSERT INTO #{name} (#{col_str}) VALUES (#{val_str});"
|
34
|
+
res << str
|
35
|
+
end
|
36
|
+
res
|
37
|
+
end
|
38
|
+
|
39
|
+
def sql_statements
|
40
|
+
[create_table_sql,inserts].flatten
|
41
|
+
end
|
42
|
+
|
43
|
+
def sql
|
44
|
+
sql_statements.join("\n")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/extract/formula.treetop
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
module Extract
|
2
|
+
class InlineDef
|
3
|
+
include FromHash
|
4
|
+
attr_accessor :raw
|
5
|
+
def parse_raw_cell(cell)
|
6
|
+
if cell == '_'
|
7
|
+
nil
|
8
|
+
elsif cell =~ /^=/
|
9
|
+
cell
|
10
|
+
elsif cell =~ /[a-z]/i
|
11
|
+
cell
|
12
|
+
elsif cell =~ /[0-9]/i
|
13
|
+
cell.to_f
|
14
|
+
else
|
15
|
+
raise "dunno"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
fattr(:rows) do
|
19
|
+
raw.strip.split("\n").map do |str|
|
20
|
+
str.strip.split(" ").map do |cell|
|
21
|
+
parse_raw_cell(cell)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
fattr(:sheet) do
|
27
|
+
res = Sheet.new
|
28
|
+
letters = ("A".."Z").to_a
|
29
|
+
rows.each_with_index do |row,row_i|
|
30
|
+
row.each_with_index do |val,col_i|
|
31
|
+
cell = letters[col_i] + (row_i+1).to_s
|
32
|
+
res[cell] = val if val
|
33
|
+
end
|
34
|
+
end
|
35
|
+
res
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/extract/sheet.rb
CHANGED
@@ -17,6 +17,9 @@ module Extract
|
|
17
17
|
res
|
18
18
|
end
|
19
19
|
end
|
20
|
+
def raw_value(c)
|
21
|
+
cells[c]
|
22
|
+
end
|
20
23
|
|
21
24
|
def clear_cache!
|
22
25
|
self.cache = {}
|
@@ -58,18 +61,19 @@ module Extract
|
|
58
61
|
|
59
62
|
|
60
63
|
class << self
|
61
|
-
def load(file)
|
64
|
+
def load(file,sheet_name=nil)
|
62
65
|
w = Roo::Excelx.new(file)
|
63
|
-
w.default_sheet = w.sheets.first
|
66
|
+
w.default_sheet = sheet_name || w.sheets.first
|
64
67
|
|
65
68
|
sheet = Extract::Sheet.new
|
66
69
|
|
67
70
|
("A".."Z").each do |col|
|
68
71
|
(1..100).each do |row|
|
69
|
-
|
72
|
+
cell_text = w.cell(row,col)
|
73
|
+
val = if cell_text.present? && w.formula?(row,col)
|
70
74
|
"=" + w.formula(row,col).gsub(" ","")
|
71
75
|
else
|
72
|
-
|
76
|
+
cell_text
|
73
77
|
end
|
74
78
|
loaded = w.cell(row,col)
|
75
79
|
sheet["#{col}#{row}"] = val if val.present?
|
@@ -79,6 +83,10 @@ module Extract
|
|
79
83
|
|
80
84
|
sheet
|
81
85
|
end
|
86
|
+
|
87
|
+
def inline(str)
|
88
|
+
InlineDef.new(:raw => str).sheet
|
89
|
+
end
|
82
90
|
end
|
83
91
|
|
84
92
|
end
|
@@ -3,6 +3,8 @@ module Extract
|
|
3
3
|
include FromHash
|
4
4
|
attr_accessor :sheet
|
5
5
|
|
6
|
+
fattr(:tables) { Tables.new(:sheet_def => self) }
|
7
|
+
|
6
8
|
def prev_letter(letter)
|
7
9
|
r = ("A".."Z").to_a
|
8
10
|
raise "bad letter #{letter}" unless r.index(letter)
|
@@ -19,19 +21,19 @@ module Extract
|
|
19
21
|
res = {}
|
20
22
|
(input_cells + output_cells).each do |c|
|
21
23
|
n = left(c)
|
22
|
-
res[c] = sheet[n]
|
24
|
+
res[c] = sheet[n] if !sheet[n].kind_of?(Numeric) && sheet[n].to_s.strip != '' && sheet[n].to_s.strip != 'N/A'
|
25
|
+
end
|
26
|
+
|
27
|
+
each_other_basic do |c|
|
28
|
+
n = left(c)
|
29
|
+
res[c] = sheet[n] if !sheet[n].kind_of?(Numeric) && sheet[n].to_s.strip != '' && sheet[n].to_s.strip != 'N/A'
|
23
30
|
end
|
31
|
+
|
24
32
|
res
|
25
33
|
end
|
26
34
|
fattr(:output_cells) { [] }
|
27
35
|
def output_cells=(arr)
|
28
|
-
@output_cells = arr.
|
29
|
-
if c =~ /:/
|
30
|
-
Extract::Tree::Range.cells_in_range(c)
|
31
|
-
else
|
32
|
-
c
|
33
|
-
end
|
34
|
-
end.flatten
|
36
|
+
@output_cells = Extract.expand_cells(arr).uniq
|
35
37
|
end
|
36
38
|
|
37
39
|
fattr(:dep_map) do
|
@@ -48,6 +50,20 @@ module Extract
|
|
48
50
|
res
|
49
51
|
end
|
50
52
|
|
53
|
+
def deps(cell,ops={})
|
54
|
+
raw = sheet.deps(cell)
|
55
|
+
if ops[:table]
|
56
|
+
res = []
|
57
|
+
raw.each do |c|
|
58
|
+
t = tables.for_cell(c)
|
59
|
+
res << (t || c)
|
60
|
+
end
|
61
|
+
res.uniq.sort
|
62
|
+
else
|
63
|
+
raw
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
51
67
|
fattr(:input_cells) do
|
52
68
|
output_cells.map do |c|
|
53
69
|
a = dep_map[c] || []
|
@@ -86,6 +102,9 @@ module Extract
|
|
86
102
|
def [](c)
|
87
103
|
sheet[c]
|
88
104
|
end
|
105
|
+
def raw_value(c)
|
106
|
+
sheet.cells[c]
|
107
|
+
end
|
89
108
|
|
90
109
|
def each_input
|
91
110
|
input_cells.each do |cell|
|
@@ -99,7 +118,7 @@ module Extract
|
|
99
118
|
end
|
100
119
|
end
|
101
120
|
|
102
|
-
def
|
121
|
+
def each_other_basic
|
103
122
|
res = []
|
104
123
|
bad = input_cells + output_cells
|
105
124
|
sheet.cells.each do |k,v|
|
@@ -110,15 +129,21 @@ module Extract
|
|
110
129
|
|
111
130
|
res.each do |c|
|
112
131
|
d = sheet.deps(c)
|
113
|
-
yield c
|
132
|
+
yield c if sheet.cells[c].present? && d.size > 0
|
114
133
|
end
|
134
|
+
end
|
115
135
|
|
136
|
+
def each_other
|
137
|
+
each_other_basic do |c|
|
138
|
+
d = sheet.deps(c)
|
139
|
+
yield c,cell_names[c],sheet[c],sheet.cells[c],d
|
140
|
+
end
|
116
141
|
end
|
117
142
|
|
118
143
|
class << self
|
119
|
-
def load(file,output)
|
144
|
+
def load(file,output,sheet_name=nil)
|
120
145
|
res = new
|
121
|
-
res.sheet = Sheet.load(file)
|
146
|
+
res.sheet = Sheet.load(file,sheet_name)
|
122
147
|
res.output_cells = output
|
123
148
|
res
|
124
149
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Extract
|
2
|
+
class Row
|
3
|
+
include FromHash
|
4
|
+
include Enumerable
|
5
|
+
attr_accessor :table, :cells
|
6
|
+
|
7
|
+
fattr(:value_hash) do
|
8
|
+
res = {}
|
9
|
+
raise "bad match" unless table.field_names.size == cells.size
|
10
|
+
table.field_names.each_with_index do |name,i|
|
11
|
+
cell = cells[i]
|
12
|
+
res[name] = cell.value
|
13
|
+
end
|
14
|
+
res
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](k)
|
18
|
+
value_hash[k]
|
19
|
+
end
|
20
|
+
|
21
|
+
def each(&b)
|
22
|
+
value_hash.each(&b)
|
23
|
+
end
|
24
|
+
|
25
|
+
def present?
|
26
|
+
value_hash.values.any? { |x| x.present? }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
class Table
|
30
|
+
include FromHash
|
31
|
+
attr_accessor :cell_range, :name, :sheet_def
|
32
|
+
|
33
|
+
def cells
|
34
|
+
Extract.expand_cells(cell_range)
|
35
|
+
end
|
36
|
+
|
37
|
+
def cell_objs
|
38
|
+
cells.map { |c| Cell.new(:sheet_def => sheet_def, :cell => c) }
|
39
|
+
end
|
40
|
+
def cell_row_hash
|
41
|
+
res = Hash.new { |h,k| h[k] = [] }
|
42
|
+
cell_objs.each do |c|
|
43
|
+
res[c.row] << c
|
44
|
+
end
|
45
|
+
res
|
46
|
+
end
|
47
|
+
|
48
|
+
def field_names
|
49
|
+
k = cell_row_hash.keys.min
|
50
|
+
cell_row_hash[k].map { |x| x.value }
|
51
|
+
end
|
52
|
+
|
53
|
+
def rows
|
54
|
+
cell_row_hash.values[1..-1].map { |a| Row.new(:table => self, :cells => a) }.select { |x| x.present? }
|
55
|
+
end
|
56
|
+
|
57
|
+
def sql_statements
|
58
|
+
res = Extract::Export::Table.new(:name => name)
|
59
|
+
rows.each do |row|
|
60
|
+
res.rows << row.value_hash
|
61
|
+
end
|
62
|
+
res.sql_statements
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Extract
|
2
|
+
class Tables
|
3
|
+
include FromHash
|
4
|
+
attr_accessor :sheet_def
|
5
|
+
fattr(:tables) { {} }
|
6
|
+
|
7
|
+
def add(name,range)
|
8
|
+
self.tables[name] = Table.new(:cell_range => range, :name => name, :sheet_def => sheet_def)
|
9
|
+
end
|
10
|
+
|
11
|
+
fattr(:cell_map) do
|
12
|
+
res = {}
|
13
|
+
tables.each do |name,table|
|
14
|
+
table.cells.each do |c|
|
15
|
+
res[c] = name
|
16
|
+
end
|
17
|
+
end
|
18
|
+
res
|
19
|
+
end
|
20
|
+
|
21
|
+
def for_cell(c)
|
22
|
+
cell_map[c]
|
23
|
+
end
|
24
|
+
|
25
|
+
def [](c)
|
26
|
+
if c.to_s == 'all'
|
27
|
+
Table.new(:cell_range => "A1:D100", :name => "all", :sheet_def => sheet_def)
|
28
|
+
else
|
29
|
+
tables[c]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
def each(&b)
|
33
|
+
tables.each(&b)
|
34
|
+
end
|
35
|
+
def values
|
36
|
+
tables.values
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
Binary file
|
data/spec/cell_spec.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe 'Cell' 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
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:cell) do
|
18
|
+
Extract::Cell.new(:sheet_def => sheet, :cell => "C1")
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'value' do
|
22
|
+
cell.value.should == 1
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'row' do
|
26
|
+
cell.row.should == 1
|
27
|
+
end
|
28
|
+
it 'col' do
|
29
|
+
cell.col.should == "C"
|
30
|
+
end
|
31
|
+
end
|
data/spec/config/mongoid.yml
CHANGED
@@ -3,4 +3,11 @@ development:
|
|
3
3
|
default:
|
4
4
|
database: mongoid_dev
|
5
5
|
hosts:
|
6
|
-
- localhost:27017
|
6
|
+
- localhost:27017
|
7
|
+
test:
|
8
|
+
sessions:
|
9
|
+
default:
|
10
|
+
uri: mongodb://extract_test:extract_test@linus.mongohq.com:10038/extract_test
|
11
|
+
options:
|
12
|
+
skip_version_check: true
|
13
|
+
safe: true
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
class String
|
4
|
+
def ssf
|
5
|
+
gsub("\n"," ").gsub(/[ ]{2,99}/," ")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'Export Table' do
|
10
|
+
describe "basic" do
|
11
|
+
let(:table) do
|
12
|
+
res = Extract::Export::Table.new(:name => "widgets")
|
13
|
+
res.rows << {:color => "Green", :price => 20}
|
14
|
+
res
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'create table sql' do
|
18
|
+
exp = "CREATE TABLE widgets (
|
19
|
+
color varchar(255),
|
20
|
+
price varchar(255)
|
21
|
+
);"
|
22
|
+
table.create_table_sql.ssf.should == exp.ssf
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'inserts' do
|
26
|
+
table.inserts.size.should == 1
|
27
|
+
table.inserts[0].ssf.should == "INSERT INTO widgets (color,price) VALUES ('Green','20');"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "needs quotes" do
|
32
|
+
let(:table) do
|
33
|
+
res = Extract::Export::Table.new(:name => "widgets")
|
34
|
+
res.rows << {"2B" => 14}
|
35
|
+
res
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'create table' do
|
39
|
+
exp = 'CREATE TABLE widgets (
|
40
|
+
"2B" varchar(255)
|
41
|
+
);'
|
42
|
+
table.create_table_sql.ssf.should == exp.ssf
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|