Add support for Airtable

This commit is contained in:
2020-04-13 22:59:46 +02:00
parent 80fe5f4220
commit edf6884647
12 changed files with 179 additions and 60 deletions

View File

@@ -1,15 +1,21 @@
require 'google/apis/sheets_v4'
class Form < ApplicationRecord
# Hash to translate an index to a A1 notation. e.g. 1 => 'B', 27 => 'AA'
COLUMN_INDEX_TO_LETTER = Hash.new {|hash,key| hash[key] = hash[key - 1].next }.merge({0 => "A"})
belongs_to :user
has_many :submissions, dependent: :destroy
before_validation :insert_defaults, on: :create
after_create :create_spreadsheet
has_secure_token
encrypts :airtable_api_key
encrypts :airtable_app_key
validates_presence_of :title
validates_inclusion_of :backend_name, in: ['google_sheets', 'airtable']
# Airtable validations
validates_presence_of :airtable_api_key, if: :airtable?
validates_presence_of :airtable_app_key, if: :airtable?
validates_presence_of :airtable_table, if: :airtable?
# TODO: use counter_cache option on association
def submissions_count
@@ -28,63 +34,39 @@ class Form < ApplicationRecord
self.user.active?
end
def google_spreadsheet_url
"https://docs.google.com/spreadsheets/d/#{google_spreadsheet_id}/edit" if google_spreadsheet_id.present?
def airtable?
backend_name == 'airtable'
end
def google
backend_name == 'google'
end
def backend
@backend ||= SpreadsheetBackends.const_get(backend_name.camelize).new(self)
end
def spreadsheet_url
backend.url
end
def create_spreadsheet
sheets = Google::Apis::SheetsV4::SheetsService.new
sheets.authorization = user.google_authorization
create_object = Google::Apis::SheetsV4::Spreadsheet.new(properties: { title: title})
spreadsheet = sheets.create_spreadsheet(create_object)
update(google_spreadsheet_id: spreadsheet.spreadsheet_id)
backend.create
end
def spreadsheet_service
@spreadsheet_service ||= Google::Apis::SheetsV4::SheetsService.new.tap do |s|
s.authorization = user.google_authorization
end
def append_to_spreadsheet(data)
backend.append(data)
end
def header_values
@header_values ||= begin
values = spreadsheet_service.get_spreadsheet_values(google_spreadsheet_id, 'A1:An').values
# if there are no headers yet, return an empty array
if values
values[0].map(&:strip)
else
[]
end
end
end
def append(data)
data = data.with_indifferent_access
check_spreadsheed_headers!(data)
values = header_values.map { |key| data[key] }
range = "A1:A#{COLUMN_INDEX_TO_LETTER[values.length]}1"
value_range = Google::Apis::SheetsV4::ValueRange.new(values: [values], major_dimension: 'ROWS')
spreadsheet_service.append_spreadsheet_value(google_spreadsheet_id, range, value_range, value_input_option: 'USER_ENTERED')
end
def check_spreadsheed_headers!(data)
missing_headers = data.keys.map { |k| k.to_s.strip } - header_values
append_missing_headers(missing_headers) unless missing_headers.empty?
end
def append_missing_headers(missing_headers)
start_column = COLUMN_INDEX_TO_LETTER[header_values.length]
end_column = COLUMN_INDEX_TO_LETTER[header_values.length + missing_headers.length]
range = "#{start_column}1:#{end_column}1"
value_range = Google::Apis::SheetsV4::ValueRange.new(values: [missing_headers], major_dimension: 'ROWS')
spreadsheet_service.update_spreadsheet_value(google_spreadsheet_id, range, value_range, value_input_option: 'USER_ENTERED')
@header_values = nil # reset header values to refresh memoization on next access
def spreadsheet_headers
backend.headers
end
def to_param
token
end
def insert_defaults
self.backend_name ||= airtable_app_key.present? ? 'airtable' : 'google_sheets'
end
end

View File

@@ -44,7 +44,7 @@ class Submission < ApplicationRecord
end
def append_to_spreadsheet
result = form.append(data)
update_column(:appended_at, Time.current) if result.updates.updated_rows > 0
form.append_to_spreadsheet(data) &&
update_column(:appended_at, Time.current)
end
end