roo 1.0.2 → 1.1.0

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.
@@ -9,17 +9,20 @@ class GenericSpreadsheet
9
9
  # sets the line with attribute names (default: 1)
10
10
  attr_accessor :header_line
11
11
 
12
+ def initialize
13
+ end
14
+
12
15
  # set the working sheet in the document
13
16
  def default_sheet=(sheet)
14
- if sheet.kind_of? Fixnum
17
+ if sheet.kind_of? Fixnum
15
18
  if sheet >= 0 and sheet <= sheets.length
16
- sheet = self.sheets[sheet-1]
19
+ sheet = self.sheets[sheet-1]
17
20
  else
18
21
  raise RangeError
19
22
  end
20
23
  elsif sheet.kind_of?(String)
21
24
  raise RangeError if ! self.sheets.include?(sheet)
22
- else
25
+ else
23
26
  raise TypeError, "what are you trying to set as default sheet?"
24
27
  end
25
28
  @default_sheet = sheet
@@ -27,121 +30,6 @@ class GenericSpreadsheet
27
30
  @first_row[sheet] = @last_row[sheet] = @first_column[sheet] = @last_column[sheet] = nil
28
31
  @cells_read[sheet] = false
29
32
  end
30
-
31
- # converts cell coordinate to numeric values of row,col
32
- def normalize(row,col)
33
- if row.class == String
34
- if col.class == Fixnum
35
- # ('A',1):
36
- # ('B', 5) -> (5, 2)
37
- row, col = col, row
38
- else
39
- raise ArgumentError
40
- end
41
- end
42
- if col.class == String
43
- col = GenericSpreadsheet.letter_to_number(col)
44
- end
45
- return row,col
46
- end
47
-
48
- # true if cell is empty
49
- def empty?(row, col, sheet=nil)
50
- #def excel_empty?(row, col, sheet=nil)
51
- sheet = @default_sheet unless sheet
52
- read_cells(sheet) unless @cells_read[sheet] or self.class == Excel
53
- row,col = normalize(row,col)
54
- return true unless cell(row, col, sheet)
55
- return true if celltype(row, col, sheet) == :string && cell(row, col, sheet).empty?
56
- #false
57
- #end
58
-
59
- # true if a cell is empty
60
- #def oo_empty?(row, col, sheet=nil)
61
- #sheet = @default_sheet unless sheet
62
- #read_cells(sheet) unless @cells_read[sheet]
63
- #row,col = normalize(row,col)
64
- return true if row < first_row(sheet) || row > last_row(sheet) || col < first_column(sheet) || col > last_column(sheet)
65
- #return true unless cell(row, col, sheet)
66
- # return true if celltype(row, col, sheet) == "string" && cell(row, col, sheet) == ""
67
- #return true if celltype(row, col, sheet) == :string && cell(row, col, sheet) == ""
68
- false
69
- end
70
-
71
-
72
- # reopens and read a spreadsheet document
73
- def reload
74
- ds = @default_sheet
75
- initialize(@filename) if self.class == Openoffice or
76
- self.class == Excel
77
- initialize(@spreadsheetkey,@user,@password) if self.class == Google
78
- self.default_sheet = ds
79
- #@first_row = @last_row = @first_column = @last_column = nil
80
- end
81
-
82
- # Returns information of the spreadsheet document and all sheets within
83
- # this document.
84
- def info
85
- # $log.debug(self.class.to_s+"#info started")
86
- result = "File: #{File.basename(@filename)}\n"+
87
- "Number of sheets: #{sheets.size}\n"+
88
- "Sheets: #{sheets.map{|sheet| sheet+", "}.to_s[0..-3]}\n"
89
- n = 1
90
- # $log.debug(sheets.inspect)
91
- sheets.each {|sheet|
92
- # $log.debug("Info fuer Sheet=#{sheet}")
93
- self.default_sheet = sheet
94
- # $log.debug("nach default_sheet=")
95
- result << "Sheet " + n.to_s + ":\n"
96
- unless first_row
97
- result << " - empty -"
98
- else
99
- result << " First row: #{first_row}\n"
100
- result << " Last row: #{last_row}\n"
101
- result << " First column: #{GenericSpreadsheet.number_to_letter(first_column)}\n"
102
- result << " Last column: #{GenericSpreadsheet.number_to_letter(last_column)}"
103
- end
104
- result << "\n" if sheet != sheets.last
105
- n += 1
106
- }
107
- # $log.debug(self.class.to_s+"#info ended")
108
- result
109
- end
110
-
111
- # returns a rectangular area (default: all cells) as yaml-output
112
- # you can add additional attributes with the prefix parameter like:
113
- # oo.to_yaml({"file"=>"flightdata_2007-06-26", "sheet" => "1"})
114
- def to_yaml(prefix={}, from_row=nil, from_column=nil, to_row=nil, to_column=nil,sheet=nil)
115
- sheet = @default_sheet unless sheet
116
- result = "--- \n"
117
- (from_row||first_row(sheet)).upto(to_row||last_row(sheet)) do |row|
118
- (from_column||first_column(sheet)).upto(to_column||last_column(sheet)) do |col|
119
- unless empty?(row,col,sheet)
120
- result << "cell_#{row}_#{col}: \n"
121
- prefix.each {|k,v|
122
- result << " #{k}: #{v} \n"
123
- }
124
- result << " row: #{row} \n"
125
- result << " col: #{col} \n"
126
- result << " celltype: #{self.celltype(row,col,sheet)} \n"
127
- if self.celltype(row,col,sheet) == :time
128
- result << " value: #{GenericSpreadsheet.integer_to_timestring( self.cell(row,col,sheet))} \n"
129
- else
130
- result << " value: #{self.cell(row,col,sheet)} \n"
131
- end
132
- end
133
- end
134
- end
135
- result
136
- end
137
-
138
- # recursively removes the current temporary directory
139
- # this is only needed if you work with zipped files or files via the web
140
- def remove_tmp
141
- if File.exists?(@tmpdir)
142
- FileUtils::rm_r(@tmpdir)
143
- end
144
- end
145
33
 
146
34
  # first non-empty column as a letter
147
35
  def first_column_as_letter(sheet=nil)
@@ -153,80 +41,43 @@ class GenericSpreadsheet
153
41
  GenericSpreadsheet.number_to_letter(last_column(sheet))
154
42
  end
155
43
 
156
- def open_from_uri(uri)
157
- require 'open-uri' ;
158
- tempfilename = File.join(@tmpdir, File.basename(uri))
159
- f = File.open(tempfilename,"wb")
160
- begin
161
- open(uri) do |net|
162
- f.write(net.read)
163
- end
164
- rescue
165
- raise "could not open #{uri}"
44
+ # returns the number of the first non-empty row
45
+ def first_row(sheet=nil)
46
+ if sheet == nil
47
+ sheet = @default_sheet
166
48
  end
167
- f.close
168
- File.join(@tmpdir, File.basename(uri))
169
- end
170
-
171
- # returns the number of the last non-empty row
172
- def last_row(sheet=nil)
173
- sheet = @default_sheet unless sheet
174
49
  read_cells(sheet) unless @cells_read[sheet]
175
- if @last_row[sheet]
176
- return @last_row[sheet]
50
+ if @first_row[sheet]
51
+ return @first_row[sheet]
177
52
  end
178
- impossible_value = 0
53
+ impossible_value = 999_999 # more than a spreadsheet can hold
179
54
  result = impossible_value
180
55
  @cell[sheet].each_pair {|key,value|
181
56
  y,x = key.split(',')
182
57
  y = y.to_i
183
- result = [result, y].max if value
58
+ result = [result, y].min if value
184
59
  } if @cell[sheet]
185
60
  result = nil if result == impossible_value
186
- @last_row[sheet] = result
61
+ @first_row[sheet] = result
187
62
  result
188
63
  end
189
64
 
190
- # returns the number of the last non-empty column
191
- def last_column(sheet=nil)
192
- # $log.debug("#{self.class.to_s}#last_column(#{sheet})")
65
+ # returns the number of the last non-empty row
66
+ def last_row(sheet=nil)
193
67
  sheet = @default_sheet unless sheet
194
68
  read_cells(sheet) unless @cells_read[sheet]
195
- if @last_column[sheet]
196
- # $log.debug("last_column of sheet #{sheet} already set")
197
- return @last_column[sheet]
69
+ if @last_row[sheet]
70
+ return @last_row[sheet]
198
71
  end
199
- # $log.debug("last_column of sheet #{sheet} not yet set")
200
72
  impossible_value = 0
201
73
  result = impossible_value
202
74
  @cell[sheet].each_pair {|key,value|
203
- y,x = key.split(',')
204
- x = x.to_i
205
- result = [result, x].max if value
206
- } if @cell[sheet]
207
- result = nil if result == impossible_value
208
- @last_column[sheet] = result
209
- result
210
- end
211
-
212
- # returns the number of the first non-empty row
213
- def first_row(sheet=nil)
214
- if sheet == nil
215
- sheet = @default_sheet
216
- end
217
- read_cells(sheet) unless @cells_read[sheet]
218
- if @first_row[sheet]
219
- return @first_row[sheet]
220
- end
221
- impossible_value = 999_999 # more than a spreadsheet can hold
222
- result = impossible_value
223
- @cell[sheet].each_pair {|key,value|
224
75
  y,x = key.split(',')
225
76
  y = y.to_i
226
- result = [result, y].min if value
77
+ result = [result, y].max if value
227
78
  } if @cell[sheet]
228
79
  result = nil if result == impossible_value
229
- @first_row[sheet] = result
80
+ @last_row[sheet] = result
230
81
  result
231
82
  end
232
83
 
@@ -251,27 +102,51 @@ class GenericSpreadsheet
251
102
  result
252
103
  end
253
104
 
254
- # convert a number to something like this: 'AB'
255
- def GenericSpreadsheet.number_to_letter(n)
256
- letters=""
257
- while n > 0
258
- num = n%26
259
- letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[num-1,1] + letters
260
- n = n.div(26)
105
+ # returns the number of the last non-empty column
106
+ def last_column(sheet=nil)
107
+ # $log.debug("#{self.class.to_s}#last_column(#{sheet})")
108
+ sheet = @default_sheet unless sheet
109
+ read_cells(sheet) unless @cells_read[sheet]
110
+ if @last_column[sheet]
111
+ # $log.debug("last_column of sheet #{sheet} already set")
112
+ return @last_column[sheet]
261
113
  end
262
- letters
114
+ # $log.debug("last_column of sheet #{sheet} not yet set")
115
+ impossible_value = 0
116
+ result = impossible_value
117
+ @cell[sheet].each_pair {|key,value|
118
+ y,x = key.split(',')
119
+ x = x.to_i
120
+ result = [result, x].max if value
121
+ } if @cell[sheet]
122
+ result = nil if result == impossible_value
123
+ @last_column[sheet] = result
124
+ result
263
125
  end
264
126
 
265
- # convert letters like 'AB' to a number
266
- def self.letter_to_number(letters)
267
- result = 0
268
- while letters && letters.length > 0
269
- character = letters[0,1].upcase
270
- num = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".index(character)
271
- raise ArgumentError, "invalid column character '#{letters[0,1]}'" if num == nil
272
- num += 1
273
- result = result * 26 + num
274
- letters = letters[1..-1]
127
+ # returns a rectangular area (default: all cells) as yaml-output
128
+ # you can add additional attributes with the prefix parameter like:
129
+ # oo.to_yaml({"file"=>"flightdata_2007-06-26", "sheet" => "1"})
130
+ def to_yaml(prefix={}, from_row=nil, from_column=nil, to_row=nil, to_column=nil,sheet=nil)
131
+ sheet = @default_sheet unless sheet
132
+ result = "--- \n"
133
+ (from_row||first_row(sheet)).upto(to_row||last_row(sheet)) do |row|
134
+ (from_column||first_column(sheet)).upto(to_column||last_column(sheet)) do |col|
135
+ unless empty?(row,col,sheet)
136
+ result << "cell_#{row}_#{col}: \n"
137
+ prefix.each {|k,v|
138
+ result << " #{k}: #{v} \n"
139
+ }
140
+ result << " row: #{row} \n"
141
+ result << " col: #{col} \n"
142
+ result << " celltype: #{self.celltype(row,col,sheet)} \n"
143
+ if self.celltype(row,col,sheet) == :time
144
+ result << " value: #{GenericSpreadsheet.integer_to_timestring( self.cell(row,col,sheet))} \n"
145
+ else
146
+ result << " value: #{self.cell(row,col,sheet)} \n"
147
+ end
148
+ end
149
+ end
275
150
  end
276
151
  result
277
152
  end
@@ -333,7 +208,7 @@ class GenericSpreadsheet
333
208
  if @header_line
334
209
  result = [ tmp ]
335
210
  else
336
- result = tmp
211
+ result = tmp
337
212
  end
338
213
  #-- :all
339
214
  elsif args[0] == :all
@@ -381,46 +256,224 @@ class GenericSpreadsheet
381
256
  result
382
257
  end
383
258
 
259
+ # returns all values in this row as an array
260
+ # row numbers are 1,2,3,... like in the spreadsheet
261
+ def row(rownumber,sheet=nil)
262
+ sheet = @default_sheet unless sheet
263
+ read_cells(sheet) unless @cells_read[sheet]
264
+ result = []
265
+ tmp_arr = []
266
+ @cell[sheet].each_pair {|key,value|
267
+ y,x = key.split(',')
268
+ x = x.to_i
269
+ y = y.to_i
270
+ if y == rownumber
271
+ tmp_arr[x] = value
272
+ end
273
+ }
274
+ result = tmp_arr[1..-1]
275
+ while result && result[-1] == nil
276
+ result = result[0..-2]
277
+ end
278
+ result
279
+ end
280
+
281
+ # returns all values in this column as an array
282
+ # column numbers are 1,2,3,... like in the spreadsheet
283
+ def column(columnnumber,sheet=nil)
284
+ if columnnumber.class == String
285
+ columnnumber = Excel.letter_to_number(columnnumber)
286
+ end
287
+ sheet = @default_sheet unless sheet
288
+ read_cells(sheet) unless @cells_read[sheet]
289
+ result = []
290
+ first_row(sheet).upto(last_row(sheet)) do |row|
291
+ result << cell(row,columnnumber,sheet)
292
+ end
293
+ result
294
+ end
295
+
296
+ # reopens and read a spreadsheet document
297
+ def reload
298
+ ds = @default_sheet
299
+ initialize(@filename) if self.class == Openoffice or
300
+ self.class == Excel
301
+ initialize(@spreadsheetkey,@user,@password) if self.class == Google
302
+ self.default_sheet = ds
303
+ #@first_row = @last_row = @first_column = @last_column = nil
304
+ end
305
+
306
+ # true if cell is empty
307
+ def empty?(row, col, sheet=nil)
308
+ sheet = @default_sheet unless sheet
309
+ read_cells(sheet) unless @cells_read[sheet] or self.class == Excel
310
+ row,col = normalize(row,col)
311
+ return true unless cell(row, col, sheet)
312
+ return true if celltype(row, col, sheet) == :string && cell(row, col, sheet).empty?
313
+ return true if row < first_row(sheet) || row > last_row(sheet) || col < first_column(sheet) || col > last_column(sheet)
314
+ false
315
+ end
316
+
317
+ # recursively removes the current temporary directory
318
+ # this is only needed if you work with zipped files or files via the web
319
+ def remove_tmp
320
+ if File.exists?(@tmpdir)
321
+ FileUtils::rm_r(@tmpdir)
322
+ end
323
+ end
324
+
325
+ # Returns information of the spreadsheet document and all sheets within
326
+ # this document.
327
+ def info
328
+ # $log.debug(self.class.to_s+"#info started")
329
+ result = "File: #{File.basename(@filename)}\n"+
330
+ "Number of sheets: #{sheets.size}\n"+
331
+ "Sheets: #{sheets.map{|sheet| sheet+", "}.to_s[0..-3]}\n"
332
+ n = 1
333
+ # $log.debug(sheets.inspect)
334
+ sheets.each {|sheet|
335
+ # $log.debug("Info fuer Sheet=#{sheet}")
336
+ self.default_sheet = sheet
337
+ # $log.debug("nach default_sheet=")
338
+ result << "Sheet " + n.to_s + ":\n"
339
+ unless first_row
340
+ result << " - empty -"
341
+ else
342
+ result << " First row: #{first_row}\n"
343
+ result << " Last row: #{last_row}\n"
344
+ result << " First column: #{GenericSpreadsheet.number_to_letter(first_column)}\n"
345
+ result << " Last column: #{GenericSpreadsheet.number_to_letter(last_column)}"
346
+ end
347
+ result << "\n" if sheet != sheets.last
348
+ n += 1
349
+ }
350
+ # $log.debug(self.class.to_s+"#info ended")
351
+ result
352
+ end
353
+
384
354
  def to_xml
385
355
  xml_document = ''
386
356
  xml = Builder::XmlMarkup.new(:target => xml_document, :indent => 2)
387
357
  xml.instruct! :xml, :version =>"1.0", :encoding => "utf-8"
388
-
389
- xml.spreadsheet {
390
- self.sheets.each do |sheet|
391
- self.default_sheet = sheet
392
- xml.sheet(:name => sheet) { |x|
393
- first_row.upto(last_row) do |row|
394
- first_column.upto(last_column) do |col|
395
- unless empty?(row,col)
396
- x.cell(cell(row,col),
397
- :row =>row,
398
- :column => col,
399
- :type => celltype(row,col))
358
+ xml.spreadsheet {
359
+ self.sheets.each do |sheet|
360
+ self.default_sheet = sheet
361
+ xml.sheet(:name => sheet) { |x|
362
+ if first_row and last_row and first_column and last_column
363
+ # sonst gibt es Fehler bei leeren Blaettern
364
+ first_row.upto(last_row) do |row|
365
+ first_column.upto(last_column) do |col|
366
+ unless empty?(row,col)
367
+ x.cell(cell(row,col),
368
+ :row =>row,
369
+ :column => col,
370
+ :type => celltype(row,col))
371
+ end
372
+ end
400
373
  end
401
374
  end
402
- end
403
- }
404
- end
405
- }
406
-
375
+ }
376
+ end
377
+ }
407
378
  xml_document
408
379
  end
409
-
380
+
410
381
  protected
411
382
 
412
- def unzip(filename)
413
- ret = nil
414
- Zip::ZipFile.open(filename) do |zip|
415
- ret = process_zipfile_packed zip
383
+ def file_type_check(filename, ext, name)
384
+ new_expression = {
385
+ '.ods' => 'Openoffice.new',
386
+ '.xls' => 'Excel.new',
387
+ '.xlsx' => 'Excelx.new',
388
+ }
389
+ case ext
390
+ when '.ods', '.xls', '.xlsx'
391
+ correct_class = "use #{new_expression[ext]} to handle #{ext} spreadsheet files"
392
+ else
393
+ raise "unknown file type: #{ext}"
394
+ end
395
+ if File.extname(filename).downcase != ext
396
+ case @file_warning
397
+ when :error
398
+ warn correct_class
399
+ raise TypeError, "#{filename} is not #{name} file"
400
+ when :warning
401
+ warn "are you sure, this is #{name} spreadsheet file?"
402
+ warn correct_class
403
+ when :ignore
404
+ # ignore
405
+ else
406
+ raise "#{@file_warning} illegal state of file_warning"
407
+ end
416
408
  end
417
- ret
418
409
  end
419
410
 
411
+
420
412
  private
421
413
 
422
- def initialize
414
+ # converts cell coordinate to numeric values of row,col
415
+ def normalize(row,col)
416
+ if row.class == String
417
+ if col.class == Fixnum
418
+ # ('A',1):
419
+ # ('B', 5) -> (5, 2)
420
+ row, col = col, row
421
+ else
422
+ raise ArgumentError
423
+ end
424
+ end
425
+ if col.class == String
426
+ col = GenericSpreadsheet.letter_to_number(col)
427
+ end
428
+ return row,col
429
+ end
423
430
 
431
+ def open_from_uri(uri)
432
+ require 'open-uri' ;
433
+ tempfilename = File.join(@tmpdir, File.basename(uri))
434
+ f = File.open(tempfilename,"wb")
435
+ begin
436
+ open(uri) do |net|
437
+ f.write(net.read)
438
+ end
439
+ rescue
440
+ raise "could not open #{uri}"
441
+ end
442
+ f.close
443
+ File.join(@tmpdir, File.basename(uri))
444
+ end
445
+
446
+ # convert a number to something like this: 'AB'
447
+ def GenericSpreadsheet.number_to_letter(n)
448
+ letters=""
449
+ while n > 0
450
+ num = n%26
451
+ letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[num-1,1] + letters
452
+ n = n.div(26)
453
+ end
454
+ letters
455
+ end
456
+
457
+ # convert letters like 'AB' to a number
458
+ def self.letter_to_number(letters)
459
+ result = 0
460
+ while letters && letters.length > 0
461
+ character = letters[0,1].upcase
462
+ num = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".index(character)
463
+ raise ArgumentError, "invalid column character '#{letters[0,1]}'" if num == nil
464
+ num += 1
465
+ result = result * 26 + num
466
+ letters = letters[1..-1]
467
+ end
468
+ result
469
+ end
470
+
471
+ def unzip(filename)
472
+ ret = nil
473
+ Zip::ZipFile.open(filename) do |zip|
474
+ ret = process_zipfile_packed zip
475
+ end
476
+ ret
424
477
  end
425
478
 
426
479
  # check if default_sheet was set and exists in sheets-array
@@ -460,7 +513,6 @@ class GenericSpreadsheet
460
513
  end
461
514
 
462
515
  def write_csv_content(file=nil,sheet=nil)
463
- # sheet = @default_sheet unless sheet
464
516
  file = STDOUT unless file
465
517
  if first_row(sheet) # sheet is not empty
466
518
  first_row(sheet).upto(last_row(sheet)) do |row|
@@ -512,20 +564,16 @@ class GenericSpreadsheet
512
564
  raise "unhandled onecell-class "+onecell.class.to_s
513
565
  end
514
566
  when :date
515
- # str << '"'+onecell.to_s+'"'
516
567
  str << onecell.to_s
517
- # str << onecell.strftime("%d.%m.%y")
518
568
  when :time
519
569
  str << GenericSpreadsheet.integer_to_timestring(onecell)
520
570
  else
521
571
  raise "unhandled celltype "+onecelltype.to_s
522
572
  end
523
573
  end
524
- #cells << onecell
525
574
  str
526
575
  end
527
576
 
528
- private
529
577
  def GenericSpreadsheet.integer_to_timestring(content)
530
578
  h = (content/3600.0).floor
531
579
  content = content - h*3600