cloudfactory 0.6.4 → 0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,9 @@
1
- ## 0.6.3(2012-6-29)
2
- * Removed unnecessary requires.
1
+ ## 0.7 (2012-07-09)
2
+ * Badge creation, update, deletion and listing added
3
3
 
4
- ## 0.6.2(2012-6-29)
4
+ ## 0.6(2012-6-29)
5
5
  * CustomTaskForm update feature added
6
+ * Give the link to the production run page and not the work preview page when a run is created using the CLI.
6
7
 
7
8
  ## 0.5.6 (2012-05-17)
8
9
  * Priority field added in the line
@@ -13,45 +13,146 @@ module CF
13
13
  attr_accessor :description
14
14
 
15
15
  # number of badge provided to the worker
16
- attr_accessor :max_badges
16
+ attr_accessor :max_badge_assignments
17
+
18
+ attr_accessor :allow_retake_after
17
19
 
18
20
  # Contains error message
19
21
  attr_accessor :errors
20
22
 
21
23
  def initialize(options={})
22
24
  options.symbolize_keys!
23
- if options[:test_attributes] && options[:test_attributes][:form_attributes]
24
- form = options[:test_attributes][:form_attributes]
25
- if( form[:type] == "CustomTaskForm" && form[:file])
26
- raw_html = ""
27
- File.open("#{form[:file]}").each_line do |line|
28
- raw_html += line
25
+ #if the form is provided as a file then explicitly add title and raw_html for the form
26
+ if options[:form] && options[:form].class != Hash
27
+ if File.exist?(options[:form])
28
+ file_type = IO.popen(["file", "--brief", "--mime-type", options[:form]], in: :close, err: :close).read.chomp
29
+ if file_type == "text/html"
30
+ options[:form] = {:title => "#{options[:name]}_form",:_type => "CustomTaskForm", :raw_html => File.read(options[:form])}
31
+ else
32
+ @errors = ["Wrong type of file for the badge form."]
33
+ return @errors
34
+ exit(1)
29
35
  end
30
- options[:test_attributes][:form_attributes].merge!({:raw_html => raw_html,:_type =>"CustomTaskForm"})
36
+ else
37
+ @errors = ["Badge form path is not valid."]
31
38
  end
32
- options[:test_attributes][:form_attributes].merge!({:_type =>"TaskForm"}) if form[:type] == "TaskForm"
33
39
  end
40
+
41
+ #known_answers of the badge get customize because to if unique identifier doesn't present then array of hashs returns the last hash for Httparty and get all key value merge into one in rest client/ rack
42
+ options[:known_answers] = self.class.customize_known_answers(options[:known_answers], options[:name]) if options[:known_answers].present?
43
+
34
44
  @settings = options
35
- @station = options.delete(:station)
36
- @line = options.delete(:line)
37
- if @line.present? && @station.present?
38
- request =
45
+ @name = options[:name]
46
+ @description = options[:description]
47
+ @allow_retake_after = options[:allow_retake_after]
48
+
49
+ request =
50
+ {
51
+ :body =>
39
52
  {
40
- :body =>
41
- {
42
- :api_key => CF.api_key,
43
- :badge => options
44
- }
53
+ :api_key => CF.api_key,
54
+ :badge => options
45
55
  }
56
+ }
46
57
 
47
- resp = HTTParty.post("#{CF.api_url}#{CF.api_version}/lines/#{CF.account_name}/#{@line.title.downcase}/stations/#{@station.index}/badges.json",request)
48
- @station.badges = self
49
- self.errors = resp.parsed_response['error']['message'] if resp.code != 200
58
+ resp = HTTParty.post("#{CF.api_url}#{CF.api_version}/accounts/#{CF.account_name}/badges.json",request)
59
+ begin
60
+ self.errors = resp["error"]["message"] if resp.code != 200
61
+ return resp
62
+ rescue
63
+ self.errors = ["Unexpected error found. Confirm that you are in correct path and form for the badge is provided."]
50
64
  end
51
65
  end
52
66
 
53
67
  def self.create(*args)
54
68
  Badge.new(args.first)
55
69
  end
70
+
71
+ def self.update(badge_name, update_params)
72
+ badge_name = badge_name.parameterize
73
+ update_params.symbolize_keys!
74
+ if update_params[:form] && update_params[:form].class != Hash && File.exist?(update_params[:form])
75
+ file_type = IO.popen(["file", "--brief", "--mime-type", update_params[:form]], in: :close, err: :close).read.chomp
76
+ if file_type == "text/html"
77
+ update_params[:form] = {:title => "#{update_params[:name]}_form",:_type => "CustomTaskForm", :raw_html => File.read(update_params[:form])}
78
+ else
79
+ return
80
+ end
81
+ end
82
+
83
+ update_params[:known_answers] = customize_known_answers(update_params[:known_answers], update_params[:name]) if update_params[:known_answers].present?
84
+
85
+ request =
86
+ {
87
+ :body =>
88
+ {
89
+ :api_key => CF.api_key,
90
+ :badge => update_params
91
+ }
92
+ }
93
+ resp = HTTParty.put("#{CF.api_url}#{CF.api_version}/accounts/#{CF.account_name}/badges/#{badge_name}.json",request)
94
+ return resp
95
+ end
96
+
97
+ def self.destroy(*args)
98
+ name = args[0].parameterize
99
+ options = args[1].nil? ? {} : args[1]
100
+ decendents = check_for_decendents(name)
101
+ if decendents
102
+ response = delete("/accounts/#{CF.account_name}/badges/#{name}.json") if options.force
103
+ else
104
+ response = delete("/accounts/#{CF.account_name}/badges/#{name}.json")
105
+ end
106
+ @errors = response['error']['message'] if response['code'] != 200
107
+ return response
108
+ end
109
+
110
+ #list all badges with in the account if name is not provided else it will return list all values of the given badge
111
+ def self.list(name = nil)
112
+ response = get("/accounts/#{CF.account_name}/badges.json", :name => name)
113
+ @errors = response['error']['message'] if response['code'] != 200
114
+ return response
115
+ end
116
+
117
+ def self.show(name)
118
+ response = get("/accounts/#{CF.account_name}/badges/#{name.parameterize}.json")
119
+ @errors = response['error']['message'] if response['code'] != 200
120
+ return response
121
+ end
122
+
123
+ protected
124
+
125
+ #insert name to the goldstandard/known_answer if name is not provided
126
+ def self.customize_known_answers(known_answers, badge_name)
127
+ index = 1
128
+ known_answers.each do |known_answer|
129
+ unless known_answer["name"].present?
130
+ known_answer.merge!("name" => "#{badge_name}_answer_#{index}")
131
+ index +=1
132
+ end
133
+ end
134
+ end
135
+
136
+
137
+ #list the decendents for the badges like worker_associated, line_associated, number of tasks associated with the badge
138
+ def self.check_for_decendents(name)
139
+ show_response = get("/accounts/#{CF.account_name}/badges/#{name.parameterize}.json")
140
+ @errors = show_response["error"]["message"] unless show_response["code"] == 200
141
+ unless @errors
142
+ response = get("/accounts/#{CF.account_name}/badges.json", :name => name)
143
+ errors = response["error"]["message"] unless response["code"] == 200
144
+ return false if errors
145
+
146
+ lines = response["badges"].first["lines_associated"]
147
+ workers = response["badges"].first["workers"]
148
+ tasks = response["badges"].first["num_of_tasks"]
149
+ #if the lines of worker count is greater then 0 then return true else return lines and worker count
150
+ if lines > 0 || workers > 0
151
+ {:lines_count => lines, :workers_count => workers, :tasks => tasks }
152
+ else
153
+ false
154
+ end
155
+ end
156
+ end
56
157
  end
57
158
  end
@@ -17,12 +17,15 @@ require 'active_support/core_ext/object/blank'
17
17
 
18
18
  cli_directory = File.expand_path("../cf/cli", File.dirname(__FILE__))
19
19
  require "#{cli_directory}/config"
20
+ require "#{cli_directory}/badge_yaml_validator"
21
+ require "#{cli_directory}/badge"
20
22
  require "#{cli_directory}/line_yaml_validator"
21
23
  require "#{cli_directory}/line"
22
24
  require "#{cli_directory}/form"
23
25
  require "#{cli_directory}/production"
24
26
 
25
27
 
28
+
26
29
  if ENV['TEST']
27
30
  require 'pry'
28
31
  end
@@ -33,9 +36,9 @@ module Cf # :nodoc: all
33
36
  include Cf::Config
34
37
 
35
38
  def help(*args)
36
- File.open(File.expand_path(File.dirname(__FILE__)+"/help.txt")).each_line{ |s|
37
- say s
38
- }
39
+ File.open(File.expand_path(File.dirname(__FILE__)+"/help.txt")).each_line{ |s|
40
+ say s
41
+ }
39
42
  end
40
43
 
41
44
  map "-v" => :version
@@ -59,7 +62,7 @@ module Cf # :nodoc: all
59
62
 
60
63
  no_tasks do
61
64
  def ask_password(message)
62
- ::HighLine.new.ask(message) do |q|
65
+ ::HighLine.new.ask(message) do |q|
63
66
  q.echo = '*'
64
67
  end
65
68
  end
@@ -103,6 +106,9 @@ module Cf # :nodoc: all
103
106
  # later it can be replaced with hacked millisami-thor version of the thor library with run-time dependency via Bundler
104
107
  subcommand "production", Cf::Production
105
108
 
109
+ desc "badge", "Commands to manage Badges. For more info cf badge help"
110
+ subcommand "badge", Cf::Badge
111
+
106
112
  desc "output <run-title>", "Get the output of run. For more info, cf output help"
107
113
  method_option :run_title, :type => :string, :required => true, :aliases => "-t", :desc => "the index of the station"
108
114
  method_option :station_index, :type => :numeric, :aliases => "-s", :desc => "the index of the station"
@@ -128,7 +134,7 @@ module Cf # :nodoc: all
128
134
  output = CF::Run.final_output(run_title)
129
135
  end
130
136
  if !output.empty?
131
- output.each
137
+ output.each
132
138
  csv_str = CSVHash(output,output.first.keys)
133
139
  csv_str = csv_str.gsub("\"\"", '"')
134
140
  FileUtils.mkdir("#{line_source}/output") unless Dir.exist?("#{line_source}/output") if RUBY_VERSION > "1.9"
@@ -153,5 +159,36 @@ module Cf # :nodoc: all
153
159
  def version
154
160
  say("Version: #{CF::VERSION}")
155
161
  end
162
+
163
+ desc "generate <project_name>", "Generate directory hierarchy for a new project"
164
+ method_option :force, :type => :boolean, :default => false, :aliases => "-f", :desc => "force to overwrite the files if already exists"
165
+ def generate(project_name)
166
+ source = Dir.pwd
167
+
168
+ parent_dir = "#{source}/#{project_name}"
169
+ lines_dir = "#{parent_dir}/lines"
170
+ badges_dir = "#{parent_dir}/badges"
171
+
172
+ if options.force?
173
+ FileUtils.rm_rf( parent_dir, :verbose => true)
174
+ end
175
+
176
+ if File.exist?(parent_dir)
177
+ say "Skipping #{project_name} because it already exists.", :red
178
+ exit(1)
179
+ end
180
+
181
+ say "Generating #{project_name} project hierarchy"
182
+ FileUtils.mkdir(parent_dir)
183
+ say_status "create", "#{project_name}"
184
+ FileUtils.mkdir(badges_dir)
185
+ say_status "create", "#{project_name}/badges"
186
+ FileUtils.mkdir(lines_dir)
187
+ say_status "create", "#{project_name}/lines"
188
+
189
+ say "#{project_name.humanize} project hierarchy generated successfully.", :green
190
+ say "Go to the directory #{lines_dir} and run command 'cf line generate <line_name>' to create line.", :yellow
191
+ say "Go to the directory #{badges_dir} and run command 'cf badge generate <badge_name>' to create badge.", :yellow
192
+ end
156
193
  end
157
194
  end
@@ -0,0 +1,248 @@
1
+ require 'thor/group'
2
+ module Cf # :nodoc: all
3
+ class NewBadge < Thor::Group # :nodoc: all
4
+ include Thor::Actions
5
+ include Cf::Config
6
+ source_root File.expand_path('../templates', __FILE__)
7
+ argument :name, :type => :string, :desc => "The badge name."
8
+ argument :source_destination, :type => :string, :required => true
9
+
10
+ def generate
11
+ parent_dir = "#{source_destination}/#{name}"
12
+ FileUtils.mkdir(parent_dir)
13
+ template("sample-line/badge.yml.erb", "#{parent_dir}/badge.yml")
14
+ copy_file("sample-line/form.html", "#{parent_dir}/form.html")
15
+ end
16
+ end
17
+ end
18
+
19
+ module Cf # :nodoc: all
20
+ class Badge < Thor
21
+ include Cf::Config
22
+ include Cf::BadgeYamlValidator
23
+
24
+
25
+ desc "badge generate <badge_name>", "Generate a new badge template."
26
+ method_option :force, :type => :boolean, :default => false, :aliases => "-f", :desc => "force to overwrite the files if already exists, default is false"
27
+ def generate(badge_name = nil)
28
+ badge_name = badge_name.nil? ? "web_research" : badge_name.chomp(File.extname(badge_name))
29
+ source_destination = "#{Dir.pwd}"
30
+
31
+ if options.force?
32
+ FileUtils.rm_rf(badge_name, :verbose => true)
33
+ end
34
+
35
+ if File.exist?(badge_name)
36
+ say "Skipping #{badge_name} because it already exists.", :red
37
+ else
38
+ say "Generating a new badge template #{badge_name}", :green
39
+ Cf::NewBadge.start([badge_name, source_destination])
40
+ say "Badge template generated successfully.", :green
41
+ say "Modify files inside #{badge_name} directory and create badge with: cf badge create", :yellow
42
+ end
43
+ end
44
+
45
+
46
+
47
+ desc "badge create <badge_name>", "takes the badge.yml from badge_name directory and create a badge"
48
+ def create(badge_name)
49
+ yaml_source = "#{Dir.pwd}/#{badge_name}/badge.yml"
50
+
51
+ unless File.exist?(yaml_source)
52
+ say "A file with name badge.yml inside #{badge_name} directory does not exists.", :red
53
+ return
54
+ end
55
+
56
+ errors = validate(yaml_source)
57
+ unless errors.present?
58
+ set_target_uri(false)
59
+ set_api_key(yaml_source)
60
+ CF.account_name = CF::Account.info['name']
61
+
62
+ badge_hash = YAML::load(File.read(yaml_source).strip)
63
+
64
+ name = badge_hash["name"]
65
+
66
+ #check whether the badge form exist or not. If the badge form path looks invalid then it tries to recover and then again check and then also its does not exit then it raise errors.
67
+ if badge_hash["form"].class == String
68
+ unless File.exist?(badge_hash["form"])
69
+ badge_hash["form"] = "#{badge_name}/#{badge_hash["form"]}"
70
+ unless File.exist?(badge_hash["form"])
71
+ say "Invalid path for badge form.", :red
72
+ exit(1)
73
+ end
74
+ end
75
+ end
76
+
77
+ do_exist = CF::Badge.show(name.parameterize)
78
+ if do_exist && do_exist["code"]==200
79
+ say "A badge named #{name} already exists.", :yellow
80
+ override = agree("Do you want to override ? [y/n] ")
81
+ override == true ? badge_hash.merge!(:confirmed => true) : ((say "Badge creation aborted!", :red) and exit(1))
82
+ end
83
+ say "Creating badge '#{name}'"
84
+ badge = CF::Badge.new(badge_hash)
85
+ if badge.errors.present?
86
+ say "Errors: \n", :red
87
+ badge.errors.each do |error|
88
+ say(error, :red)
89
+ end
90
+ exit(1)
91
+ else
92
+ say "A new badge named '#{name}' created sucessfully!"
93
+ end
94
+ else
95
+ say "Following errors has been encountered in your badge.yml file: #{errors.join(",")}" , :red
96
+ end
97
+ end
98
+
99
+
100
+
101
+ desc "badge update <badge_name>", "takes the badge.yml from badge_name directory and update the badge"
102
+ def update(badge_name)
103
+ yaml_source = "#{Dir.pwd}/#{badge_name}/badge.yml"
104
+
105
+ unless File.exist?(yaml_source)
106
+ say "A file with name badge.yml inside #{badge_name} directory does not exists.", :red
107
+ return
108
+ end
109
+ errors = validate(yaml_source)
110
+
111
+ unless errors.present?
112
+ set_target_uri(false)
113
+ set_api_key(yaml_source)
114
+ CF.account_name = CF::Account.info['name']
115
+
116
+ badge_hash = YAML::load(File.read(yaml_source).strip)
117
+
118
+ #check whether the badge form exist or not. If the badge form path looks invalid then it tries to recover and then again check and then also its does not exit then it raise errors.
119
+ if badge_hash["form"].class == String
120
+ unless File.exist?(badge_hash["form"])
121
+ badge_hash["form"] = "#{badge_name}/#{badge_hash["form"]}"
122
+ unless File.exist?(badge_hash["form"])
123
+ say "Invalid path for badge form.", :red
124
+ exit(1)
125
+ end
126
+ end
127
+ end
128
+
129
+ do_exist = CF::Badge.list(badge_name.parameterize)
130
+ #check whetether the badge do exist and has its decendents if do then retain_current_worker_badge = true so that worker and lines associated get retain if the badge get updated other wise the badge act as a new badge
131
+ if do_exist && do_exist["code"]==200
132
+ descendents = CF::Badge.check_for_decendents(badge_name.parameterize)
133
+ if descendents
134
+ say("\n!!! Warning !!!\n The badge '#{badge_name}' has the following number of tasks, lines and workers asscociated with it.\n", :yellow)
135
+ badge_table = table do |t|
136
+ t.headings = [ 'Tasks', 'Lines', 'Workers']
137
+ t << [ descendents[:tasks], descendents[:lines_count], descendents[:workers_count]]
138
+ end
139
+ say(badge_table)
140
+ say("\n!!! Warning !!!\n Retaining the workers will keep the workers associated with the badge.\n", :yellow)
141
+ update_forcefully = agree("Do you want to retain the workers? [y/n] ")
142
+ badge_hash.merge!(:retain_workers => false) if update_forcefully == false
143
+ end
144
+
145
+ #now update the badge
146
+ say "Updating badge named '#{badge_name}'"
147
+ badge = CF::Badge.update(badge_name,badge_hash)
148
+ if badge && badge.code == 200
149
+ say "A badge named '#{badge_name}' updated sucessfully!"
150
+ else
151
+ if badge.code == 404
152
+ say "Badge does not exist."
153
+ elsif badge.code == 422
154
+ say badge["error"]["message"], :red
155
+ else
156
+ say "A badge named '#{badge_name}' can not be updated", :red
157
+ end
158
+ exit(1)
159
+ end
160
+ else
161
+ say "A badge named '#{badge_name}' does not exist!", :red
162
+ exit(1)
163
+ end
164
+ else
165
+ say "Following errors has been encountered in your badge.yml file: #{errors.join(",")}" , :red
166
+ end
167
+ end
168
+
169
+
170
+
171
+ desc "badge delete <badge name>", "Delete the given badge within your account."
172
+ method_option :force, :type => :boolean, :aliases => "-f", :default => false, :desc => "force delete the badge"
173
+ def delete(badge_name)
174
+ #set badge basic credentials
175
+ line_source = Dir.pwd
176
+ yaml_source = "#{line_source}/line.yml"
177
+ set_target_uri(false)
178
+ set_api_key(yaml_source)
179
+ set_target_uri(false)
180
+ CF.account_name = CF::Account.info['name']
181
+
182
+ #list all badges if name is not present else list the specific badge
183
+ if badge_name
184
+ badge_decendents = CF::Badge.check_for_decendents(badge_name)
185
+ if badge_decendents
186
+ say("\n!!! Warning !!!\n The badge #{badge_name} has following number of tasks, lines and workers asscociated with it.\n", :yellow)
187
+ badge_table = table do |t|
188
+ t.headings = [ 'Tasks', 'Lines', 'Workers']
189
+ t << [ badge_decendents[:tasks], badge_decendents[:lines_count], badge_decendents[:workers_count]]
190
+ end
191
+ say(badge_table)
192
+
193
+ delete_forcefully = agree("Do you still want to delete this badge? [y/n] ")
194
+ if delete_forcefully
195
+ badge_resp = CF::Badge.destroy(badge_name,options.merge(:force => true))
196
+ else
197
+ say "Badge deletion aborted!", :yellow
198
+ exit(1)
199
+ end
200
+ else
201
+ say "Deleting badge named '#{badge_name}'"
202
+ badge_resp = CF::Badge.destroy(badge_name)
203
+ end
204
+ badge_resp["code"] == 200 ? (say "The badge named #{badge_name} deleted sucessfully!", :green) : (say badge_resp["error"]["message"], :red)
205
+ else
206
+ say "Name of the badge for deletion is missing.", :red
207
+ end
208
+ end
209
+
210
+
211
+
212
+ desc "badge list ", "List all badges asscociated within your account. Use 'cf badge list <badge_name>' for single ."
213
+ def list(name = nil)
214
+ #set badge basic credentials
215
+ line_source = Dir.pwd
216
+ yaml_source = "#{line_source}/line.yml"
217
+ set_target_uri(false)
218
+ set_api_key(yaml_source)
219
+ set_target_uri(false)
220
+ CF.account_name = CF::Account.info['name']
221
+
222
+ #list all badges if name is not present else list the specific badge
223
+ if name
224
+ badge_resp = CF::Badge.list(name)
225
+ else
226
+ badge_resp = CF::Badge.list
227
+ end
228
+
229
+ #show list of badges in the tabular view
230
+ if badges = badge_resp["badges"]
231
+ say "Listing badges within your account" , :green
232
+ badges.sort! { |a, b| a['name'] <=> b['name'] }
233
+ badge_table = table do |t|
234
+ t.headings = ["Name", 'Number Of Tasks', 'Lines Associated', 'Workers', 'Description']
235
+ badges.each do |badge|
236
+ badge = Hashie::Mash.new(badge)
237
+ t << [badge.name, badge.num_of_tasks, badge.lines_associated, badge.workers, badge.description]
238
+ end
239
+ end
240
+ say(badge_table)
241
+ else
242
+ say badge_resp["error"]["message"], :red
243
+ end
244
+ end
245
+
246
+
247
+ end
248
+ end