spreadsheet_goodies 0.0.2
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/.gitignore +13 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE +24 -0
- data/LICENSE.txt +24 -0
- data/README.md +57 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/builds/spreadsheet_goodies-0.0.1.gem +0 -0
- data/lib/spreadsheet_goodies.rb +34 -0
- data/lib/spreadsheet_goodies/excel_workbook_builder.rb +78 -0
- data/lib/spreadsheet_goodies/excel_worksheet_proxy.rb +68 -0
- data/lib/spreadsheet_goodies/excel_worksheet_proxy_row.rb +30 -0
- data/lib/spreadsheet_goodies/google_drive_connector.rb +93 -0
- data/lib/spreadsheet_goodies/google_worksheet_proxy.rb +61 -0
- data/lib/spreadsheet_goodies/google_worksheet_proxy_row.rb +23 -0
- data/lib/spreadsheet_goodies/version.rb +3 -0
- data/spreadsheet_goodies.gemspec +51 -0
- metadata +168 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f8a77ef379add514b246cd65ddde8c2263a53b4d
|
4
|
+
data.tar.gz: f0a5cc31a25417c65c3d76b5f64128d561f1ca20
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4c8fba74cbcae86da1613a7b6b502e6115784ac6077e44850c18269102639d6152b6245653ab18ff290eba5022b2bd8fbb8ada46ee757370219d36ec2259d262
|
7
|
+
data.tar.gz: d6442b9b72b622f0cadcb51ee67235a3208b8b1910769bd06e277c4692261e5f9eb00423116f948eae229adcc4408964a4375248cc27ebde810b314d6ee17312
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
spreadsheet_goodies
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.3.1
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
4
|
+
distribute this software, either in source code form or as a compiled
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
6
|
+
means.
|
7
|
+
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
9
|
+
of this software dedicate any and all copyright interest in the
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
11
|
+
of the public at large and to the detriment of our heirs and
|
12
|
+
successors. We intend this dedication to be an overt act of
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
14
|
+
software under copyright law.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
24
|
+
For more information, please refer to <http://unlicense.org>
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
4
|
+
distribute this software, either in source code form or as a compiled
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
6
|
+
means.
|
7
|
+
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
9
|
+
of this software dedicate any and all copyright interest in the
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
11
|
+
of the public at large and to the detriment of our heirs and
|
12
|
+
successors. We intend this dedication to be an overt act of
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
14
|
+
software under copyright law.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
24
|
+
For more information, please refer to <http://unlicense.org>
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# SpreadsheetGoodies
|
2
|
+
|
3
|
+
SpreadsheetGoodies is a collection of tools to help work with spreadsheets in
|
4
|
+
Excel and Google Spreadhseets formats. It relies heavily on other gems to make
|
5
|
+
the actual work of reading and writing to spreadsheet documents. It main
|
6
|
+
features are:
|
7
|
+
|
8
|
+
* Read a spreadseet as an array of arrays to allow aceessing its data without
|
9
|
+
using the original document
|
10
|
+
* Access a row's elements using the column titles as keys"
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
gem 'spreadsheet_goodies'
|
18
|
+
```
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
|
26
|
+
$ gem install spreadsheet_goodies
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
Read a Google Spreadsheet:
|
31
|
+
```
|
32
|
+
spreadsheet_key = '1UC43X6aZwlWPCnn...'
|
33
|
+
sheet = SpreadsheetGoodies::GoogleWorksheetProxy.new(spreadsheet_key, 'Relação Lojas').read_from_google_drive
|
34
|
+
```
|
35
|
+
|
36
|
+
Read an Excel workbook:
|
37
|
+
```
|
38
|
+
sheet = SpreadsheetGoodies::ExcelWorksheetProxy.new('~/workbook.xlsx')
|
39
|
+
```
|
40
|
+
|
41
|
+
Iterate over every data row (i.e., all but the header row) and print the value
|
42
|
+
of a column titled 'Total':
|
43
|
+
```
|
44
|
+
sheet.data_rows.each do |row|
|
45
|
+
puts "#{row.row_number} -- #{row['Total']}"
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
## Contributing
|
50
|
+
|
51
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ricardo-jasinski/spreadsheet_goodies.
|
52
|
+
|
53
|
+
|
54
|
+
## License
|
55
|
+
|
56
|
+
The gem is available as open source under the terms of the [Unlicense](http://unlicense.org/UNLICENSE).
|
57
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "spreadsheet_goodies"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
Binary file
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spreadsheet_goodies/version'
|
2
|
+
require 'spreadsheet_goodies/google_worksheet_proxy'
|
3
|
+
require 'spreadsheet_goodies/google_drive_connector'
|
4
|
+
require 'spreadsheet_goodies/excel_worksheet_proxy'
|
5
|
+
require 'roo'
|
6
|
+
|
7
|
+
module SpreadsheetGoodies
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_accessor :configuration
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.configuration
|
14
|
+
@configuration ||= Configuration.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.reset
|
18
|
+
@configuration = Configuration.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.configure
|
22
|
+
yield(configuration)
|
23
|
+
end
|
24
|
+
|
25
|
+
class Configuration
|
26
|
+
attr_accessor :service_account_key_json, :login_method,
|
27
|
+
:google_client_id, :client_secret, :refresh_token
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@login_method = :oauth2
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'axlsx'
|
2
|
+
|
3
|
+
# A set of methods to help create and format Excel workbooks
|
4
|
+
class SpreadsheetGoodies::ExcelWorkbookBuilder
|
5
|
+
attr_reader :current_sheet
|
6
|
+
|
7
|
+
def initialize(output_file_pathname)
|
8
|
+
@output_file_pathname = output_file_pathname
|
9
|
+
@axlsx_package = Axlsx::Package.new
|
10
|
+
end
|
11
|
+
|
12
|
+
# The underlying Axlsx::Workbook object
|
13
|
+
def workbook
|
14
|
+
@axlsx_package.workbook
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_worksheet(sheet_name)
|
18
|
+
@current_sheet = @workbook.add_worksheet(name: sheet_name)
|
19
|
+
setup_current_sheet_styles
|
20
|
+
freeze_top_row
|
21
|
+
@current_sheet
|
22
|
+
end
|
23
|
+
|
24
|
+
# Add a row to the current sheet using the data row style
|
25
|
+
def add_data_row(row_values)
|
26
|
+
@current_sheet.add_row(row_values, style: @data_row_style)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Add a row to the current sheet using the header row style
|
30
|
+
def add_header_row(row_values)
|
31
|
+
@current_sheet.add_row(row_values, style: @header_row_style)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Add filter and sorting controls to a sheet.
|
35
|
+
def setup_auto_filter(sheet=nil)
|
36
|
+
worksheet = sheet || @current_sheet
|
37
|
+
top_left_cell_label = worksheet.dimension.first_cell_reference
|
38
|
+
bottom_right_cell_label = worksheet.dimension.last_cell_reference
|
39
|
+
filter_range = "#{top_left_cell_label}:#{bottom_right_cell_label}"
|
40
|
+
worksheet.auto_filter = filter_range
|
41
|
+
end
|
42
|
+
|
43
|
+
def write_to_file
|
44
|
+
@axlsx_package.serialize(@output_file_pathname)
|
45
|
+
end
|
46
|
+
|
47
|
+
def freeze_top_row
|
48
|
+
@current_sheet.sheet_view.pane do |pane|
|
49
|
+
pane.top_left_cell = 'A2'
|
50
|
+
pane.state = :frozen_split
|
51
|
+
pane.y_split = 1
|
52
|
+
pane.active_pane = :bottom_right
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def setup_current_sheet_styles
|
59
|
+
@header_row_style = @current_sheet.styles.add_style(
|
60
|
+
alignment: {horizontal: :center, vertical: :center},
|
61
|
+
font_name: 'Calibri',
|
62
|
+
bg_color: 'FFDDDDDD',
|
63
|
+
b: true,
|
64
|
+
)
|
65
|
+
|
66
|
+
@header_row_sodexo_style = @current_sheet.styles.add_style(
|
67
|
+
alignment: {horizontal: :center, vertical: :center},
|
68
|
+
font_name: 'Calibri',
|
69
|
+
bg_color: '002060',
|
70
|
+
fg_color: 'FFFFFF',
|
71
|
+
b: true,
|
72
|
+
)
|
73
|
+
|
74
|
+
@data_row_style = @current_sheet.styles.add_style(
|
75
|
+
font_name: 'Calibri',
|
76
|
+
)
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative 'excel_worksheet_proxy_row'
|
2
|
+
|
3
|
+
# A cached copy of an Excel worksheet (a single workbook "tab"), stored as an
|
4
|
+
# array of arrays. Individual cells can be accessed by column title, e.g.:
|
5
|
+
# worksheet[0]['A column title']
|
6
|
+
class SpreadsheetGoodies::ExcelWorksheetProxy
|
7
|
+
attr_reader :rows, :workbook, :worksheet
|
8
|
+
|
9
|
+
# @param workbook_file_pathname [String] Full path and filename to Excel workbook document
|
10
|
+
# @param worksheet_title_or_index [String|Integer] Sheet name or index (zero-based)
|
11
|
+
# within the workbook. Optional; if unspecified, the first sheet will be used.
|
12
|
+
# @param num_header_rows [Integer] Number of rows at the top of the sheet that
|
13
|
+
# contain headers or stuff other than data. Optional; if unspecified, assumes
|
14
|
+
# that a single top row contains the header and all rows below are data.
|
15
|
+
def initialize(workbook_file_pathname, worksheet_title_or_index=0, num_header_rows=1)
|
16
|
+
@worksheet_title = worksheet_title_or_index
|
17
|
+
@num_header_rows = num_header_rows
|
18
|
+
|
19
|
+
@workbook = case workbook_file_pathname.to_s
|
20
|
+
when /\.xls[^x]/ then Roo::Excel.new(workbook_file_pathname, file_warning: :ignore)
|
21
|
+
when /\.xlsx/ then Roo::Excelx.new(workbook_file_pathname, file_warning: :ignore)
|
22
|
+
end
|
23
|
+
|
24
|
+
@worksheet = @workbook.sheet(worksheet_title_or_index)
|
25
|
+
|
26
|
+
@header_row = @worksheet.row(num_header_rows)
|
27
|
+
|
28
|
+
# Reads all the worksheet rows, one at a time, using Workshete#row (note: reading
|
29
|
+
# them all at once using Worksheet#parse didn't work because the first row
|
30
|
+
# was missed some times.)
|
31
|
+
rows = (1..@worksheet.last_row).map {|row_number| @worksheet.row(row_number) }
|
32
|
+
|
33
|
+
# Create the rows cache, made of instances of ExcelWorksheetProxyRow
|
34
|
+
@rows = rows.collect.with_index do |row, index|
|
35
|
+
ExcelWorksheetProxyRow.new(@header_row, index+1, *row)
|
36
|
+
end
|
37
|
+
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def [](index)
|
42
|
+
@rows[index]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return only the rows that contain data (excludes the header rows)
|
46
|
+
def data_rows
|
47
|
+
@rows[@num_header_rows..-1]
|
48
|
+
end
|
49
|
+
|
50
|
+
# Finds and returns the first row that contains cell_value at the given column
|
51
|
+
def find_row_by_column_value(column_title, cell_value)
|
52
|
+
data_rows.each do |row|
|
53
|
+
return row if row[column_title] == cell_value
|
54
|
+
end
|
55
|
+
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_row(row_data)
|
60
|
+
last_row_number = @rows.count
|
61
|
+
@rows << ExcelWorksheetProxyRow.new(@header_row, last_row_number+1, *row_data)
|
62
|
+
end
|
63
|
+
|
64
|
+
def spreadsheet
|
65
|
+
@workbook
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class SpreadsheetGoodies::ExcelWorksheetProxy
|
2
|
+
|
3
|
+
# Overload Array#[] to allow reading a cell's contents using its column title
|
4
|
+
# as index, e.g.: row['Column Title']
|
5
|
+
class ::ExcelWorksheetProxyRow < Array
|
6
|
+
attr_reader :header_row, :row_number
|
7
|
+
|
8
|
+
# @param header_row [Array] The header cells of the sheet from were the row was taken
|
9
|
+
# @param sheet_row_number [Integer] The original row number of the row in the
|
10
|
+
# sheet from where the row was taken. Row numebrs follow the spreadshet convention,
|
11
|
+
# i.e., starting from 1.
|
12
|
+
def initialize(header_row, sheet_row_number, *args)
|
13
|
+
@header_row = header_row
|
14
|
+
@row_number = sheet_row_number
|
15
|
+
super(args)
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param locator [Integer|String] The index of a row element. Can be a string
|
19
|
+
# (a column title) or an integer (starting at 0 for the first cell).
|
20
|
+
# @return [String]
|
21
|
+
def [](locator)
|
22
|
+
if locator.is_a?(String)
|
23
|
+
return self[ @header_row.index(locator) ]
|
24
|
+
else
|
25
|
+
return super(locator)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'google_drive'
|
2
|
+
require 'googleauth'
|
3
|
+
|
4
|
+
class SpreadsheetGoodies::GoogleDriveConnector
|
5
|
+
attr_reader :logger, :session
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@logger = Logger.new(STDOUT)
|
9
|
+
@session = log_into_google_drive
|
10
|
+
raise 'Session not found!' unless @session
|
11
|
+
end
|
12
|
+
|
13
|
+
def log_into_google_drive
|
14
|
+
case SpreadsheetGoodies.configuration.login_method
|
15
|
+
when :service_account then log_into_google_drive_via_service_account
|
16
|
+
when :oauth2 then log_into_google_drive_via_oauth2
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def read_worksheet(spreadsheet_key, worksheet_title=nil)
|
21
|
+
puts "Reading sheet '#{worksheet_title}' from Google Drive..."
|
22
|
+
spreadsheet = @session.spreadsheet_by_key(spreadsheet_key)
|
23
|
+
|
24
|
+
if worksheet_title
|
25
|
+
worksheet = spreadsheet.worksheet_by_title(worksheet_title)
|
26
|
+
if worksheet.nil?
|
27
|
+
raise "Worksheet named '#{worksheet_title}' not found in spreadsheet."
|
28
|
+
end
|
29
|
+
else
|
30
|
+
worksheet = spreadsheet.worksheets.first
|
31
|
+
end
|
32
|
+
|
33
|
+
worksheet
|
34
|
+
end
|
35
|
+
|
36
|
+
def read_worksheet_as_string(spreadsheet_key, worksheet_title)
|
37
|
+
worksheet = read_worksheet(spreadsheet_key, worksheet_title)
|
38
|
+
contents = worksheet.export_as_string.force_encoding('utf-8')
|
39
|
+
puts 'Spreadhseet read successfully.'
|
40
|
+
contents
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Authenticate with Google Drive via OAuth2.
|
46
|
+
# Your OAuth2 ids can be accessed at https://console.developers.google.com/apis/credentials,
|
47
|
+
# logged with your Google account in your custom project. The keys are at
|
48
|
+
# 'API manager' > 'Credentials'. CLIENT_ID and CLIENT_SECRET are available
|
49
|
+
# from the credentials page. The REFRESH_TOKEN must be obtained uncommenting
|
50
|
+
# some rows from the method below and obtaining an access_token via a browser
|
51
|
+
# logged in to your google account.
|
52
|
+
def log_into_google_drive_via_oauth2
|
53
|
+
credentials = Google::Auth::UserRefreshCredentials.new(
|
54
|
+
client_id: SpreadsheetGoodies.configuration.google_client_id,
|
55
|
+
client_secret: SpreadsheetGoodies.configuration.client_secret,
|
56
|
+
scope: [
|
57
|
+
'https://www.googleapis.com/auth/drive',
|
58
|
+
'https://spreadsheets.google.com/feeds/',
|
59
|
+
],
|
60
|
+
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob'
|
61
|
+
)
|
62
|
+
credentials.refresh_token = SpreadsheetGoodies.configuration.refresh_token if SpreadsheetGoodies.configuration.refresh_token
|
63
|
+
|
64
|
+
# Uncomment the rows below to obtain the refresh_token:
|
65
|
+
# print("1. Open this page:\n%s\n\n" % credentials.authorization_uri)
|
66
|
+
# print("2. Enter the authorization code shown in the page: ")
|
67
|
+
# credentials.code = $stdin.gets.chomp
|
68
|
+
# credentials.fetch_access_token!
|
69
|
+
# puts credentials.refresh_token
|
70
|
+
# debugger
|
71
|
+
|
72
|
+
begin
|
73
|
+
credentials.fetch_access_token!
|
74
|
+
rescue Faraday::ConnectionFailed
|
75
|
+
logger.info 'Error logging into Google Drive. Is your internet connection down?'
|
76
|
+
exit
|
77
|
+
end
|
78
|
+
|
79
|
+
session = GoogleDrive::Session.from_credentials(credentials)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Authenticate with Google Drive via a service account.
|
83
|
+
# Your service accounts can be accessed at https://console.developers.google.com/apis/credentials,
|
84
|
+
# logged with your Google account in your custom project.
|
85
|
+
# The service accounts are at 'IAM and administrator' > 'Service accounts'.
|
86
|
+
# The keys are at 'API manager' > 'Credentials'.
|
87
|
+
def log_into_google_drive_via_service_account
|
88
|
+
session = GoogleDrive::Session.from_service_account_key(
|
89
|
+
StringIO.new(SpreadsheetGoodies.configuration.service_account_key_json)
|
90
|
+
)
|
91
|
+
session
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require_relative 'google_worksheet_proxy_row'
|
2
|
+
require 'csv'
|
3
|
+
|
4
|
+
# A cached copy of a Google Spreadsheets worksheet (i.e., a single workbook "tab"),
|
5
|
+
# stored as an array of arrays. Individual cells can be accessed by column title.
|
6
|
+
# Example:
|
7
|
+
# worksheet[0]['A column title']
|
8
|
+
class SpreadsheetGoodies::GoogleWorksheetProxy
|
9
|
+
attr_reader :rows, :header_row
|
10
|
+
|
11
|
+
# @param worksheet_title_or_index [String] Sheet name; if unspecified, the
|
12
|
+
# first sheet will be used.
|
13
|
+
# @param num_header_rows [Integer] Number of rows at the top of the sheet that
|
14
|
+
# contain headers or stuff other than data. Optional; if unspecified, assumes
|
15
|
+
# that a single top row contains the header and all rows below are data.
|
16
|
+
def initialize(spreadsheet_key, worksheet_title=nil, num_header_rows=1)
|
17
|
+
@spreadsheet_key = spreadsheet_key
|
18
|
+
@worksheet_title = worksheet_title
|
19
|
+
@num_header_rows = num_header_rows
|
20
|
+
end
|
21
|
+
|
22
|
+
# Reads sheet contents and caches it into instance variables to so that it
|
23
|
+
# can be accessed later without accessing Google Drive.
|
24
|
+
def read_from_google_drive
|
25
|
+
worksheet_contents = read_worksheet.export_as_string.force_encoding('utf-8')
|
26
|
+
|
27
|
+
rows = CSV::parse(worksheet_contents)
|
28
|
+
@header_row = rows[@num_header_rows-1]
|
29
|
+
@rows = rows.collect.with_index do |row, index|
|
30
|
+
GoogleWorksheetProxyRow.new(@header_row, index+1, *row)
|
31
|
+
end
|
32
|
+
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def [](index)
|
37
|
+
@rows[index]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return only the rows that contain data (excludes the header rows)
|
41
|
+
def data_rows
|
42
|
+
@rows[@num_header_rows..-1]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Finds and returns the first row that contains cell_value at the given column
|
46
|
+
def find_row_by_column_value(column_title, cell_value)
|
47
|
+
data_rows.each do |row|
|
48
|
+
return row if row[column_title] == cell_value
|
49
|
+
end
|
50
|
+
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# Reads a GoogleDrive::Spreadsheet object from Google Drive. The sheet object
|
55
|
+
# can be used to write data to the online spreadhseet, as we don't yet provide
|
56
|
+
# the helper methods for our users to do it via our public interface.
|
57
|
+
def read_worksheet
|
58
|
+
connector = SpreadsheetGoodies::GoogleDriveConnector.new
|
59
|
+
connector.read_worksheet(@spreadsheet_key, @worksheet_title)
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class SpreadsheetGoodies::GoogleWorksheetProxy
|
2
|
+
|
3
|
+
# Sobrecarrega método [] da Array para permitir acessar células passando
|
4
|
+
# o título da coluna como índice. Ex: row['Data formal do pedido']
|
5
|
+
class ::GoogleWorksheetProxyRow < Array
|
6
|
+
attr_reader :header_row, :row_number
|
7
|
+
|
8
|
+
def initialize(header_row, sheet_row_number, *args)
|
9
|
+
@header_row = header_row
|
10
|
+
@row_number = sheet_row_number
|
11
|
+
super(args)
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](locator)
|
15
|
+
if locator.is_a?(String)
|
16
|
+
return self[ @header_row.index(locator) ]
|
17
|
+
else
|
18
|
+
return super(locator)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'spreadsheet_goodies/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gemspec|
|
7
|
+
gemspec.name = 'spreadsheet_goodies'
|
8
|
+
gemspec.version = SpreadsheetGoodies::VERSION
|
9
|
+
gemspec.authors = ['Ricardo Jasinski']
|
10
|
+
gemspec.email = ['jasinski@solvis.com.br']
|
11
|
+
|
12
|
+
gemspec.summary = "SpreadsheetGoodies is a collection of tools to help work " +
|
13
|
+
"with Excel and Google Drive spreadhseets."
|
14
|
+
gemspec.description = "SpreadsheetGoodies is a collection of tools to help work " +
|
15
|
+
"with Excel and Google Drive spreadhseets. It relies " +
|
16
|
+
"on other gems to do the actual work of reading and writing to " +
|
17
|
+
"spreadsheet documents. It main features are:"
|
18
|
+
" * Read a spreadseet to an array of arrays, to allow accessing its data " +
|
19
|
+
" without using the original document"
|
20
|
+
" * Access a row's elements using the column titles as keys"
|
21
|
+
gemspec.homepage = 'https://github.com/ricardo-jasinski/spreadsheet_goodies'
|
22
|
+
gemspec.license = 'Unlicense'
|
23
|
+
|
24
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
25
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
26
|
+
if gemspec.respond_to?(:metadata)
|
27
|
+
gemspec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
28
|
+
else
|
29
|
+
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
30
|
+
'public gem pushes.'
|
31
|
+
end
|
32
|
+
|
33
|
+
gemspec.files = `git ls-files -z`.split("\x0").reject do |f|
|
34
|
+
f.match(%r{^(test|spec|features)/})
|
35
|
+
end
|
36
|
+
gemspec.bindir = 'exe'
|
37
|
+
gemspec.executables = gemspec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
38
|
+
gemspec.require_paths = ['lib']
|
39
|
+
|
40
|
+
gemspec.add_development_dependency 'bundler', '~> 1.14'
|
41
|
+
gemspec.add_development_dependency 'rake', '~> 10.0'
|
42
|
+
gemspec.add_development_dependency 'rspec', '~> 3.0'
|
43
|
+
|
44
|
+
# gemspec.add_dependency 'zip-zip'#, '~> 0'
|
45
|
+
gemspec.add_dependency 'axlsx', '~> 0'
|
46
|
+
gemspec.add_dependency 'google_drive', '~> 0'
|
47
|
+
gemspec.add_dependency 'csv', '~> 0'
|
48
|
+
gemspec.add_dependency 'roo', '~> 0'#, '~> 1.13.2'
|
49
|
+
# gemspec.add_dependency 'roo-xls', '~> 1.2.0'
|
50
|
+
|
51
|
+
end
|
metadata
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: spreadsheet_goodies
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ricardo Jasinski
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-07-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.14'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.14'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: axlsx
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: google_drive
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: csv
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: roo
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: 'SpreadsheetGoodies is a collection of tools to help work with Excel
|
112
|
+
and Google Drive spreadhseets. It relies on other gems to do the actual work of
|
113
|
+
reading and writing to spreadsheet documents. It main features are:'
|
114
|
+
email:
|
115
|
+
- jasinski@solvis.com.br
|
116
|
+
executables: []
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files: []
|
119
|
+
files:
|
120
|
+
- ".gitignore"
|
121
|
+
- ".rspec"
|
122
|
+
- ".ruby-gemset"
|
123
|
+
- ".ruby-version"
|
124
|
+
- ".travis.yml"
|
125
|
+
- Gemfile
|
126
|
+
- LICENSE
|
127
|
+
- LICENSE.txt
|
128
|
+
- README.md
|
129
|
+
- Rakefile
|
130
|
+
- bin/console
|
131
|
+
- bin/setup
|
132
|
+
- builds/spreadsheet_goodies-0.0.1.gem
|
133
|
+
- lib/spreadsheet_goodies.rb
|
134
|
+
- lib/spreadsheet_goodies/excel_workbook_builder.rb
|
135
|
+
- lib/spreadsheet_goodies/excel_worksheet_proxy.rb
|
136
|
+
- lib/spreadsheet_goodies/excel_worksheet_proxy_row.rb
|
137
|
+
- lib/spreadsheet_goodies/google_drive_connector.rb
|
138
|
+
- lib/spreadsheet_goodies/google_worksheet_proxy.rb
|
139
|
+
- lib/spreadsheet_goodies/google_worksheet_proxy_row.rb
|
140
|
+
- lib/spreadsheet_goodies/version.rb
|
141
|
+
- spreadsheet_goodies.gemspec
|
142
|
+
homepage: https://github.com/ricardo-jasinski/spreadsheet_goodies
|
143
|
+
licenses:
|
144
|
+
- Unlicense
|
145
|
+
metadata:
|
146
|
+
allowed_push_host: https://rubygems.org
|
147
|
+
post_install_message:
|
148
|
+
rdoc_options: []
|
149
|
+
require_paths:
|
150
|
+
- lib
|
151
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
152
|
+
requirements:
|
153
|
+
- - ">="
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
version: '0'
|
156
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
requirements: []
|
162
|
+
rubyforge_project:
|
163
|
+
rubygems_version: 2.6.6
|
164
|
+
signing_key:
|
165
|
+
specification_version: 4
|
166
|
+
summary: SpreadsheetGoodies is a collection of tools to help work with Excel and Google
|
167
|
+
Drive spreadhseets.
|
168
|
+
test_files: []
|