spreadsheet_goodies 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0ac665483c2a857085d25a1237cbab883b2b12c3
4
- data.tar.gz: 01d5aeed49efd551611bfc80981258a9d4346dcf
3
+ metadata.gz: 1cb1da443d319f91e093023601d1688081db8344
4
+ data.tar.gz: 10d6c084df8ae3da6ba1a1322805750d52191a3d
5
5
  SHA512:
6
- metadata.gz: f963fcdda1e06d423fdb49584e9d19e7b8507eb2a20bbebd0ca4716b01b6aef9134594819aede023b1dfd044cbb93e48209ef1e173f8a52e5b5bd0e281d0d0f7
7
- data.tar.gz: 2c03a6bb43be7e8794ea8d03e8f7c23b4a226b16b6d777d1aff7309526a2467d077d7a2bee5e5463fdd8dea78750940d1948125dc2e91311d0113fe3d244ad37
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
- spreadsheet_key = '1UC43X6aZwlWPCnn...', '~> 0'#, '~> 1.13.2'
33
- sheet = SpreadsheetGoodies::GoogleDriveWorksheet.new(spreadsheet_key, 'Relação Lojas').read_from_google_drive
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::ExcelWorksheet.new('~/workbook.xlsx')
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
-
@@ -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
@@ -1,3 +1,3 @@
1
1
  module SpreadsheetGoodies
2
- VERSION = '0.0.4'
2
+ VERSION = '0.0.5'
3
3
  end
@@ -1,7 +1,10 @@
1
+ module SpreadsheetGoodies
2
+
3
+ end
4
+
1
5
  require 'spreadsheet_goodies/version'
2
- require 'spreadsheet_goodies/google_drive_worksheet'
3
- require 'spreadsheet_goodies/google_drive_connector'
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
@@ -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 spreadhseets."
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 spreadhseets. It relies " +
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
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-07-09 00:00:00.000000000 Z
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 spreadhseets. It relies on other gems to do the actual work of
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/excel_workbook_builder.rb
137
- - lib/spreadsheet_goodies/excel_worksheet.rb
138
- - lib/spreadsheet_goodies/excel_worksheet_row.rb
139
- - lib/spreadsheet_goodies/google_drive_connector.rb
140
- - lib/spreadsheet_goodies/google_drive_worksheet.rb
141
- - lib/spreadsheet_goodies/google_drive_worksheet_row.rb
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.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 spreadhseets.
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