tofulcrum 0.0.8 → 0.0.9

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 CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- MGFiOTNkNTQ2YmJmYzQ4OTQ4YzI4NDdkNWRiM2EwNTk0ZDgyZjFlOA==
5
- data.tar.gz: !binary |-
6
- Y2E2NjE2MmZhNjM4NTM0OTgyMDZlYzYzODU4ZjZjMTdjM2M1OThhYg==
2
+ SHA1:
3
+ metadata.gz: a68e8c5c982398ee78373d7dd468a82beaf4a895
4
+ data.tar.gz: 2c14b9b22084c4c8b13e62f5999040d18fa3c742
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- NzZkNjlkOTJiYWIxYThhNTVlZGVkMDkwZDg0ZjFmMGIyNjQzMmE2ZWMxMmY1
10
- OWUxNjI1ZTkxYWI0NWM2NGEyNzU1MzM0ZGNhZGJkYTJjOGQwYzUyZTNjMmJl
11
- MTMwOTg3NmViYWU3NmM2ZDEyZGJkYTBjMTIyMDYxN2E4ZWU2NzQ=
12
- data.tar.gz: !binary |-
13
- MDU3ZGZlMjNmMDg5NDJkMzZlNGVjZjdkOTcyMGZjMmMxYWNiNzkzZTc4NDVl
14
- MjJhNGYyMWRiMTIyM2QyMDBjMjc4MDQ2MGI5MTRmMGZiNmY3N2QzZjU2NWU4
15
- MzBkYjZlYTVjMmM2NDg5MThjMmU4M2QwNWU2Yjk2MTAxMDI0MDg=
6
+ metadata.gz: cddb2b6b3d5950c8892f2ac2cf6ff9a44aa7325a98bc253954110eb18bc0d93a49c9b214c19e5259a285afdd271896b95c3aba4205e39ba495d29a647ce0ea69
7
+ data.tar.gz: f4e77446631aae67b4130fe920a720dddad74bd54e70ca66cd8787db01a2abd22cce43e8ba77eed42b8644d7d8eefa72a8a934df7ab37b2e8cbd37e80087fc69
@@ -1,15 +1,31 @@
1
+ require 'bundler/setup'
2
+ require 'rubygems'
1
3
  require "tofulcrum/version"
2
4
  require 'csv'
3
5
  require 'fulcrum'
4
6
  require 'thor'
7
+ require 'json'
8
+ require 'roo'
9
+ require 'axlsx'
10
+ require 'securerandom'
11
+
12
+ API_URL = 'https://api.fulcrumapp.com'
13
+
14
+ require 'tofulcrum/fulcrum-xls-form/xls_reader'
15
+ require 'tofulcrum/fulcrum-xls-form/xls_writer'
5
16
 
6
17
  module Tofulcrum
7
18
  class CLI < Thor
19
+ desc "xls", "Convert an Excel spreadsheet to a Fulcrum form"
20
+ def xls(file)
21
+ Fulcrum::XlsReader.read(file)
22
+ end
23
+
8
24
  desc "import", "Import a CSV into a Fulcrum app"
9
25
  def import(file, form_id, api_key, mapping=nil)
10
26
 
11
27
  Fulcrum::Api.configure do |config|
12
- config.uri = 'https://api.fulcrumapp.com/api/v2'
28
+ config.uri = API_URL
13
29
  config.key = api_key
14
30
  end
15
31
 
@@ -0,0 +1,228 @@
1
+ module Fulcrum
2
+ class XlsReader
3
+ VALID_ROW_TYPES = [
4
+ 'section begin',
5
+ 'section end',
6
+ 'text',
7
+ 'choice',
8
+ 'numeric',
9
+ 'repeatable begin',
10
+ 'repeatable end',
11
+ 'date',
12
+ 'address',
13
+ 'signature',
14
+ 'photos'
15
+ ]
16
+
17
+ def self.read(file)
18
+ spreadsheet = Roo::Spreadsheet.open(file)
19
+
20
+ parse_metadata(spreadsheet)
21
+ parse_status_field(spreadsheet)
22
+ parse_choices(spreadsheet)
23
+ parse_schema(spreadsheet)
24
+ end
25
+
26
+ def self.parse_schema(spreadsheet)
27
+ form = { form: { name: @metadata['name'],
28
+ description: @metadata['description'],
29
+ status_field: @status_field,
30
+ elements: make_elements(make_hash(spreadsheet.sheet('form'))) } }
31
+
32
+ puts form.to_json
33
+ end
34
+
35
+ def self.make_elements(rows)
36
+ root = { elements: [] }
37
+
38
+ containers = [ root ]
39
+
40
+ current_container = root
41
+
42
+ rows.each do |row|
43
+ row = row.with_indifferent_access
44
+
45
+ row_type = row['type'].to_s.downcase.strip
46
+
47
+ raise "Invalid row type #{row_type}." unless VALID_ROW_TYPES.include?(row_type)
48
+
49
+ if !['repeatable end', 'section end'].include?(row_type)
50
+ element = make_element(row, current_container)
51
+ current_container[:elements] << element
52
+ end
53
+
54
+ case row_type
55
+ when 'section begin'
56
+ containers << element
57
+ current_container = element
58
+
59
+ when 'section end'
60
+ raise "section end found without matching begin" if containers.count == 1
61
+
62
+ containers = containers[0..-2]
63
+ current_container = containers.last
64
+
65
+ when 'repeatable begin'
66
+ containers << element
67
+ current_container = element
68
+
69
+ when 'repeatable end'
70
+ raise "repeatable end found without matching begin" if containers.count == 1
71
+
72
+ containers = containers[0..-2]
73
+ current_container = containers.last
74
+
75
+ end
76
+ end
77
+
78
+ raise "Unmatched sections or repeatables found. The open fields are: #{containers.map {|c| c[:label]}.join(', ')}" if containers.count != 1
79
+
80
+ root[:elements]
81
+ end
82
+
83
+ def self.make_element(row, container)
84
+ {}.tap do |hash|
85
+ hash[:type] = element_type_for_type(row[:type])
86
+ hash[:label] = row[:label]
87
+ hash[:data_name] = make_data_name(row, container)
88
+ hash[:description] = row[:description]
89
+ hash[:required] = boolean_value(row[:required])
90
+ hash[:hidden] = boolean_value(row[:hidden])
91
+ hash[:disabled] = boolean_value(row[:disabled])
92
+ hash[:numeric] = true if row[:type] == 'numeric'
93
+ hash[:elements] = [] if %w(Section Repeatable).include?(hash[:type])
94
+ hash[:key] = SecureRandom.hex(2)
95
+
96
+ if hash[:type] == 'ChoiceField'
97
+ hash[:choices] = @choices[row['choices']] if hash[:type] == 'ChoiceField'
98
+ hash[:allow_other] = boolean_value(row[:allow_other])
99
+ end
100
+ end
101
+ end
102
+
103
+ def self.make_data_name(row, container)
104
+ return row[:data_name] if row[:data_name]
105
+
106
+ if container[:data_name].present?
107
+ "#{container[:data_name]}_#{row[:label].to_s.downcase.parameterize.underscore}"
108
+ else
109
+ row[:label].to_s.downcase.parameterize.underscore
110
+ end
111
+ end
112
+
113
+ def self.element_type_for_type(type)
114
+ case type
115
+ when 'section begin' then 'Section'
116
+ when 'text' then 'TextField'
117
+ when 'choice' then 'ChoiceField'
118
+ when 'numeric' then 'TextField'
119
+ when 'repeatable begin' then 'Repeatable'
120
+ when 'date' then 'DateTimeField'
121
+ when 'address' then 'AddressField'
122
+ when 'signature' then 'SignatureField'
123
+ when 'photos' then 'PhotoField'
124
+ else nil
125
+ end
126
+ end
127
+
128
+ def self.parse_metadata(spreadsheet)
129
+ sheet = spreadsheet.sheet('metadata')
130
+
131
+ raise "Can't find metadata sheet." unless sheet
132
+
133
+ metadata = make_hash(sheet)
134
+
135
+ @metadata = {}
136
+
137
+ metadata.each do |row|
138
+ @metadata[row['key']] = row['value']
139
+ end
140
+ end
141
+
142
+ def self.parse_choices(spreadsheet)
143
+ sheet = spreadsheet.sheet('choices')
144
+
145
+ return unless sheet
146
+
147
+ all_choices = make_hash(sheet)
148
+
149
+ @choices = {}
150
+
151
+ all_choices.each do |choice|
152
+ @choices[choice['list']] ||= []
153
+ @choices[choice['list']] << choice.slice('label', 'value')
154
+ end
155
+ end
156
+
157
+ def self.parse_status_field(spreadsheet)
158
+ sheet = spreadsheet.sheet('status')
159
+
160
+ return unless sheet
161
+
162
+ statuses = make_hash(sheet)
163
+
164
+ @status_field = {}
165
+ @status_field[:label] = @metadata['status_label']
166
+ @status_field[:description] = @metadata['status_description']
167
+ @status_field[:data_name] = @metadata['status_data_name']
168
+ @status_field[:default_value] = @metadata['status_default_value']
169
+ @status_field[:hidden] = boolean_value(@metadata['status_hidden'])
170
+ @status_field[:read_only] = boolean_value(@metadata['status_read_only'])
171
+ @status_field[:enabled] = boolean_value(@metadata['status_enabled'])
172
+
173
+ statuses.each do |status|
174
+ @status_field[:choices] ||= []
175
+ @status_field[:choices] << { label: status['label'] || '',
176
+ value: status['value'] || status['label'] || '',
177
+ color: status_color(status['color']) }
178
+ end
179
+ end
180
+
181
+ def self.boolean_value(value, default=false)
182
+ return default if value.nil?
183
+ return %(true yes 1).include?(value.to_s.downcase.strip)
184
+ end
185
+
186
+ def self.status_color(human_name)
187
+ case human_name.to_s.strip.downcase
188
+ when 'black' then '#242424'
189
+ when 'gray' then '#B3B3B3'
190
+ when 'white' then '#FFFFFF'
191
+ when 'brown' then '#704B10'
192
+ when 'pink' then '#DA0796'
193
+ when 'red' then '#CB0D0C'
194
+ when 'orange' then '#FF8819'
195
+ when 'yellow' then '#FFD300'
196
+ when 'green' then '#87D30F'
197
+ when 'dark green' then '#2D5D00'
198
+ when 'dark blue' then '#294184'
199
+ when 'blue' then '#1891C9'
200
+ else '#CB0D0C'
201
+ end
202
+ end
203
+
204
+ def self.make_hash(sheet)
205
+ column_names = []
206
+
207
+ sheet.row(1).to_a.each_with_index do |column_name, index|
208
+ column_names << {name: column_name, index: index} if column_name.present?
209
+ end
210
+
211
+ rows = []
212
+
213
+ sheet.each_with_index do |item, index|
214
+ next if index == 0
215
+
216
+ hash = {}
217
+
218
+ column_names.each do |column|
219
+ hash[column[:name]] = item[column[:index]]
220
+ end
221
+
222
+ rows << hash
223
+ end
224
+
225
+ rows
226
+ end
227
+ end
228
+ end
@@ -1,3 +1,3 @@
1
1
  module Tofulcrum
2
- VERSION = "0.0.8"
2
+ VERSION = "0.0.9"
3
3
  end
@@ -18,8 +18,11 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.add_dependency "faraday", "~> 0.7.6"
21
22
  spec.add_dependency "thor"
22
23
  spec.add_dependency "fulcrum"
24
+ spec.add_dependency "axlsx"
25
+ spec.add_dependency "roo"
23
26
 
24
27
  spec.add_development_dependency "bundler", "~> 1.3"
25
28
  spec.add_development_dependency "rake"
metadata CHANGED
@@ -1,69 +1,111 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tofulcrum
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zac McCormick
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-25 00:00:00.000000000 Z
11
+ date: 2014-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.7.6
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.7.6
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: thor
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
- - - ! '>='
31
+ - - ">="
18
32
  - !ruby/object:Gem::Version
19
33
  version: '0'
20
34
  type: :runtime
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
24
- - - ! '>='
38
+ - - ">="
25
39
  - !ruby/object:Gem::Version
26
40
  version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: fulcrum
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
- - - ! '>='
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: axlsx
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: roo
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
32
74
  - !ruby/object:Gem::Version
33
75
  version: '0'
34
76
  type: :runtime
35
77
  prerelease: false
36
78
  version_requirements: !ruby/object:Gem::Requirement
37
79
  requirements:
38
- - - ! '>='
80
+ - - ">="
39
81
  - !ruby/object:Gem::Version
40
82
  version: '0'
41
83
  - !ruby/object:Gem::Dependency
42
84
  name: bundler
43
85
  requirement: !ruby/object:Gem::Requirement
44
86
  requirements:
45
- - - ~>
87
+ - - "~>"
46
88
  - !ruby/object:Gem::Version
47
89
  version: '1.3'
48
90
  type: :development
49
91
  prerelease: false
50
92
  version_requirements: !ruby/object:Gem::Requirement
51
93
  requirements:
52
- - - ~>
94
+ - - "~>"
53
95
  - !ruby/object:Gem::Version
54
96
  version: '1.3'
55
97
  - !ruby/object:Gem::Dependency
56
98
  name: rake
57
99
  requirement: !ruby/object:Gem::Requirement
58
100
  requirements:
59
- - - ! '>='
101
+ - - ">="
60
102
  - !ruby/object:Gem::Version
61
103
  version: '0'
62
104
  type: :development
63
105
  prerelease: false
64
106
  version_requirements: !ruby/object:Gem::Requirement
65
107
  requirements:
66
- - - ! '>='
108
+ - - ">="
67
109
  - !ruby/object:Gem::Version
68
110
  version: '0'
69
111
  description: Convert data to Fulcrum
@@ -74,13 +116,15 @@ executables:
74
116
  extensions: []
75
117
  extra_rdoc_files: []
76
118
  files:
77
- - .gitignore
119
+ - ".gitignore"
78
120
  - Gemfile
79
121
  - LICENSE.txt
80
122
  - README.md
81
123
  - Rakefile
82
124
  - bin/tofulcrum
83
125
  - lib/tofulcrum.rb
126
+ - lib/tofulcrum/fulcrum-xls-form/xls_reader.rb
127
+ - lib/tofulcrum/fulcrum-xls-form/xls_writer.rb
84
128
  - lib/tofulcrum/version.rb
85
129
  - tofulcrum.gemspec
86
130
  homepage: https://github.com/zhm/tofulcrum
@@ -93,19 +137,18 @@ require_paths:
93
137
  - lib
94
138
  required_ruby_version: !ruby/object:Gem::Requirement
95
139
  requirements:
96
- - - ! '>='
140
+ - - ">="
97
141
  - !ruby/object:Gem::Version
98
142
  version: '0'
99
143
  required_rubygems_version: !ruby/object:Gem::Requirement
100
144
  requirements:
101
- - - ! '>='
145
+ - - ">="
102
146
  - !ruby/object:Gem::Version
103
147
  version: '0'
104
148
  requirements: []
105
149
  rubyforge_project:
106
- rubygems_version: 2.1.5
150
+ rubygems_version: 2.2.1
107
151
  signing_key:
108
152
  specification_version: 4
109
153
  summary: Import data into Fulcrum from a CSV
110
154
  test_files: []
111
- has_rdoc: