Add support for Airtable
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
<div class="media-right has-text-right is-size-4">
|
||||
<figure class="image is-48x48 ">
|
||||
<%= link_to form.google_spreadsheet_url, {class: "has-text-success", target: "__blank"} do %>
|
||||
<%= link_to form.spreadsheet_url, {class: "has-text-success", target: "__blank"} do %>
|
||||
<i class="far fa-file-excel"></i>
|
||||
<% end %>
|
||||
</figure>
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
<%= submission_url(@form) %>
|
||||
</p>
|
||||
<p>
|
||||
<%= link_to 'Google spreadsheet', @form.google_spreadsheet_url %>
|
||||
<%= link_to 'Google spreadsheet', @form.spreadsheet_url %>
|
||||
</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<% @form.header_values.each do |value| %>
|
||||
<% @form.spreadsheet_headers.each do |value| %>
|
||||
<th><%= value %></th>
|
||||
<% end %>
|
||||
</tr>
|
||||
@@ -16,7 +16,7 @@
|
||||
<tbody>
|
||||
<% @submissions.each do |submission| %>
|
||||
<tr>
|
||||
<% @form.header_values.each do |column| %>
|
||||
<% @form.spreadsheet_headers.each do |column| %>
|
||||
<td><%= submission.data[column] %></td>
|
||||
<% end %>
|
||||
</tr>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<input type="hidden" name="ID" value="tinyforms_id">
|
||||
<h3>Demo</h3>
|
||||
<p>
|
||||
This short form is connected to the embedded <%= link_to 'document', DEMO_FORM.google_spreadsheet_url %> on the right. <br>
|
||||
This short form is connected to the embedded <%= link_to 'document', DEMO_FORM.spreadsheet_url %> on the right. <br>
|
||||
Submit the form and see the update of the document in realtime.
|
||||
</p>
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
|
||||
<div class="column">
|
||||
<iframe id="demo-sheet" style="width:100%;height:100%;" src="https://docs.google.com/spreadsheets/d/<%= DEMO_FORM.google_spreadsheet_id %>/edit?usp=sharing&nocache=<%= Time.now.to_i %>#gid=0&range=<%= DEMO_FORM.submissions.count + 1 %>:<%= DEMO_FORM.submissions.count + 2 %>"></iframe>
|
||||
<p class="has-text-grey has-text-centered">Document not loading? <%= link_to 'Open it here', DEMO_FORM.google_spreadsheet_url %>.</p>
|
||||
<p class="has-text-grey has-text-centered">Document not loading? <%= link_to 'Open it here', DEMO_FORM.spreadsheet_url %>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user