tofulcrum 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
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: