Merge pull request #453 from PetroFeed/scenario-tag-color

Add ability to set color for the scenario tags.

Andrew Cantino %!s(int64=10) %!d(string=hace) años
padre
commit
28707507c7

+ 2 - 0
Gemfile

@@ -56,6 +56,8 @@ gem 'uglifier', '>= 1.3.0'
56 56
 gem 'select2-rails', '~> 3.5.4'
57 57
 gem 'jquery-rails', '~> 3.1.0'
58 58
 gem 'ace-rails-ap', '~> 2.0.1'
59
+gem 'spectrum-rails'
60
+
59 61
 
60 62
 # geokit-rails doesn't work with geokit 1.8.X but it specifies ~> 1.5
61 63
 # in its own Gemfile.

+ 3 - 0
Gemfile.lock

@@ -293,6 +293,8 @@ GEM
293 293
     simplecov-html (0.8.0)
294 294
     slack-notifier (0.5.0)
295 295
     slop (3.6.0)
296
+    spectrum-rails (1.3.4)
297
+      railties (>= 3.1)
296 298
     sprockets (2.11.0)
297 299
       hike (~> 1.2)
298 300
       multi_json (~> 1.0)
@@ -418,6 +420,7 @@ DEPENDENCIES
418 420
   select2-rails (~> 3.5.4)
419 421
   shoulda-matchers
420 422
   slack-notifier (~> 0.5.0)
423
+  spectrum-rails
421 424
   therubyracer (~> 0.12.1)
422 425
   twilio-ruby (~> 3.11.5)
423 426
   twitter (~> 5.8.0)

+ 1 - 0
app/assets/javascripts/application.js.coffee.erb

@@ -6,6 +6,7 @@
6 6
 #= require json2
7 7
 #= require jquery.json-editor
8 8
 #= require latlon_and_geo
9
+#= require spectrum
9 10
 #= require ./worker-checker
10 11
 #= require_self
11 12
 

+ 15 - 0
app/assets/stylesheets/application.css.scss.erb

@@ -12,6 +12,7 @@
12 12
  *= require select2-bootstrap
13 13
  *= require jquery.json-editor
14 14
  *= require rickshaw
15
+ *= require spectrum
15 16
  *= require_tree .
16 17
  *= require_self
17 18
  */
@@ -186,3 +187,17 @@ h2 .scenario, a span.label.scenario {
186 187
 .color-success {
187 188
   color: #5cb85c;
188 189
 }
190
+
191
+.form-group {
192
+  .sp-replacer {
193
+    @extend .form-control;
194
+  }
195
+
196
+  .sp-preview {
197
+    width: 100%;
198
+  }
199
+
200
+  .sp-dd {
201
+    display: none;
202
+  }
203
+}

+ 2 - 0
app/controllers/scenarios_controller.rb

@@ -45,6 +45,8 @@ class ScenariosController < ApplicationController
45 45
     @exporter = AgentsExporter.new(:name => @scenario.name,
46 46
                                    :description => @scenario.description,
47 47
                                    :guid => @scenario.guid,
48
+                                   :tag_fg_color => @scenario.tag_fg_color,
49
+                                   :tag_bg_color => @scenario.tag_bg_color,
48 50
                                    :source_url => @scenario.public? && export_scenario_url(@scenario),
49 51
                                    :agents => @scenario.agents)
50 52
     response.headers['Content-Disposition'] = 'attachment; filename="' + @exporter.filename + '"'

+ 2 - 2
app/helpers/agent_helper.rb

@@ -8,11 +8,11 @@ module AgentHelper
8 8
 
9 9
   def scenario_links(agent)
10 10
     agent.scenarios.map { |scenario|
11
-      link_to(scenario.name, scenario, class: "label label-info")
11
+      link_to(scenario.name, scenario, class: "label", style: style_colors(scenario))
12 12
     }.join(" ").html_safe
13 13
   end
14 14
 
15 15
   def agent_show_class(agent)
16 16
     agent.short_type.underscore.dasherize
17 17
   end
18
-end
18
+end

+ 23 - 0
app/helpers/scenario_helper.rb

@@ -0,0 +1,23 @@
1
+module ScenarioHelper
2
+
3
+  def style_colors(scenario)
4
+    colors = {
5
+      color: scenario.tag_fg_color || default_scenario_fg_color,
6
+      background_color: scenario.tag_bg_color || default_scenario_bg_color
7
+    }.map { |key, value| "#{key.to_s.dasherize}:#{value}" }.join(';')
8
+  end
9
+
10
+  def scenario_label(scenario, text = nil)
11
+    text ||= scenario.name
12
+    content_tag :span, text, class: 'label scenario', style: style_colors(scenario)
13
+  end
14
+
15
+  def default_scenario_bg_color
16
+    '#5BC0DE'
17
+  end
18
+
19
+  def default_scenario_fg_color
20
+    '#FFFFFF'
21
+  end
22
+
23
+end

+ 6 - 1
app/models/scenario.rb

@@ -1,7 +1,7 @@
1 1
 class Scenario < ActiveRecord::Base
2 2
   include HasGuid
3 3
 
4
-  attr_accessible :name, :agent_ids, :description, :public, :source_url
4
+  attr_accessible :name, :agent_ids, :description, :public, :source_url, :tag_fg_color, :tag_bg_color
5 5
 
6 6
   belongs_to :user, :counter_cache => :scenario_count, :inverse_of => :scenarios
7 7
   has_many :scenario_memberships, :dependent => :destroy, :inverse_of => :scenario
@@ -9,6 +9,11 @@ class Scenario < ActiveRecord::Base
9 9
 
10 10
   validates_presence_of :name, :user
11 11
 
12
+  validates_format_of :tag_fg_color, :tag_bg_color,
13
+    # Regex adapted from: http://stackoverflow.com/a/1636354/3130625
14
+    :with => /\A#(?:[0-9a-fA-F]{3}){1,2}\z/, :allow_nil => true,
15
+    :message => "must be a valid hex color."
16
+
12 17
   validate :agents_are_owned
13 18
 
14 19
   protected

+ 5 - 1
app/models/scenario_import.rb

@@ -60,10 +60,14 @@ class ScenarioImport
60 60
     description = parsed_data['description']
61 61
     name = parsed_data['name']
62 62
     links = parsed_data['links']
63
+    tag_fg_color = parsed_data['tag_fg_color']
64
+    tag_bg_color = parsed_data['tag_bg_color']
63 65
     source_url = parsed_data['source_url'].presence || nil
64 66
     @scenario = user.scenarios.where(:guid => guid).first_or_initialize
65 67
     @scenario.update_attributes!(:name => name, :description => description,
66
-                                 :source_url => source_url, :public => false)
68
+                                 :source_url => source_url, :public => false,
69
+                                 :tag_fg_color => tag_fg_color,
70
+                                 :tag_bg_color => tag_bg_color)
67 71
 
68 72
     unless options[:skip_agents]
69 73
       created_agents = agent_diffs.map do |agent_diff|

+ 1 - 1
app/views/agents/_action_menu.html.erb

@@ -32,7 +32,7 @@
32 32
 
33 33
     <% agent.scenarios.each do |scenario| %>
34 34
       <li>
35
-        <%= link_to "<span class='color-warning glyphicon glyphicon-remove-circle'></span> Remove from <span class='scenario label label-info'>#{h scenario.name}</span>".html_safe, leave_scenario_agent_path(agent, :scenario_id => scenario.to_param, :return => returnTo), method: :put, :tabindex => "-1" %>
35
+        <%= link_to "<span class='color-warning glyphicon glyphicon-remove-circle'></span> Remove from #{scenario_label(scenario)}".html_safe, leave_scenario_agent_path(agent, :scenario_id => scenario.to_param, :return => returnTo), method: :put, :tabindex => "-1" %>
36 36
       </li>
37 37
     <% end %>
38 38
   <% end %>

+ 2 - 3
app/views/scenario_imports/_step_two.html.erb

@@ -13,9 +13,8 @@
13 13
       <div class="alert alert-warning">
14 14
         <span class='glyphicon glyphicon-warning-sign'></span>
15 15
         This Scenario already exists in your system. The import will update your existing
16
-        <span class='label label-info scenario'><%= @scenario_import.existing_scenario.name %></span> Scenario's title
17
-        and
18
-        description. Below you can customize how the individual agents get updated.
16
+        <%= scenario_label(@scenario_import.existing_scenario) %> Scenario's title,
17
+        description and tag colors. Below you can customize how the individual agents get updated.
19 18
       </div>
20 19
     <% end %>
21 20
 

+ 13 - 1
app/views/scenarios/_form.html.erb

@@ -15,6 +15,18 @@
15 15
         <%= f.text_field :name, :class => 'form-control', :placeholder => "Name your Scenario" %>
16 16
       </div>
17 17
     </div>
18
+    <div class="col-md-2">
19
+      <div class="form-group">
20
+        <%= f.label :tag_bg_color, "Tag Background Color" %>
21
+        <%= f.color_field :tag_bg_color, :class => 'form-control', :value => @scenario.tag_bg_color || default_scenario_bg_color %>
22
+      </div>
23
+    </div>
24
+    <div class="col-md-2">
25
+      <div class="form-group">
26
+        <%= f.label :tag_fg_color, "Tag Foreground Color" %>
27
+        <%= f.color_field :tag_fg_color, :class => 'form-control', :value => @scenario.tag_fg_color || default_scenario_fg_color %>
28
+      </div>
29
+    </div>
18 30
   </div>
19 31
 
20 32
   <div class="row">
@@ -54,4 +66,4 @@
54 66
       </div>
55 67
     </div>
56 68
   </div>
57
-<% end %>
69
+<% end %>

+ 2 - 1
app/views/scenarios/index.html.erb

@@ -21,6 +21,7 @@
21 21
         <% @scenarios.each do |scenario| %>
22 22
           <tr>
23 23
             <td>
24
+              <%= scenario_label(scenario, content_tag(:i, '', class: 'glyphicon glyphicon-font')) %>
24 25
               <%= link_to(scenario.name, scenario) %>
25 26
             </td>
26 27
             <td><%= link_to pluralize(scenario.agents.count, "agent"), scenario %></td>
@@ -47,4 +48,4 @@
47 48
       </div>
48 49
     </div>
49 50
   </div>
50
-</div>
51
+</div>

+ 2 - 2
app/views/scenarios/share.html.erb

@@ -2,7 +2,7 @@
2 2
   <div class='row'>
3 3
     <div class='col-md-12'>
4 4
       <div class="page-header">
5
-        <h2>Share <span class='label label-info scenario'><%= @scenario.name %></span> with the world</h2>
5
+        <h2>Share <%= scenario_label(@scenario) %> with the world</h2>
6 6
       </div>
7 7
 
8 8
       <p>
@@ -30,4 +30,4 @@
30 30
       </div>
31 31
     </div>
32 32
   </div>
33
-</div>
33
+</div>

+ 2 - 1
app/views/scenarios/show.html.erb

@@ -2,7 +2,8 @@
2 2
   <div class='row'>
3 3
     <div class='col-md-12'>
4 4
       <div class="page-header">
5
-        <h2><span class='label label-info scenario'><%= @scenario.name %></span> <%= "Public" if @scenario.public? %> Scenario</h2>
5
+        <h2><%= scenario_label(@scenario) %> <%= "Public" if @scenario.public? %> Scenario</h2>
6
+
6 7
       </div>
7 8
 
8 9
       <% if @scenario.description.present? %>

+ 6 - 0
db/migrate/20140820003139_add_tag_color_to_scenarios.rb

@@ -0,0 +1,6 @@
1
+class AddTagColorToScenarios < ActiveRecord::Migration
2
+  def change
3
+    add_column :scenarios, :tag_bg_color, :string
4
+    add_column :scenarios, :tag_fg_color, :string
5
+  end
6
+end

+ 3 - 1
db/schema.rb

@@ -11,7 +11,7 @@
11 11
 #
12 12
 # It's strongly recommended that you check this file into your version control system.
13 13
 
14
-ActiveRecord::Schema.define(version: 20140605032822) do
14
+ActiveRecord::Schema.define(version: 20140820003139) do
15 15
 
16 16
   create_table "agent_logs", force: true do |t|
17 17
     t.integer  "agent_id",                                       null: false
@@ -111,6 +111,8 @@ ActiveRecord::Schema.define(version: 20140605032822) do
111 111
     t.boolean  "public",      default: false, null: false
112 112
     t.string   "guid",                        null: false
113 113
     t.string   "source_url"
114
+    t.string   "tag_bg_color"
115
+    t.string   "tag_fg_color"
114 116
   end
115 117
 
116 118
   add_index "scenarios", ["user_id", "guid"], name: "index_scenarios_on_user_id_and_guid", unique: true, using: :btree

+ 3 - 1
lib/agents_exporter.rb

@@ -16,6 +16,8 @@ class AgentsExporter
16 16
       :description => options[:description].presence || 'No description provided',
17 17
       :source_url => options[:source_url],
18 18
       :guid => options[:guid],
19
+      :tag_fg_color => options[:tag_fg_color],
20
+      :tag_bg_color => options[:tag_bg_color],
19 21
       :exported_at => Time.now.utc.iso8601,
20 22
       :agents => agents.map { |agent| agent_as_json(agent) },
21 23
       :links => links
@@ -51,4 +53,4 @@ class AgentsExporter
51 53
       options[:propagate_immediately] = agent.propagate_immediately if agent.can_receive_events?
52 54
     end
53 55
   end
54
-end
56
+end

+ 2 - 0
spec/controllers/scenarios_controller_spec.rb

@@ -50,6 +50,8 @@ describe ScenariosController do
50 50
       assigns(:exporter).options[:description].should == scenarios(:bob_weather).description
51 51
       assigns(:exporter).options[:agents].should == scenarios(:bob_weather).agents
52 52
       assigns(:exporter).options[:guid].should == scenarios(:bob_weather).guid
53
+      assigns(:exporter).options[:tag_fg_color].should == scenarios(:bob_weather).tag_fg_color
54
+      assigns(:exporter).options[:tag_bg_color].should == scenarios(:bob_weather).tag_bg_color
53 55
       assigns(:exporter).options[:source_url].should be_falsey
54 56
       response.headers['Content-Disposition'].should == 'attachment; filename="bob-s-weather-alert-scenario.json"'
55 57
       response.headers['Content-Type'].should == 'application/json; charset=utf-8'

+ 30 - 0
spec/helpers/scenario_helper_spec.rb

@@ -0,0 +1,30 @@
1
+require 'spec_helper'
2
+
3
+describe ScenarioHelper do
4
+  let(:scenario) { users(:bob).scenarios.build(name: 'Scene', tag_fg_color: '#AAAAAA', tag_bg_color: '#000000') }
5
+
6
+  describe '#style_colors' do
7
+    it 'returns a css style-formated version of the scenario foreground and background colors' do
8
+      style_colors(scenario).should == "color:#AAAAAA;background-color:#000000"
9
+    end
10
+
11
+    it 'defauls foreground and background colors' do
12
+      scenario.tag_fg_color = nil
13
+      scenario.tag_bg_color = nil
14
+      style_colors(scenario).should == "color:#FFFFFF;background-color:#5BC0DE"
15
+    end
16
+  end
17
+
18
+  describe '#scenario_label' do
19
+    it 'creates a scenario label with the scenario name' do
20
+      scenario_label(scenario).should ==
21
+        '<span class="label scenario" style="color:#AAAAAA;background-color:#000000">Scene</span>'
22
+    end
23
+
24
+    it 'creates a scenario label with the given text' do
25
+      scenario_label(scenario, 'Other').should ==
26
+        '<span class="label scenario" style="color:#AAAAAA;background-color:#000000">Other</span>'
27
+    end
28
+  end
29
+
30
+end

+ 8 - 2
spec/lib/agents_exporter_spec.rb

@@ -7,9 +7,13 @@ describe AgentsExporter do
7 7
     let(:name) { "My set of Agents" }
8 8
     let(:description) { "These Agents work together nicely!" }
9 9
     let(:guid) { "some-guid" }
10
+    let(:tag_fg_color) { "#ffffff" }
11
+    let(:tag_bg_color) { "#000000" }
10 12
     let(:source_url) { "http://yourhuginn.com/scenarios/2/export.json" }
11 13
     let(:agent_list) { [agents(:jane_weather_agent), agents(:jane_rain_notifier_agent)] }
12
-    let(:exporter) { AgentsExporter.new(:agents => agent_list, :name => name, :description => description, :source_url => source_url, :guid => guid) }
14
+    let(:exporter) { AgentsExporter.new(
15
+      :agents => agent_list, :name => name, :description => description, :source_url => source_url,
16
+      :guid => guid, :tag_fg_color => tag_fg_color, :tag_bg_color => tag_bg_color) }
13 17
 
14 18
     it "outputs a structure containing name, description, the date, all agents & their links" do
15 19
       data = exporter.as_json
@@ -17,6 +21,8 @@ describe AgentsExporter do
17 21
       data[:description].should == description
18 22
       data[:source_url].should == source_url
19 23
       data[:guid].should == guid
24
+      data[:tag_fg_color].should == tag_fg_color
25
+      data[:tag_bg_color].should == tag_bg_color
20 26
       Time.parse(data[:exported_at]).should be_within(2).of(Time.now.utc)
21 27
       data[:links].should == [{ :source => 0, :receiver => 1 }]
22 28
       data[:agents].should == agent_list.map { |agent| exporter.agent_as_json(agent) }
@@ -58,4 +64,4 @@ describe AgentsExporter do
58 64
       AgentsExporter.new(:name => ",,").filename.should == "exported-agents.json"
59 65
     end
60 66
   end
61
-end
67
+end

+ 11 - 3
spec/models/scenario_import_spec.rb

@@ -3,6 +3,8 @@ require 'spec_helper'
3 3
 describe ScenarioImport do
4 4
   let(:user) { users(:bob) }
5 5
   let(:guid) { "somescenarioguid" }
6
+  let(:tag_fg_color) { "#ffffff" }
7
+  let(:tag_bg_color) { "#000000" }
6 8
   let(:description) { "This is a cool Huginn Scenario that does something useful!" }
7 9
   let(:name) { "A useful Scenario" }
8 10
   let(:source_url) { "http://example.com/scenarios/2/export.json" }
@@ -46,10 +48,12 @@ describe ScenarioImport do
46 48
     }
47 49
   end
48 50
   let(:valid_parsed_data) do
49
-    { 
51
+    {
50 52
       :name => name,
51 53
       :description => description,
52 54
       :guid => guid,
55
+      :tag_fg_color => tag_fg_color,
56
+      :tag_bg_color => tag_bg_color,
53 57
       :source_url => source_url,
54 58
       :exported_at => 2.days.ago.utc.iso8601,
55 59
       :agents => [
@@ -142,7 +146,7 @@ describe ScenarioImport do
142 146
       end
143 147
     end
144 148
   end
145
-  
149
+
146 150
   describe "#dangerous?" do
147 151
     it "returns false on most Agents" do
148 152
       ScenarioImport.new(:data => valid_data).should_not be_dangerous
@@ -171,6 +175,8 @@ describe ScenarioImport do
171 175
           scenario_import.scenario.name.should == name
172 176
           scenario_import.scenario.description.should == description
173 177
           scenario_import.scenario.guid.should == guid
178
+          scenario_import.scenario.tag_fg_color.should == tag_fg_color
179
+          scenario_import.scenario.tag_bg_color.should == tag_bg_color
174 180
           scenario_import.scenario.source_url.should == source_url
175 181
           scenario_import.scenario.public.should be_falsey
176 182
         end
@@ -269,6 +275,8 @@ describe ScenarioImport do
269 275
 
270 276
           existing_scenario.reload
271 277
           existing_scenario.guid.should == guid
278
+          existing_scenario.tag_fg_color.should == tag_fg_color
279
+          existing_scenario.tag_bg_color.should == tag_bg_color
272 280
           existing_scenario.description.should == description
273 281
           existing_scenario.name.should == name
274 282
           existing_scenario.source_url.should == source_url
@@ -408,4 +416,4 @@ describe ScenarioImport do
408 416
       end
409 417
     end
410 418
   end
411
-end
419
+end

+ 24 - 0
spec/models/scenario_spec.rb

@@ -20,6 +20,30 @@ describe Scenario do
20 20
       new_instance.should_not be_valid
21 21
     end
22 22
 
23
+    it "validates tag_fg_color is hex color" do
24
+      new_instance.tag_fg_color = '#N07H3X'
25
+      new_instance.should_not be_valid
26
+      new_instance.tag_fg_color = '#BADA55'
27
+      new_instance.should be_valid
28
+    end
29
+
30
+    it "allows nil tag_fg_color" do
31
+      new_instance.tag_fg_color = nil
32
+      new_instance.should be_valid
33
+    end
34
+
35
+    it "validates tag_bg_color is hex color" do
36
+      new_instance.tag_bg_color = '#N07H3X'
37
+      new_instance.should_not be_valid
38
+      new_instance.tag_bg_color = '#BADA55'
39
+      new_instance.should be_valid
40
+    end
41
+
42
+    it "allows nil tag_bg_color" do
43
+      new_instance.tag_bg_color = nil
44
+      new_instance.should be_valid
45
+    end
46
+
23 47
     it "only allows Agents owned by user" do
24 48
       new_instance.agent_ids = [agents(:bob_website_agent).id]
25 49
       new_instance.should be_valid