cloudfactory 0.1.12 → 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,2 +1,2 @@
1
- Company,Website,meta_data_company
2
- Apple,apple.com,Apple
1
+ Company,Website
2
+ Apple,apple.com
data/lib/cf.rb CHANGED
@@ -90,4 +90,5 @@ require "#{directory}/cf/custom_task_form"
90
90
  require "#{directory}/cf/run"
91
91
  require "#{directory}/cf/department"
92
92
  require "#{directory}/cf/final_output"
93
- require "#{directory}/cf/robot_worker"
93
+ require "#{directory}/cf/robot_worker"
94
+ require "#{directory}/cf/version"
@@ -18,6 +18,7 @@ require "#{cli_directory}/config"
18
18
  require "#{cli_directory}/line"
19
19
  require "#{cli_directory}/form"
20
20
  require "#{cli_directory}/production"
21
+ require "#{cli_directory}/line_yaml_validator"
21
22
 
22
23
  if ENV['TEST']
23
24
  require 'ruby-debug'
@@ -28,6 +29,8 @@ module Cf # :nodoc: all
28
29
  include Thor::Actions
29
30
  include Cf::Config
30
31
 
32
+ map "-v" => :version
33
+
31
34
  desc "login", "Setup the cloudfactory credentials"
32
35
  def login
33
36
  email = ask("Enter your email:")
@@ -128,5 +131,9 @@ module Cf # :nodoc: all
128
131
  end
129
132
  end
130
133
 
134
+ desc "version", "Shows the current version of cloudfactory gem"
135
+ def version
136
+ say("Version: #{CF::VERSION}")
137
+ end
131
138
  end
132
139
  end
@@ -100,7 +100,15 @@ module Cf # :nodoc: all
100
100
  say "The line.yml file does not exist in this directory", :red
101
101
  return
102
102
  end
103
-
103
+
104
+ errors = LineYamlValidator.validate(yaml_source)
105
+
106
+ if errors.present?
107
+ say("Invalid line.yml file. Correct its structure as per the errors shown below.", :red)
108
+ errors.each {|error| say(" #{error}", :cyan)}
109
+ exit(1)
110
+ end
111
+
104
112
  set_target_uri(false)
105
113
  set_api_key(yaml_source)
106
114
 
@@ -0,0 +1,183 @@
1
+ module Cf
2
+ class LineYamlValidator
3
+
4
+ def self.validate(yaml_path)
5
+ line_dump = YAML::load(File.read(yaml_path))
6
+ errors = []
7
+ # Checking Department
8
+ if line_dump['department'].nil?
9
+ errors << "The line Department is missing!"
10
+ end
11
+
12
+ # Checking Input Formats
13
+ input_formats = line_dump['input_formats']
14
+ if input_formats.nil?
15
+ errors << "The Input Format is missing!"
16
+ else
17
+ if input_formats.class == Array
18
+ input_formats.each_with_index do |input_format, index|
19
+ name = input_format['name']
20
+ required = input_format['required']
21
+ valid_type = input_format['valid_type']
22
+ if name.nil?
23
+ errors << "Input Format name is missing in Block #{index+1}!"
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ # Checking Stations
30
+ stations = line_dump['stations']
31
+ if stations.nil?
32
+ errors << "Station is missing!"
33
+ else
34
+ if stations.class == Array
35
+ if stations.first['station'].nil?
36
+ errors << "Station Settings missing!"
37
+ else
38
+ stations.each_with_index do |station, i|
39
+ station_index = station['station']['station_index']
40
+ errors << "Station Index is missing in Block station #{i+1}!" if station_index.nil?
41
+
42
+ station_type = station['station']['station_type']
43
+ errors << "Station type is missing in Block station #{i+1}!" if station_type.nil?
44
+
45
+ if station_type == "tournament"
46
+ jury_worker = station['station']['jury_worker']
47
+ if jury_worker.nil?
48
+ errors << "Jury worker setting is missing in Block station #{i+1}!"
49
+ elsif !jury_worker.nil?
50
+ reward = jury_worker['reward']
51
+ errors << "Reward for worker is missing in Block station #{i+1}!" if reward.nil?
52
+ errors << "Reward Must be greater than 0 in Block station #{i+1}!" if !reward.nil? && reward < 1
53
+ end
54
+ end
55
+ # Checking Worker
56
+ worker = station['station']['worker']
57
+ if worker.class != Hash
58
+ errors << "Worker is missing in Block station #{i+1}!"
59
+ elsif worker.class == Hash
60
+ # Checking Worker type
61
+ worker.each_pair do |k, v|
62
+ errors << "Should not be an array" if v.class == Array && k != "skill_badges"
63
+ end
64
+ worker_type = worker['worker_type']
65
+ if worker_type.nil?
66
+ errors << "Worker Type is missing!"
67
+ else
68
+ if worker_type != "human"
69
+ errors << "Worker type is invalid in Block station #{i+1}!" if worker_type.split("_").last != "robot"
70
+ if worker_type.split("_").last == "robot"
71
+ settings = worker['settings']
72
+ errors << "Settings for the robot worker is missingin Block station #{i+1}!" if settings.nil?
73
+ end
74
+ elsif worker_type == "human"
75
+ # Checking number of workers if worker_type == "human"
76
+ num_workers = worker['num_workers']
77
+ if num_workers.nil?
78
+ errors << "Number of workers not specified in Block station #{i+1}!"
79
+ else
80
+ errors << "Number of workers must be greater than 0 in Block station #{i+1}!" if num_workers < 1
81
+ end
82
+
83
+ # Checking reward of workers if worker_type == "human"
84
+ reward = worker['reward']
85
+ if reward.nil?
86
+ errors << "Reward of workers not specified in Block station #{i+1}!"
87
+ else
88
+ errors << "Reward of workers must be greater than 0 in Block station #{i+1}!" if reward < 1
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ # Checking Stat_badge
95
+ stat_badge = station['station']['worker']['stat_badge']
96
+ if !stat_badge.nil?
97
+ errors << "Stat badge setting is invalid in Block station #{i+1}!" if stat_badge.class != Hash
98
+ end
99
+
100
+ # Checking skill_badge
101
+ skill_badges = station['station']['worker']['skill_badges']
102
+ if !skill_badges.nil?
103
+ errors << "Skill badge settings is invalid in Block station #{i+1}!" if skill_badges.class != Array
104
+ skill_badges.each_with_index do |badge, badge_index|
105
+ badge_title = badge['title']
106
+ badge_description = badge['description']
107
+ max_badges = badge['max_badges']
108
+ badge_test = badge['test']
109
+ test_input = badge_test['input'] if badge_test.class == Hash
110
+ expected_output = badge_test['expected_output'] if badge_test.class == Hash
111
+ errors << "Skill badge title is Missing in Block #{badge_index+1}!" if badge_title.nil?
112
+ errors << "Skill badge Description is Missing in Block #{badge_index+1}!" if badge_description.nil?
113
+ errors << "Skill badge max_badges must be greater than 1000 in Block #{badge_index+1}!" if max_badges < 1000 && !max_badges.nil?
114
+ errors << "Skill badge Test is Missing in Block #{badge_index+1}!" if badge_test.nil?
115
+ errors << "Skill badge Test is Invalid (must be Hash) in Block #{badge_index+1}!" if badge_test.class != Hash && !badge_test.nil?
116
+ errors << "Skill badge Test input is Missing in Block #{badge_index+1}!" if test_input.nil? && !badge_test.nil?
117
+ errors << "Skill badge Test input is Invalid (must be Hash) in Block #{badge_index+1}!" if test_input.class != Hash && !test_input.nil? && !badge_test.nil?
118
+ errors << "Skill badge Test expected_output is Missing in Block #{badge_index+1}!" if expected_output.nil? && !badge_test.nil?
119
+ errors << "Skill badge Test expected_output is Invalid (must be an array) in Block #{badge_index+1}!" if expected_output.class != Array && !expected_output.nil? && !badge_test.nil?
120
+ end
121
+ end
122
+
123
+ # Checking TaskForm
124
+ if worker_type == "human"
125
+ task_form = station['station']['task_form']
126
+ if task_form.nil?
127
+ custom_task_form = station['station']['custom_task_form']
128
+ if custom_task_form.nil?
129
+ errors << "Form is missing in Block station #{i+1}!"
130
+ elsif custom_task_form.class == Hash
131
+ form_title = custom_task_form['form_title']
132
+ errors << "Form Title is missing in Block station #{i+1}!" if form_title.nil?
133
+
134
+ instruction = custom_task_form['instruction']
135
+ errors << "Form Instruction is missing in Block station #{i+1}!" if instruction.nil?
136
+ end
137
+ elsif task_form.class == Hash
138
+ form_title = task_form['form_title']
139
+ errors << "Form Title is missing in Block station #{i+1}!" if form_title.nil?
140
+
141
+ instruction = task_form['instruction']
142
+ errors << "Form Instruction is missing in Block station #{i+1}!" if instruction.nil?
143
+
144
+ # Checking Form Fields
145
+ form_fields = task_form['form_fields']
146
+ errors << "Form Field is missing in Block station #{i+1}!" if form_fields.nil?
147
+ if form_fields.class == Array
148
+ form_fields.each_with_index do |form_field, index|
149
+ field_label = form_field['label']
150
+ errors << "Label is missing in block #{index+1} of Form Field within Station #{i+1}!" if field_label.nil?
151
+ required = form_field['required']
152
+ field_type = form_field['field_type']
153
+ if !field_type.nil?
154
+ unless %w(short_answer long_answer radio_button check_box select_box).include?(field_type)
155
+ errors << "Field Type of Form Field is invalid in Block #{index+1} of station Block #{i+1}!"
156
+ end
157
+ if field_type == "radio_button" || field_type == "select_box"
158
+ option_values = form_field['option_values']
159
+ if option_values.nil?
160
+ errors << "Option values is required for field_type => #{field_type} in block #{index+1} of Form Field within Station #{i+1} !"
161
+ elsif !option_values.nil?
162
+ if option_values.class != Array
163
+ errors << "Option values must be an array for field_type => #{field_type} in block #{index+1} of Form Field within Station #{i+1}!"
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
169
+ else
170
+ errors << "Form fields must be an array for Station #{i+1}!"
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+ return errors
180
+ end
181
+
182
+ end
183
+ end
@@ -125,5 +125,22 @@ module Cf # :nodoc: all
125
125
  say("\n")
126
126
  say(runs_table)
127
127
  end
128
+
129
+ desc "production resume", "resume a paused production run"
130
+ method_option :run_title, :type => :string, :required => true, :aliases => "-r", :desc => "the title of the run to resume"
131
+ def resume
132
+ set_target_uri(false)
133
+ set_api_key
134
+ CF.account_name = CF::Account.info.name
135
+ result = CF::Run.resume(options['run_title'].parameterize)
136
+
137
+ if result.error.present?
138
+ say("Error: #{result.error.message}", :red) and exit(1)
139
+ end
140
+
141
+ # if result.status == "resumed"
142
+ say("Run with title \"#{result.title}\" is resumed!", :green)
143
+ # end
144
+ end
128
145
  end
129
146
  end
@@ -60,14 +60,6 @@ module CF
60
60
  if type == "Improve" && self.stations.size < 1
61
61
  raise ImproveStationNotAllowed.new("You cannot add Improve Station as a first station of a line")
62
62
  else
63
- request_general =
64
- {
65
- :body =>
66
- {
67
- :api_key => CF.api_key,
68
- :station => {:type => type, :input_formats => @station_input_formats}
69
- }
70
- }
71
63
  if type == "Tournament"
72
64
  @jury_worker = stations.jury_worker
73
65
  @auto_judge = stations.auto_judge
@@ -81,6 +73,14 @@ module CF
81
73
  }
82
74
  resp = HTTParty.post("#{CF.api_url}#{CF.api_version}/lines/#{CF.account_name}/#{self.title.downcase}/stations.json",request_tournament)
83
75
  else
76
+ request_general =
77
+ {
78
+ :body =>
79
+ {
80
+ :api_key => CF.api_key,
81
+ :station => {:type => type, :input_formats => @station_input_formats}
82
+ }
83
+ }
84
84
  resp = HTTParty.post("#{CF.api_url}#{CF.api_version}/lines/#{CF.account_name}/#{self.title.downcase}/stations.json",request_general)
85
85
  end
86
86
  station = CF::Station.new()
@@ -157,6 +157,7 @@ module CF
157
157
  end
158
158
 
159
159
  end
160
+
160
161
  def input_formats=(input_formats_value) # :nodoc:
161
162
  @input_formats << input_formats_value
162
163
  end
@@ -27,10 +27,10 @@ module CF
27
27
  # You can pass line object instead of passing line title:
28
28
  # run = CF::Run.new(line_object, "run name", file_path)
29
29
  def initialize(line, title, input)
30
- if line.class == CF::Line || Hashie::Mash
30
+ if line.class == CF::Line || line.class == Hashie::Mash
31
31
  @line = line
32
- @line_title = @line.title
33
- else
32
+ @line_title = line.title
33
+ elsif line.class == String
34
34
  @line_title = line
35
35
  end
36
36
  @title = title
@@ -1,3 +1,3 @@
1
1
  module CF # :nodoc: all
2
- VERSION = "0.1.12"
2
+ VERSION = "0.1.13"
3
3
  end
@@ -40,8 +40,8 @@ module CF
40
40
  line.stations.first.type.should eql("WorkStation")
41
41
  line.stations.first.worker.number.should eql(2)
42
42
  line.stations.first.worker.reward.should eql(20)
43
- line.stations.first.worker.skill_badges.first.should eql([{"title"=>"Football Fanatic", "description"=>"This qualification allows you to perform work at stations which have this badge.", "score"=>nil, "speed"=>nil, "quality_rating"=>nil, "max_badges"=>3, "skill_test"=>{"score_after"=>"submit", "manual_scoring"=>false, "display_answers"=>false, "edit_answers"=>true, "retries"=>0, "pass_percentage"=>100, "test_units"=>[{"input"=>{"name"=>"Lionel Andres Messi", "country"=>"Argentina"}, "expected_output"=>[{"birthplace"=>"Rosario, Santa Fe, Argentina", "match_options"=>{"tolerance"=>"1", "ignore_case"=>"false"}, "position"=>"CF", "current-club"=>"Barcelona"}], "match_options"=>{"tolerance"=>0, "ignore_case"=>false}}]}}])
44
- line.stations.first.worker.skill_badges.last.should eql([{"title"=>"Football Fanatic", "description"=>"This qualification allows you to perform work at stations which have this badge.", "score"=>nil, "speed"=>nil, "quality_rating"=>nil, "max_badges"=>3, "skill_test"=>{"score_after"=>"submit", "manual_scoring"=>false, "display_answers"=>false, "edit_answers"=>true, "retries"=>0, "pass_percentage"=>100, "test_units"=>[{"input"=>{"name"=>"Cristiano Ronaldo", "country"=>"Portugal"}, "expected_output"=>[{"birthplace"=>"Rosario, Santa Fe, Portugal", "match_options"=>{"tolerance"=>"1", "ignore_case"=>"false"}, "position"=>"CF", "current-club"=>"Real Madrid"}], "match_options"=>{"tolerance"=>0, "ignore_case"=>false}}]}}])
43
+ line.stations.first.worker.skill_badges.first.should eql([{"title"=>"Football Fanatic", "description"=>"This qualification allows you to perform work at stations which have this badge.", "score"=>nil, "quality_rating"=>nil, "max_badges"=>3, "skill_test"=>{"score_after"=>"submit", "manual_scoring"=>false, "display_answers"=>false, "edit_answers"=>true, "retries"=>0, "pass_percentage"=>100, "test_units"=>[{"input"=>{"name"=>"Lionel Andres Messi", "country"=>"Argentina"}, "expected_output"=>[{"birthplace"=>"Rosario, Santa Fe, Argentina", "match_options"=>{"tolerance"=>"1", "ignore_case"=>"false"}, "position"=>"CF", "current-club"=>"Barcelona"}], "match_options"=>{"tolerance"=>0, "ignore_case"=>false}}]}}])
44
+ line.stations.first.worker.skill_badges.last.should eql([{"title"=>"Football Fanatic", "description"=>"This qualification allows you to perform work at stations which have this badge.", "score"=>nil, "quality_rating"=>nil, "max_badges"=>3, "skill_test"=>{"score_after"=>"submit", "manual_scoring"=>false, "display_answers"=>false, "edit_answers"=>true, "retries"=>0, "pass_percentage"=>100, "test_units"=>[{"input"=>{"name"=>"Cristiano Ronaldo", "country"=>"Portugal"}, "expected_output"=>[{"birthplace"=>"Rosario, Santa Fe, Portugal", "match_options"=>{"tolerance"=>"1", "ignore_case"=>"false"}, "position"=>"CF", "current-club"=>"Real Madrid"}], "match_options"=>{"tolerance"=>0, "ignore_case"=>false}}]}}])
45
45
  line.stations.first.worker.stat_badge.should eql({"approval_rating"=>80, "assignment_duration"=>3600, "abandonment_rate"=>30, "country"=>nil})
46
46
  end
47
47
  end
@@ -10,7 +10,7 @@ module CF
10
10
  line = CF::Line.create("concept_tagging_robot","Digitization") do |l|
11
11
  CF::InputFormat.new({:line => l, :name => "url", :valid_type => "url", :required => "true"})
12
12
  CF::Station.create({:line => l, :type => "work"}) do |s|
13
- CF::RobotWorker.create({:station => s, :type => "concept_tagging_robot", :settings => {:url => ["{url}"]}})
13
+ CF::RobotWorker.create({:station => s, :type => "concept_tagging_robot", :settings => {:url => ["{{url}}"]}})
14
14
  end
15
15
  end
16
16
  run = CF::Run.create(line, "concept_tagging_robot_run", [{"url"=>"www.mosexindex.com"}])
@@ -18,9 +18,9 @@ module CF
18
18
  output.first.final_output.first.concept_tagging_of_url.should eql(["Canada", "English language"])
19
19
  output.first.final_output.first.concept_tagging_relevance_of_url.should eql([89.5153, 79.0912])
20
20
  line.stations.first.worker.class.should eql(CF::RobotWorker)
21
- line.stations.first.worker.reward.should eql(1)
21
+ line.stations.first.worker.reward.should eql(0.5)
22
22
  line.stations.first.worker.number.should eql(1)
23
- line.stations.first.worker.settings.should eql({:url => ["{url}"]})
23
+ line.stations.first.worker.settings.should eql({:url => ["{{url}}"]})
24
24
  line.stations.first.worker.type.should eql("ConceptTaggingRobot")
25
25
  end
26
26
  end
@@ -35,7 +35,7 @@ module CF
35
35
  station = CF::Station.new({:type => "work"})
36
36
  line.stations station
37
37
 
38
- worker = CF::RobotWorker.create({:type => "concept_tagging_robot", :settings => {:url => ["{url}"]}})
38
+ worker = CF::RobotWorker.create({:type => "concept_tagging_robot", :settings => {:url => ["{{url}}"]}})
39
39
  line.stations.first.worker = worker
40
40
 
41
41
  run = CF::Run.create(line, "concept_tagging_robot_run_1", [{"url"=>"www.mosexindex.com"}])
@@ -43,9 +43,9 @@ module CF
43
43
  output.first.final_output.first.concept_tagging_of_url.should eql(["Canada", "English language"])
44
44
  output.first.final_output.first.concept_tagging_relevance_of_url.should eql([89.5153, 79.0912])
45
45
  line.stations.first.worker.class.should eql(CF::RobotWorker)
46
- line.stations.first.worker.reward.should eql(1)
46
+ line.stations.first.worker.reward.should eql(0.5)
47
47
  line.stations.first.worker.number.should eql(1)
48
- line.stations.first.worker.settings.should eql({:url => ["{url}"]})
48
+ line.stations.first.worker.settings.should eql({:url => ["{{url}}"]})
49
49
  line.stations.first.worker.type.should eql("ConceptTaggingRobot")
50
50
  end
51
51
  end
@@ -17,10 +17,9 @@ module CF
17
17
 
18
18
  output = run.final_output
19
19
  output.first.final_output.first.scraped_link_from_document.should eql([["http://www.cloudfactory.com", "http://www.bizcardarmy.com"]])
20
- output.first.final_output.first.query.should eql("1st 2 links after Sprout products")
21
20
 
22
21
  line.stations.first.worker.class.should eql(CF::RobotWorker)
23
- line.stations.first.worker.reward.should eql(10)
22
+ line.stations.first.worker.reward.should eql(0.5)
24
23
  line.stations.first.worker.number.should eql(1)
25
24
  line.stations.first.worker.settings.should eql({:document => ["http://www.sprout-technology.com"], :query => "1st 2 links after Sprout products"})
26
25
  line.stations.first.worker.type.should eql("ContentScrapingRobot")
@@ -44,10 +43,9 @@ module CF
44
43
 
45
44
  output = run.final_output
46
45
  output.first.final_output.first.scraped_link_from_document.should eql([["http://www.cloudfactory.com", "http://www.bizcardarmy.com"]])
47
- output.first.final_output.first.query.should eql("1st 2 links after Sprout products")
48
46
 
49
47
  line.stations.first.worker.class.should eql(CF::RobotWorker)
50
- line.stations.first.worker.reward.should eql(10)
48
+ line.stations.first.worker.reward.should eql(0.5)
51
49
  line.stations.first.worker.number.should eql(1)
52
50
  line.stations.first.worker.settings.should eql({:document => ["http://www.sprout-technology.com"], :query => "1st 2 links after Sprout products"})
53
51
  line.stations.first.worker.type.should eql("ContentScrapingRobot")
@@ -78,14 +78,14 @@ describe CF::CustomTaskForm do
78
78
  line = CF::Line.create("Digitizecustomform11", "Digitization") do
79
79
  CF::InputFormat.new({:line => self, :name => "Name", :required => true, :valid_format => "general"})
80
80
  CF::InputFormat.new({:line => self, :name => "Contact", :required => true, :valid_type => "url"})
81
- CF::Station.create({:line => self, :type => "tournament", :max_judges => 10, :auto_judge => true}) do |s|
81
+ CF::Station.create({:line => self, :type => "work"}) do |s|
82
82
  CF::HumanWorker.new({:station => s, :number => 3, :reward => 20})
83
83
  CF::CustomTaskForm.create({:station => s, :title => "Enter text from a business card image", :instruction => "Describe", :raw_html => html, :raw_css => css, :raw_javascript => javascript})
84
84
  end
85
85
  end
86
86
  line.title.should eql("Digitizecustomform11")
87
87
  line.department_name.should eql("Digitization")
88
- line.stations.first.type.should eql("TournamentStation")
88
+ line.stations.first.type.should eql("WorkStation")
89
89
  CGI.unescape_html(line.stations.first.form.raw_html).should eql(html)
90
90
  CGI.unescape_html(line.stations.first.form.raw_css).should eql(css)
91
91
  CGI.unescape_html(line.stations.first.form.raw_javascript).should eql(javascript)
@@ -94,7 +94,7 @@ describe CF::CustomTaskForm do
94
94
 
95
95
  it "in plain ruby way" do
96
96
  VCR.use_cassette "custom-task-form/plain/create", :record => :new_episodes do
97
- html = '<div id="form-content">
97
+ html = '<form><div id="form-content">
98
98
  <div id="instructions">
99
99
  <ul>
100
100
  <li>Look at the business card properly and fill in asked data.</li>
@@ -108,18 +108,18 @@ describe CF::CustomTaskForm do
108
108
  </div>
109
109
  <div id = "field-panel">
110
110
  Name<br />
111
- <input class="input-field first_name" type="text" name="final_output[first_name]" />
112
- <input class="input-field middle_name" type="text" name="final_output[middle_name]" />
113
- <input class="input-field last_name" type="text" name="final_output[last_name]" /><br />
111
+ <input class="input-field first_name" type="text" name="output[first_name]" />
112
+ <input class="input-field middle_name" type="text" name="output[middle_name]" />
113
+ <input class="input-field last_name" type="text" name="output[last_name]" /><br />
114
114
 
115
115
  <br />Contact<br />
116
- <input class="input-field email" type="text" name="final_output[email]" placeholder="Email"/>
117
- <input class="input-field phone" type="text" name="final_output[phone]" placeholder="Phone"/>
118
- <input class="input-field mobile" type="text" name="final_output[mobile]" placeholder="Mobile"/><br />
116
+ <input class="input-field email" type="text" name="output[email]" placeholder="Email"/>
117
+ <input class="input-field phone" type="text" name="output[phone]" placeholder="Phone"/>
118
+ <input class="input-field mobile" type="text" name="output[mobile]" placeholder="Mobile"/><br />
119
119
 
120
120
  </div>
121
121
  </div>
122
- </div>'
122
+ </div></form>'
123
123
 
124
124
  css = '<style>body {background:#fbfbfb;}
125
125
  #instructions{