spreadsheet_goodies 0.0.4 → 0.0.5
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 +4 -4
- data/README.md +63 -13
- data/builds/spreadsheet_goodies-0.0.4.gem +0 -0
- data/lib/spreadsheet_goodies/abstract_base_worksheet.rb +39 -0
- data/lib/spreadsheet_goodies/excel/workbook_builder.rb +80 -0
- data/lib/spreadsheet_goodies/excel/worksheet.rb +53 -0
- data/lib/spreadsheet_goodies/excel.rb +11 -0
- data/lib/spreadsheet_goodies/google_drive/connector.rb +104 -0
- data/lib/spreadsheet_goodies/google_drive/worksheet.rb +68 -0
- data/lib/spreadsheet_goodies/google_drive.rb +12 -0
- data/lib/spreadsheet_goodies/row.rb +33 -0
- data/lib/spreadsheet_goodies/version.rb +1 -1
- data/lib/spreadsheet_goodies.rb +7 -4
- data/spreadsheet_goodies.gemspec +5 -5
- metadata +16 -12
- data/LICENSE.txt +0 -24
- data/lib/spreadsheet_goodies/excel_workbook_builder.rb +0 -78
- data/lib/spreadsheet_goodies/excel_worksheet.rb +0 -68
- data/lib/spreadsheet_goodies/excel_worksheet_row.rb +0 -30
- data/lib/spreadsheet_goodies/google_drive_connector.rb +0 -93
- data/lib/spreadsheet_goodies/google_drive_worksheet.rb +0 -68
- data/lib/spreadsheet_goodies/google_drive_worksheet_row.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1cb1da443d319f91e093023601d1688081db8344
|
4
|
+
data.tar.gz: 10d6c084df8ae3da6ba1a1322805750d52191a3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec886820ca483030f181f1d82542660aa9f54a898f9d0c97478648937c7e4ea9f44cf5086a508efe4442acc4ed6f002b941e8c4f3680912fe77ee031f95a1078
|
7
|
+
data.tar.gz: 434ed1a593759e605e34f18f345245a78ea7c208c64f9f9a1f9dbdd3d1f719f093b6d638a3dfb15e47b206aaecfb3965da89e0ad37bdf8f969b7fa1529267fe9
|
data/README.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# SpreadsheetGoodies
|
2
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
|
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
6
|
features are:
|
7
7
|
|
8
|
-
* Read a spreadseet as an array of arrays to allow aceessing its data without
|
8
|
+
* Read a spreadseet as an array of arrays to allow aceessing its data without
|
9
9
|
using the original document
|
10
10
|
* Access a row's elements using the column titles as keys"
|
11
11
|
|
@@ -28,24 +28,75 @@ Or install it yourself as:
|
|
28
28
|
## Usage
|
29
29
|
|
30
30
|
Read a Google Spreadsheet:
|
31
|
-
```
|
32
|
-
|
33
|
-
|
31
|
+
```ruby
|
32
|
+
sheet = SpreadsheetGoodies::GoogleDrive.read_worksheet(
|
33
|
+
spreadsheet_key: '1UC43X6aZwlWPCnn...', # required,
|
34
|
+
worksheet_title: 'sheet1', # optional, first worksheet is loaded if not specified
|
35
|
+
)
|
34
36
|
```
|
35
37
|
|
36
38
|
Read an Excel workbook:
|
37
|
-
```
|
38
|
-
sheet = SpreadsheetGoodies::
|
39
|
+
```ruby
|
40
|
+
sheet = SpreadsheetGoodies::Excel.read_worksheet(
|
41
|
+
file_pathname: '~/workbook.xlsx', # required,
|
42
|
+
worksheet_title_or_index: 'sheet1', # optional, first worksheet is loaded if not specified
|
43
|
+
)
|
39
44
|
```
|
40
45
|
|
41
|
-
Iterate over every data row (i.e., all but the header row) and print the value
|
42
|
-
of a column titled 'Total':
|
43
|
-
```
|
46
|
+
Iterate over every data row (i.e., all but the header row) and print the value
|
47
|
+
of a column titled 'Total':
|
48
|
+
```ruby
|
44
49
|
sheet.data_rows.each do |row|
|
45
50
|
puts "#{row.row_number} -- #{row['Total']}"
|
46
51
|
end
|
47
52
|
```
|
48
53
|
|
54
|
+
Writing values to cells (only available for GoogleDrive adapter right now):
|
55
|
+
```ruby
|
56
|
+
row = sheet[0]
|
57
|
+
row[0] = 'First cell'
|
58
|
+
row[1] = 'Second cell'
|
59
|
+
sheet.commit_writes! # changes are applied to real spreadsheet
|
60
|
+
```
|
61
|
+
|
62
|
+
## Logging in to Google Drive
|
63
|
+
If you need to access a spreadsheet on Google Drive that is not publicly accessible,
|
64
|
+
you are required to setup an authentication method. Currently, there are two available
|
65
|
+
authentication methods.
|
66
|
+
|
67
|
+
### OAuth2
|
68
|
+
To setup OAuth2, first you must configure your Google client id and a client secret
|
69
|
+
like the example below. If you don't have a client id yet, you must create a project
|
70
|
+
and enable the GoogleDrive API at https://console.developers.google.com. Then you
|
71
|
+
need to create a OAuth client id.
|
72
|
+
```ruby
|
73
|
+
SpreadsheetGoodies.configure do |config|
|
74
|
+
config.login_method = :oauth2
|
75
|
+
config.google_client_id = '1012345678904-fdks82jfhe8ojdks7285fj4pnqiejrnbt.apps.googleusercontent.com' # put your real client id here
|
76
|
+
config.client_secret = 'Aa-Ku8C-askjfAYKkdjf9ssnf' # put your real secret here
|
77
|
+
end
|
78
|
+
```
|
79
|
+
|
80
|
+
Then run your code. You will be prompted to make the authorization process to obtain
|
81
|
+
a refresh token:
|
82
|
+
```
|
83
|
+
1. Open this page:
|
84
|
+
https://accounts.google.com/o/oauth2/auth?access_type=offline&client_id=1012345678904-fdks82jfhe8ojdks7285fj4pnqiejrnbt.apps.googleusercontent.com&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&scope=https://www.googleapis.com/auth/drive%20https://spreadsheets.google.com/feeds/
|
85
|
+
|
86
|
+
2. Enter the authorization code shown in the page: 4/LADQHhpk7x27BMeP2tIEe_pKuTJmJmZhWoRcBhBmFTVRqSEtcap7Z6s
|
87
|
+
|
88
|
+
Congratulations! Your refresh token is: 1/c9JDKAUF83_4SPqNc8ldQWe9TdXOxqXvMJJPtmDA2k
|
89
|
+
Set the refresh_token in your SpreadsheetGoodies configuration and run your code again
|
90
|
+
```
|
91
|
+
|
92
|
+
### Service Accounts
|
93
|
+
```ruby
|
94
|
+
SpreadsheetGoodies.configure do |config|
|
95
|
+
config.login_method = :service_account
|
96
|
+
config.service_account_key_json = '...'
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
49
100
|
## Contributing
|
50
101
|
|
51
102
|
Bug reports and pull requests are welcome on GitHub at https://github.com/ricardo-jasinski/spreadsheet_goodies.
|
@@ -54,4 +105,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/ricard
|
|
54
105
|
## License
|
55
106
|
|
56
107
|
The gem is available as open source under the terms of the [Unlicense](http://unlicense.org/UNLICENSE).
|
57
|
-
|
Binary file
|
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
# Base class for all worksheets
|
3
|
+
module SpreadsheetGoodies
|
4
|
+
class AbstractBaseWorksheet
|
5
|
+
attr_reader :rows, :header_row
|
6
|
+
|
7
|
+
def [](index)
|
8
|
+
@rows[index]
|
9
|
+
end
|
10
|
+
|
11
|
+
# Return only the rows that contain data (excludes the header rows)
|
12
|
+
def data_rows
|
13
|
+
@rows[@num_header_rows..-1]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Finds and returns the first row that contains cell_value at the given column
|
17
|
+
def find_row_by_column_value(column_title, cell_value)
|
18
|
+
data_rows.each do |row|
|
19
|
+
return row if row[column_title] == cell_value
|
20
|
+
end
|
21
|
+
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
# Writes to a given cell identified by row and column indexes (they start at 1)
|
26
|
+
# This method should be overriden by worksheets that wish to implement writes.
|
27
|
+
# Usually this method is not called directly by the gem user, but rather from
|
28
|
+
# the modified row itself
|
29
|
+
def write_to_cell(row_index, col_index, value)
|
30
|
+
raise 'writes are not implemented for this type of worksheet'
|
31
|
+
end
|
32
|
+
|
33
|
+
# Some adapters (like GoogleDrive) require writes to be committed to actually
|
34
|
+
# make the changes on the spreadsheet
|
35
|
+
def commit_writes!
|
36
|
+
raise 'this kind of worksheet does not require writes to be committed'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'axlsx'
|
2
|
+
|
3
|
+
# A set of methods to help create and format Excel workbooks
|
4
|
+
module SpreadsheetGoodies::Excel
|
5
|
+
class WorkbookBuilder
|
6
|
+
attr_reader :current_sheet
|
7
|
+
|
8
|
+
def initialize(output_file_pathname)
|
9
|
+
@output_file_pathname = output_file_pathname
|
10
|
+
@axlsx_package = Axlsx::Package.new
|
11
|
+
end
|
12
|
+
|
13
|
+
# The underlying Axlsx::Workbook object
|
14
|
+
def workbook
|
15
|
+
@axlsx_package.workbook
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_worksheet(sheet_name)
|
19
|
+
@current_sheet = @workbook.add_worksheet(name: sheet_name)
|
20
|
+
setup_current_sheet_styles
|
21
|
+
freeze_top_row
|
22
|
+
@current_sheet
|
23
|
+
end
|
24
|
+
|
25
|
+
# Add a row to the current sheet using the data row style
|
26
|
+
def add_data_row(row_values)
|
27
|
+
@current_sheet.add_row(row_values, style: @data_row_style)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Add a row to the current sheet using the header row style
|
31
|
+
def add_header_row(row_values)
|
32
|
+
@current_sheet.add_row(row_values, style: @header_row_style)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Add filter and sorting controls to a sheet.
|
36
|
+
def setup_auto_filter(sheet=nil)
|
37
|
+
worksheet = sheet || @current_sheet
|
38
|
+
top_left_cell_label = worksheet.dimension.first_cell_reference
|
39
|
+
bottom_right_cell_label = worksheet.dimension.last_cell_reference
|
40
|
+
filter_range = "#{top_left_cell_label}:#{bottom_right_cell_label}"
|
41
|
+
worksheet.auto_filter = filter_range
|
42
|
+
end
|
43
|
+
|
44
|
+
def write_to_file
|
45
|
+
@axlsx_package.serialize(@output_file_pathname)
|
46
|
+
end
|
47
|
+
|
48
|
+
def freeze_top_row
|
49
|
+
@current_sheet.sheet_view.pane do |pane|
|
50
|
+
pane.top_left_cell = 'A2'
|
51
|
+
pane.state = :frozen_split
|
52
|
+
pane.y_split = 1
|
53
|
+
pane.active_pane = :bottom_right
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def setup_current_sheet_styles
|
60
|
+
@header_row_style = @current_sheet.styles.add_style(
|
61
|
+
alignment: {horizontal: :center, vertical: :center},
|
62
|
+
font_name: 'Calibri',
|
63
|
+
bg_color: 'FFDDDDDD',
|
64
|
+
b: true,
|
65
|
+
)
|
66
|
+
|
67
|
+
@header_row_sodexo_style = @current_sheet.styles.add_style(
|
68
|
+
alignment: {horizontal: :center, vertical: :center},
|
69
|
+
font_name: 'Calibri',
|
70
|
+
bg_color: '002060',
|
71
|
+
fg_color: 'FFFFFF',
|
72
|
+
b: true,
|
73
|
+
)
|
74
|
+
|
75
|
+
@data_row_style = @current_sheet.styles.add_style(
|
76
|
+
font_name: 'Calibri',
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require_relative '../abstract_base_worksheet'
|
2
|
+
require_relative '../row'
|
3
|
+
|
4
|
+
# A cached copy of an Excel worksheet (a single workbook "tab"), stored as an
|
5
|
+
# array of arrays. Individual cells can be accessed by column title, e.g.:
|
6
|
+
# worksheet[0]['A column title']
|
7
|
+
module SpreadsheetGoodies::Excel
|
8
|
+
class Worksheet < SpreadsheetGoodies::AbstractBaseWorksheet
|
9
|
+
attr_reader :workbook, :worksheet
|
10
|
+
|
11
|
+
# @param workbook_file_pathname [String] Full path and filename to Excel workbook document
|
12
|
+
# @param worksheet_title_or_index [String|Integer] Sheet name or index (zero-based)
|
13
|
+
# within the workbook. Optional; if unspecified, the first sheet will be used.
|
14
|
+
# @param num_header_rows [Integer] Number of rows at the top of the sheet that
|
15
|
+
# contain headers or stuff other than data. Optional; if unspecified, assumes
|
16
|
+
# that a single top row contains the header and all rows below are data.
|
17
|
+
def initialize(workbook_file_pathname, worksheet_title_or_index=0, num_header_rows=1)
|
18
|
+
@worksheet_title = worksheet_title_or_index
|
19
|
+
@num_header_rows = num_header_rows
|
20
|
+
|
21
|
+
@workbook = case workbook_file_pathname.to_s
|
22
|
+
when /\.xls[^x]/ then Roo::Excel.new(workbook_file_pathname, file_warning: :ignore)
|
23
|
+
when /\.xlsx/ then Roo::Excelx.new(workbook_file_pathname, file_warning: :ignore)
|
24
|
+
end
|
25
|
+
|
26
|
+
@worksheet = @workbook.sheet(worksheet_title_or_index)
|
27
|
+
|
28
|
+
@header_row = @worksheet.row(num_header_rows)
|
29
|
+
|
30
|
+
# Reads all the worksheet rows, one at a time, using Workshete#row (note: reading
|
31
|
+
# them all at once using Worksheet#parse didn't work because the first row
|
32
|
+
# was missed some times.)
|
33
|
+
rows = (1..@worksheet.last_row).map {|row_number| @worksheet.row(row_number) }
|
34
|
+
|
35
|
+
# Create the rows cache, made of instances of ExcelWorksheetRow
|
36
|
+
@rows = rows.collect.with_index do |row, index|
|
37
|
+
SpreadsheetGoodies::Row.new(@header_row, index+1, self, *row)
|
38
|
+
end
|
39
|
+
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_row(row_data)
|
44
|
+
last_row_number = @rows.count
|
45
|
+
@rows << SpreadsheetGoodies::Row.new(@header_row, last_row_number+1, self, *row_data)
|
46
|
+
end
|
47
|
+
|
48
|
+
def spreadsheet
|
49
|
+
@workbook
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
|
2
|
+
module SpreadsheetGoodies::Excel
|
3
|
+
|
4
|
+
def self.read_worksheet(file_pathname:, worksheet_title_or_index:0, num_header_rows:1)
|
5
|
+
Worksheet.new(file_pathname, worksheet_title_or_index, num_header_rows)
|
6
|
+
end
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
# loads all files in excel folder
|
11
|
+
Dir[File.join(File.dirname(__FILE__), "excel/**/*.rb")].each { |f| require f }
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'google_drive'
|
2
|
+
require 'googleauth'
|
3
|
+
|
4
|
+
module SpreadsheetGoodies::GoogleDrive
|
5
|
+
class Connector
|
6
|
+
attr_reader :logger, :session
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@logger = Logger.new(STDOUT)
|
10
|
+
@session = log_into_google_drive
|
11
|
+
raise 'Session not found!' unless @session
|
12
|
+
end
|
13
|
+
|
14
|
+
# Reads a GoogleDrive::Spreadsheet object from Google Drive, using the
|
15
|
+
# google_drive gem. Documentation for this class can be found here:
|
16
|
+
# https://www.rubydoc.info/github/gimite/google-drive-ruby/GoogleDrive/Spreadsheet
|
17
|
+
def read_worksheet(spreadsheet_key, worksheet_title=nil)
|
18
|
+
puts "Reading sheet '#{worksheet_title}' from Google Drive..."
|
19
|
+
spreadsheet = @session.spreadsheet_by_key(spreadsheet_key)
|
20
|
+
|
21
|
+
if worksheet_title
|
22
|
+
worksheet = spreadsheet.worksheet_by_title(worksheet_title)
|
23
|
+
if worksheet.nil?
|
24
|
+
raise "Worksheet named '#{worksheet_title}' not found in spreadsheet."
|
25
|
+
end
|
26
|
+
else
|
27
|
+
worksheet = spreadsheet.worksheets.first
|
28
|
+
end
|
29
|
+
|
30
|
+
worksheet
|
31
|
+
end
|
32
|
+
|
33
|
+
def read_worksheet_as_string(spreadsheet_key, worksheet_title)
|
34
|
+
worksheet = read_worksheet(spreadsheet_key, worksheet_title)
|
35
|
+
contents = worksheet.export_as_string.force_encoding('utf-8')
|
36
|
+
puts 'Spreadsheet read successfully.'
|
37
|
+
contents
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def log_into_google_drive
|
43
|
+
case SpreadsheetGoodies.configuration.login_method
|
44
|
+
when :service_account then log_into_google_drive_via_service_account
|
45
|
+
when :oauth2 then log_into_google_drive_via_oauth2
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Authenticate with Google Drive via OAuth2.
|
50
|
+
# Your OAuth2 ids can be accessed at https://console.developers.google.com/apis/credentials,
|
51
|
+
# logged with your Google account in your custom project. The keys are at
|
52
|
+
# 'API manager' > 'Credentials'. CLIENT_ID and CLIENT_SECRET are available
|
53
|
+
# from the credentials page. The REFRESH_TOKEN must be obtained uncommenting
|
54
|
+
# some rows from the method below and obtaining an access_token via a browser
|
55
|
+
# logged in to your google account.
|
56
|
+
def log_into_google_drive_via_oauth2
|
57
|
+
credentials = Google::Auth::UserRefreshCredentials.new(
|
58
|
+
client_id: SpreadsheetGoodies.configuration.google_client_id,
|
59
|
+
client_secret: SpreadsheetGoodies.configuration.client_secret,
|
60
|
+
scope: [
|
61
|
+
'https://www.googleapis.com/auth/drive',
|
62
|
+
'https://spreadsheets.google.com/feeds/',
|
63
|
+
],
|
64
|
+
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob'
|
65
|
+
)
|
66
|
+
|
67
|
+
# if has refresh_token, logs in
|
68
|
+
if SpreadsheetGoodies.configuration.refresh_token
|
69
|
+
credentials.refresh_token = SpreadsheetGoodies.configuration.refresh_token
|
70
|
+
|
71
|
+
# if refresh_token is missing, prompts user to obtain it
|
72
|
+
else
|
73
|
+
print("1. Open this page:\n%s\n\n" % credentials.authorization_uri)
|
74
|
+
print("2. Enter the authorization code shown in the page: ")
|
75
|
+
credentials.code = $stdin.gets.chomp
|
76
|
+
credentials.fetch_access_token!
|
77
|
+
print("\nCongratulations! Your refresh token is: #{credentials.refresh_token}\n")
|
78
|
+
print("Set the refresh_token in your SpreadsheetGoodies configuration and run your code again\n")
|
79
|
+
exit(0)
|
80
|
+
end
|
81
|
+
|
82
|
+
begin
|
83
|
+
credentials.fetch_access_token!
|
84
|
+
rescue Faraday::ConnectionFailed
|
85
|
+
logger.info 'Error logging into Google Drive. Is your internet connection down?'
|
86
|
+
exit
|
87
|
+
end
|
88
|
+
|
89
|
+
session = GoogleDrive::Session.from_credentials(credentials)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Authenticate with Google Drive via a service account.
|
93
|
+
# Your service accounts can be accessed at https://console.developers.google.com/apis/credentials,
|
94
|
+
# logged with your Google account in your custom project.
|
95
|
+
# The service accounts are at 'IAM and administrator' > 'Service accounts'.
|
96
|
+
# The keys are at 'API manager' > 'Credentials'.
|
97
|
+
def log_into_google_drive_via_service_account
|
98
|
+
session = GoogleDrive::Session.from_service_account_key(
|
99
|
+
StringIO.new(SpreadsheetGoodies.configuration.service_account_key_json)
|
100
|
+
)
|
101
|
+
session
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require_relative '../abstract_base_worksheet'
|
3
|
+
require_relative '../row'
|
4
|
+
|
5
|
+
# A cached copy of a Google Spreadsheets worksheet (i.e., a single workbook "tab"),
|
6
|
+
# Individual cells of a row can be accessed by column title.
|
7
|
+
# Example:
|
8
|
+
# worksheet[0]['A column title']
|
9
|
+
module SpreadsheetGoodies::GoogleDrive
|
10
|
+
class Worksheet < SpreadsheetGoodies::AbstractBaseWorksheet
|
11
|
+
# @param spreadsheet_key [String] Spreadsheet identifier, which can be retrieved
|
12
|
+
# from the spreasheet's url
|
13
|
+
# @param worksheet_title_or_index [String] Sheet name; if unspecified, the
|
14
|
+
# first sheet will be used.
|
15
|
+
# @param num_header_rows [Integer] Number of rows at the top of the sheet that
|
16
|
+
# contain headers or stuff other than data. Optional; if unspecified, assumes
|
17
|
+
# that a single top row contains the header and all rows below are data.
|
18
|
+
def initialize(spreadsheet_key, worksheet_title=nil, num_header_rows=1)
|
19
|
+
@spreadsheet_key = spreadsheet_key
|
20
|
+
@worksheet_title = worksheet_title
|
21
|
+
@num_header_rows = num_header_rows
|
22
|
+
|
23
|
+
read_from_google_drive
|
24
|
+
end
|
25
|
+
|
26
|
+
# Writes to a given cell identified by row and column indexes (they start at 1)
|
27
|
+
# @override
|
28
|
+
def write_to_cell(row_index, col_index, value)
|
29
|
+
underlying_adapter_worksheet[row_index, col_index] = value
|
30
|
+
end
|
31
|
+
|
32
|
+
# Commit writes so they are propagated to the real spreadsheet
|
33
|
+
# @override
|
34
|
+
def commit_writes!
|
35
|
+
underlying_adapter_worksheet.save
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Reads sheet contents and caches it into instance variables to so that it
|
41
|
+
# can be accessed later without accessing Google Drive.
|
42
|
+
def read_from_google_drive
|
43
|
+
worksheet_contents = underlying_adapter_worksheet.export_as_string.force_encoding('utf-8')
|
44
|
+
|
45
|
+
rows = CSV::parse(worksheet_contents)
|
46
|
+
|
47
|
+
if SpreadsheetGoodies.configuration.strip_values_on_read
|
48
|
+
rows = rows.map do |row|
|
49
|
+
row.map {|element| element.try(:strip) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
@header_row = rows[@num_header_rows-1]
|
54
|
+
@rows = rows.collect.with_index do |row, index|
|
55
|
+
SpreadsheetGoodies::Row.new(@header_row, index+1, self, *row)
|
56
|
+
end
|
57
|
+
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
# Reads a GoogleDrive::Spreadsheet object from Google Drive, using the
|
62
|
+
# google_drive gem. Documentation for this class can be found here:
|
63
|
+
# https://www.rubydoc.info/github/gimite/google-drive-ruby/GoogleDrive/Spreadsheet
|
64
|
+
def underlying_adapter_worksheet
|
65
|
+
@underlying_adapter_worksheet ||= Connector.new.read_worksheet(@spreadsheet_key, @worksheet_title)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
|
2
|
+
module SpreadsheetGoodies::GoogleDrive
|
3
|
+
|
4
|
+
# Accesses GoogleDrive and returns a SpreadsheetGoodies::GoogleDrive::Worksheet
|
5
|
+
def self.read_worksheet(spreadsheet_key:, worksheet_title:nil, num_header_rows:1)
|
6
|
+
Worksheet.new(spreadsheet_key, worksheet_title, num_header_rows)
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
# loads all files in google_drive folder
|
12
|
+
Dir[File.join(File.dirname(__FILE__), "google_drive/**/*.rb")].each { |f| require f }
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module SpreadsheetGoodies
|
2
|
+
|
3
|
+
|
4
|
+
# Sobrecarrega método [] da Array para permitir acessar células passando
|
5
|
+
# o título da coluna como índice. Ex: row['Data formal do pedido']
|
6
|
+
class Row < Array
|
7
|
+
attr_reader :header_row, :row_number, :parent_worksheet
|
8
|
+
|
9
|
+
def initialize(header_row, sheet_row_number, parent_worksheet, *args)
|
10
|
+
@header_row = header_row
|
11
|
+
@row_number = sheet_row_number
|
12
|
+
@parent_worksheet = parent_worksheet
|
13
|
+
super(args)
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](locator)
|
17
|
+
cell_index = (locator.is_a?(String) ? @header_row.index(locator) : locator)
|
18
|
+
|
19
|
+
# queries local cache only
|
20
|
+
super(cell_index)
|
21
|
+
end
|
22
|
+
|
23
|
+
def []=(locator, value)
|
24
|
+
cell_index = (locator.is_a?(String) ? @header_row.index(locator) : locator)
|
25
|
+
|
26
|
+
# propagates change to real worksheet
|
27
|
+
@parent_worksheet.write_to_cell(@row_number, cell_index+1, value)
|
28
|
+
|
29
|
+
# updates local cache
|
30
|
+
super(cell_index, value)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/spreadsheet_goodies.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
module SpreadsheetGoodies
|
2
|
+
|
3
|
+
end
|
4
|
+
|
1
5
|
require 'spreadsheet_goodies/version'
|
2
|
-
require 'spreadsheet_goodies/
|
3
|
-
require 'spreadsheet_goodies/
|
4
|
-
require 'spreadsheet_goodies/excel_worksheet'
|
6
|
+
require 'spreadsheet_goodies/google_drive'
|
7
|
+
require 'spreadsheet_goodies/excel'
|
5
8
|
require 'roo'
|
6
9
|
|
7
10
|
module SpreadsheetGoodies
|
@@ -31,4 +34,4 @@ module SpreadsheetGoodies
|
|
31
34
|
end
|
32
35
|
end
|
33
36
|
|
34
|
-
end
|
37
|
+
end
|
data/spreadsheet_goodies.gemspec
CHANGED
@@ -6,13 +6,13 @@ require 'spreadsheet_goodies/version'
|
|
6
6
|
Gem::Specification.new do |gemspec|
|
7
7
|
gemspec.name = 'spreadsheet_goodies'
|
8
8
|
gemspec.version = SpreadsheetGoodies::VERSION
|
9
|
-
gemspec.authors = ['Ricardo Jasinski']
|
10
|
-
gemspec.email = ['jasinski@solvis.com.br']
|
9
|
+
gemspec.authors = ['Ricardo Jasinski', 'Henrique Gubert']
|
10
|
+
gemspec.email = ['jasinski@solvis.com.br', 'guberthenrique@hotmail.com']
|
11
11
|
|
12
12
|
gemspec.summary = "SpreadsheetGoodies is a collection of tools to help work " +
|
13
|
-
"with Excel and Google Drive
|
13
|
+
"with Excel and Google Drive spreadsheets."
|
14
14
|
gemspec.description = "SpreadsheetGoodies is a collection of tools to help work " +
|
15
|
-
"with Excel and Google Drive
|
15
|
+
"with Excel and Google Drive spreadsheets. It relies " +
|
16
16
|
"on other gems to do the actual work of reading and writing to " +
|
17
17
|
"spreadsheet documents. It main features are:"
|
18
18
|
" * Read a spreadseet to an array of arrays, to allow accessing its data " +
|
@@ -38,4 +38,4 @@ Gem::Specification.new do |gemspec|
|
|
38
38
|
gemspec.add_dependency 'csv', '>= 3.0.0'
|
39
39
|
gemspec.add_dependency 'roo', '>= 1.13.2'
|
40
40
|
|
41
|
-
end
|
41
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spreadsheet_goodies
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ricardo Jasinski
|
8
|
+
- Henrique Gubert
|
8
9
|
autorequire:
|
9
10
|
bindir: exe
|
10
11
|
cert_chain: []
|
11
|
-
date: 2018-
|
12
|
+
date: 2018-08-17 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: bundler
|
@@ -109,10 +110,11 @@ dependencies:
|
|
109
110
|
- !ruby/object:Gem::Version
|
110
111
|
version: 1.13.2
|
111
112
|
description: 'SpreadsheetGoodies is a collection of tools to help work with Excel
|
112
|
-
and Google Drive
|
113
|
+
and Google Drive spreadsheets. It relies on other gems to do the actual work of
|
113
114
|
reading and writing to spreadsheet documents. It main features are:'
|
114
115
|
email:
|
115
116
|
- jasinski@solvis.com.br
|
117
|
+
- guberthenrique@hotmail.com
|
116
118
|
executables: []
|
117
119
|
extensions: []
|
118
120
|
extra_rdoc_files: []
|
@@ -124,7 +126,6 @@ files:
|
|
124
126
|
- ".travis.yml"
|
125
127
|
- Gemfile
|
126
128
|
- LICENSE
|
127
|
-
- LICENSE.txt
|
128
129
|
- README.md
|
129
130
|
- Rakefile
|
130
131
|
- bin/console
|
@@ -132,13 +133,16 @@ files:
|
|
132
133
|
- builds/spreadsheet_goodies-0.0.1.gem
|
133
134
|
- builds/spreadsheet_goodies-0.0.2.gem
|
134
135
|
- builds/spreadsheet_goodies-0.0.3.gem
|
136
|
+
- builds/spreadsheet_goodies-0.0.4.gem
|
135
137
|
- lib/spreadsheet_goodies.rb
|
136
|
-
- lib/spreadsheet_goodies/
|
137
|
-
- lib/spreadsheet_goodies/
|
138
|
-
- lib/spreadsheet_goodies/
|
139
|
-
- lib/spreadsheet_goodies/
|
140
|
-
- lib/spreadsheet_goodies/
|
141
|
-
- lib/spreadsheet_goodies/
|
138
|
+
- lib/spreadsheet_goodies/abstract_base_worksheet.rb
|
139
|
+
- lib/spreadsheet_goodies/excel.rb
|
140
|
+
- lib/spreadsheet_goodies/excel/workbook_builder.rb
|
141
|
+
- lib/spreadsheet_goodies/excel/worksheet.rb
|
142
|
+
- lib/spreadsheet_goodies/google_drive.rb
|
143
|
+
- lib/spreadsheet_goodies/google_drive/connector.rb
|
144
|
+
- lib/spreadsheet_goodies/google_drive/worksheet.rb
|
145
|
+
- lib/spreadsheet_goodies/row.rb
|
142
146
|
- lib/spreadsheet_goodies/version.rb
|
143
147
|
- spreadsheet_goodies.gemspec
|
144
148
|
homepage: https://github.com/ricardo-jasinski/spreadsheet_goodies
|
@@ -162,9 +166,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
162
166
|
version: '0'
|
163
167
|
requirements: []
|
164
168
|
rubyforge_project:
|
165
|
-
rubygems_version: 2.6.
|
169
|
+
rubygems_version: 2.6.8
|
166
170
|
signing_key:
|
167
171
|
specification_version: 4
|
168
172
|
summary: SpreadsheetGoodies is a collection of tools to help work with Excel and Google
|
169
|
-
Drive
|
173
|
+
Drive spreadsheets.
|
170
174
|
test_files: []
|
data/LICENSE.txt
DELETED
@@ -1,24 +0,0 @@
|
|
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>
|
@@ -1,78 +0,0 @@
|
|
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
|
@@ -1,68 +0,0 @@
|
|
1
|
-
require_relative 'excel_worksheet_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::ExcelWorksheet
|
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 ExcelWorksheetRow
|
34
|
-
@rows = rows.collect.with_index do |row, index|
|
35
|
-
ExcelWorksheetRow.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 << ExcelWorksheetRow.new(@header_row, last_row_number+1, *row_data)
|
62
|
-
end
|
63
|
-
|
64
|
-
def spreadsheet
|
65
|
-
@workbook
|
66
|
-
end
|
67
|
-
|
68
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
class SpreadsheetGoodies::ExcelWorksheet
|
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 ::ExcelWorksheetRow < 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
|
@@ -1,93 +0,0 @@
|
|
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
|
@@ -1,68 +0,0 @@
|
|
1
|
-
require_relative 'google_drive_worksheet_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::GoogleDriveWorksheet
|
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
|
-
|
29
|
-
if SpreadsheetGoodies.configuration.strip_values_on_read
|
30
|
-
rows = rows.map do |row|
|
31
|
-
row.map {|element| element.try(:strip) }
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
@header_row = rows[@num_header_rows-1]
|
36
|
-
@rows = rows.collect.with_index do |row, index|
|
37
|
-
GoogleDriveWorksheetRow.new(@header_row, index+1, *row)
|
38
|
-
end
|
39
|
-
|
40
|
-
self
|
41
|
-
end
|
42
|
-
|
43
|
-
def [](index)
|
44
|
-
@rows[index]
|
45
|
-
end
|
46
|
-
|
47
|
-
# Return only the rows that contain data (excludes the header rows)
|
48
|
-
def data_rows
|
49
|
-
@rows[@num_header_rows..-1]
|
50
|
-
end
|
51
|
-
|
52
|
-
# Finds and returns the first row that contains cell_value at the given column
|
53
|
-
def find_row_by_column_value(column_title, cell_value)
|
54
|
-
data_rows.each do |row|
|
55
|
-
return row if row[column_title] == cell_value
|
56
|
-
end
|
57
|
-
|
58
|
-
nil
|
59
|
-
end
|
60
|
-
|
61
|
-
# Reads a GoogleDrive::Spreadsheet object from Google Drive. The sheet object
|
62
|
-
# can be used to write data to the online spreadhseet, as we don't yet provide
|
63
|
-
# the helper methods for our users to do it via our public interface.
|
64
|
-
def read_worksheet
|
65
|
-
connector = SpreadsheetGoodies::GoogleDriveConnector.new
|
66
|
-
connector.read_worksheet(@spreadsheet_key, @worksheet_title)
|
67
|
-
end
|
68
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
class SpreadsheetGoodies::GoogleDriveWorksheet
|
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 ::GoogleDriveWorksheetRow < 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
|