]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-1956 support variations in treemaps
authorsimonbrandhof <simon.brandhof@gmail.com>
Fri, 10 Dec 2010 17:56:55 +0000 (17:56 +0000)
committersimonbrandhof <simon.brandhof@gmail.com>
Fri, 10 Dec 2010 17:56:55 +0000 (17:56 +0000)
sonar-server/src/main/webapp/WEB-INF/app/controllers/filters_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/helpers/filters_helper.rb
sonar-server/src/main/webapp/WEB-INF/app/models/measure_color.rb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/models/metric.rb
sonar-server/src/main/webapp/WEB-INF/app/models/project_measure.rb
sonar-server/src/main/webapp/WEB-INF/app/models/sonar/treemap.rb
sonar-server/src/main/webapp/WEB-INF/app/models/sonar/treemap_builder.rb
sonar-server/src/main/webapp/WEB-INF/app/views/filters/_treemap.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/filters/index.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/filters/treemap.html.erb

index 69644b74a1ff5c7010fe7bb68c716485ad401ba8..d1be0ad873e3012523df8a965340838f503cfbe9 100644 (file)
@@ -333,7 +333,8 @@ class FiltersController < ApplicationController
 
     filter.columns.clear
     params[:columns].each do |colstring|
-      filter.columns<<::FilterColumn.create_from_string(colstring)
+      column=::FilterColumn.create_from_string(colstring)
+      filter.columns<<column if column
     end
     filter.save
     redirect_to :action => :edit, :id => filter.id
@@ -406,7 +407,9 @@ class FiltersController < ApplicationController
 
     @width=(params[:width]||'800').to_i
     @height=(params[:height]||'500').to_i
-    @treemap=Sonar::Treemap.new(@filter_context.measures_by_snapshot, @width, @height, @size_metric, @color_metric)
+
+    treemap_options={:variation_index => @filter_context.variation_index}
+    @treemap=Sonar::Treemap.new(@filter_context.measures_by_snapshot, @width, @height, @size_metric, @color_metric, treemap_options)
     render :action => "treemap", :layout => false
   end
 
index 35bea1ad09146f53e2e67b439aa2fc98dd26bf46..159341710d10a54a06cd3aa65956bd0925ee0fea 100644 (file)
@@ -34,7 +34,7 @@ module FiltersHelper
     end
 
     if filter.favourites
-      java_filter.setFavouriteIds((filter_context.user ? user.favourite_ids : []).to_java(:Integer))
+      java_filter.setFavouriteIds((filter_context.user ? filter_context.user.favourite_ids : []).to_java(:Integer))
     end
 
     date_criterion=filter.criterion('date')
@@ -133,7 +133,13 @@ module FiltersHelper
   def treemap_metrics(filter)
     metrics=filter.measure_columns.map{|col| col.metric}
     size_metric=(metrics.size>=1 ? metrics[0] : Metric.by_key('ncloc'))
-    color_metric=(metrics.size>=2 ? metrics[1] : Metric.by_key('violations_density'))
+    color_metric=nil
+    if metrics.size>=2
+      color_metric=metrics[1]
+    end
+    if color_metric.nil? || !color_metric.treemap_color?
+      color_metric=Metric.by_key('violations_density')
+    end
     [size_metric, color_metric]
   end
 
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/measure_color.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/measure_color.rb
new file mode 100644 (file)
index 0000000..4f8afc5
--- /dev/null
@@ -0,0 +1,80 @@
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2009 SonarSource SA
+# mailto:contact AT sonarsource DOT com
+#
+# Sonar is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# Sonar is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with Sonar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+#
+class MeasureColor
+
+  MIN_COLOR=Color::RGB.from_html("EE0000")   # red
+  MEAN_COLOR=Color::RGB.from_html("FFEE00")   # orange
+  MAX_COLOR=Color::RGB.from_html("00AA00")   # green
+  NONE_COLOR=Color::RGB.from_html("DDDDDD")   # gray
+
+  #
+  # Options :
+  #  * min : min value, else the metric worst_value
+  #  * max : max value, else the metric best_value
+  #  * variation_index: integer between 1 and 5 if set, else nil
+  #  * check_alert_status: true|false. Default is true.
+  #
+  def self.color(measure, options={})
+    return NONE_COLOR if measure.nil?
+
+    max_value = options[:max] || measure.metric.best_value
+    min_value = options[:min] || measure.metric.worst_value
+    percent=-1.0
+    
+    if options[:variation_index]
+      if min_value && max_value
+        value=measure.variation(options[:variation_index])
+        percent = value_to_percent(value, min_value, max_value)
+      end
+    else
+      if !measure.alert_status.blank? && (options[:check_alert_status]||true)
+        case(measure.alert_status)
+          when Metric::TYPE_LEVEL_OK : percent=100.0
+          when Metric::TYPE_LEVEL_ERROR : percent=0.0
+          when Metric::TYPE_LEVEL_WARN : percent=50.0
+        end
+      elsif measure.metric.value_type==Metric::VALUE_TYPE_LEVEL
+        case(measure.text_value)
+          when Metric::TYPE_LEVEL_OK : percent=100.0
+          when Metric::TYPE_LEVEL_WARN : percent=50.0
+          when Metric::TYPE_LEVEL_ERROR : percent=0.0
+        end
+      elsif measure.value && max_value && min_value
+        percent = value_to_percent(measure.value, min_value, max_value)
+      end
+    end
+
+    if percent<0.0
+      NONE_COLOR
+    elsif (percent > 50.0)
+      MAX_COLOR.mix_with(MEAN_COLOR, (percent - 50.0) * 2.0)
+    else
+      MIN_COLOR.mix_with(MEAN_COLOR, (50.0 - percent) * 2.0)
+    end
+  end
+
+
+  def self.value_to_percent(value, min, max)
+    percent = 100.0 * (value.to_f - min.to_f) / (max.to_f - min.to_f)
+    percent=100.0 if percent>100.0
+    percent=0.0 if percent<0.0
+    percent
+  end
+end
index 21a08a206164d2eed4d37fe3608aac0ab26fb46f..cc680cf71063354eae515d57bc99dce572054895 100644 (file)
@@ -194,6 +194,14 @@ class Metric < ActiveRecord::Base
       'direction' => direction.to_i, 'val_type' => val_type.to_s, 'hidden' => hidden}
   end
 
+  def treemap_color?
+    enabled && !hidden && ((numeric? && worst_value && best_value) || val_type==Metric::VALUE_TYPE_LEVEL)
+  end
+
+  def treemap_size?
+    enabled && !hidden && numeric? && !domain.blank?
+  end
+
   def to_xml(options={})
     xml = Builder::XmlMarkup.new
     xml.metric do
index cd5f7c8e6189565c83e7a2c52ef024c81ecf5013..5c45f336390e402f93c824a8c3b116c04b4ae50c 100644 (file)
@@ -150,39 +150,10 @@ class ProjectMeasure < ActiveRecord::Base
     end
   end
 
-
-  MIN_COLOR=Color::RGB.from_html("EE0000")   # red
-  MEAN_COLOR=Color::RGB.from_html("FFEE00")   # orange
-  MAX_COLOR=Color::RGB.from_html("00AA00")   # green
-
   def color
     @color ||=
       begin
-        percent=-1.0
-        if !alert_status.blank?
-          case(alert_status)
-            when Metric::TYPE_LEVEL_OK : percent=100.0
-            when Metric::TYPE_LEVEL_ERROR : percent=0.0
-            when Metric::TYPE_LEVEL_WARN : percent=50.0
-          end
-        elsif metric.value_type==Metric::VALUE_TYPE_LEVEL
-          case(text_value)
-            when Metric::TYPE_LEVEL_OK : percent=100.0
-            when Metric::TYPE_LEVEL_WARN : percent=50.0
-            when Metric::TYPE_LEVEL_ERROR : percent=0.0
-          end
-        elsif value && metric.worst_value && metric.best_value
-          percent = 100.0 * (value.to_f - metric.worst_value.to_f) / (metric.best_value.to_f - metric.worst_value.to_f)
-          percent=100.0 if percent>100.0
-          percent=0.0 if percent<0.0
-        end
-        if percent<0.0
-          nil
-        elsif (percent > 50.0)
-          MAX_COLOR.mix_with(MEAN_COLOR, (percent - 50.0) * 2.0)
-        else
-          MIN_COLOR.mix_with(MEAN_COLOR, (50.0 - percent) * 2.0)
-        end
+        MeasureColor.color(self)
       end
   end
 
index 3892d709a6d404be2c5c1bdf031572e2a9fd1e5d..a7b6c931adb7dfc900fa9546378044ee4018a878 100644 (file)
@@ -22,12 +22,16 @@ class Sonar::Treemap
   
   attr_accessor :color_metric, :size_metric, :width, :height
   
-  def initialize(measures_by_snapshot, width, height, size_metric, color_metric)
+  def initialize(measures_by_snapshot, width, height, size_metric, color_metric, options={})
     @measures_by_snapshot = measures_by_snapshot
     @size_metric = size_metric
-    @color_metric = color_metric
+    @color_metric = color_metric if color_metric && color_metric.treemap_color?
     @width = width
     @height = height
+
+    if options[:variation_index] && options[:variation_index]>0
+      @variation_index = options[:variation_index]
+    end
   end
   
   def generate_html
@@ -50,41 +54,55 @@ class Sonar::Treemap
     id_counter = 0
     @measures_by_snapshot.each_pair do |snapshot, measures|
       size_measure=measures[@size_metric]
-      color_measure=measures[@color_metric]
+      color_measure=measures[@color_metric] if @color_metric
 
       if size_measure
         resource = snapshot.project
         child = Treemap::Node.new( :id => (id_counter += 1), 
-          :size => size_measure.value.to_f||0, 
+          :size => size_value(size_measure),
           :label => resource.name(false),
           :title => escape_javascript(resource.name(true)),
           :tooltip => get_html_tooltip(snapshot, size_measure, color_measure),
           :color => html_color(color_measure),
-          :url => get_url(snapshot,color_measure))
+          :url => get_url(snapshot))
         node.add_child(child)
       end
     end
   end 
 
   
-  def get_url(snapshot,color_measure)
+  def get_url(snapshot)
     if snapshot.display_dashboard?
       "document.location='#{ApplicationController.root_context}/dashboard/index/#{snapshot.project.copy_resource_id || snapshot.project_id}'"
     else
-      "window.open('#{ApplicationController.root_context}/resource/index/#{snapshot.project_id}?viewer_metric_key=#{@color_metric.key}','resource','height=800,width=900,scrollbars=1,resizable=1');return false;"
+      color_metric_key=(@color_metric ? @color_metric.key : nil)
+      "window.open('#{ApplicationController.root_context}/resource/index/#{snapshot.project_id}?viewer_metric_key=#{color_metric_key}','resource','height=800,width=900,scrollbars=1,resizable=1');return false;"
     end 
   end
     
   def get_html_tooltip(snapshot, size_measure, color_measure)
     html = "<table>"
     html += "<tr><td align=left>#{escape_javascript(@size_metric.short_name)}</td><td align=right><b>#{escape_javascript(size_measure ? size_measure.formatted_value : '-')}</b></td></tr>"
-    html += "<tr><td align=left>#{escape_javascript(@color_metric.short_name)}</td><td align=right><b>#{escape_javascript(color_measure ? color_measure.formatted_value : '-')}</b></td></tr>"
+    if color_measure
+      html += "<tr><td align=left>#{escape_javascript(@color_metric.short_name)}</td><td align=right><b>#{escape_javascript(color_measure ? color_measure.formatted_value : '-')}</b></td></tr>"
+    end
     html += "</table>"
     html
   end
+
+  def size_value(measure)
+    if @variation_index
+      var=measure.variation(@variation_index)
+      var ? var.to_f.abs : 0.0
+    elsif measure.value
+      measure.value.to_f.abs||0.0
+    else
+      0.0
+    end
+  end
   
   def html_color(measure)
-    measure ? measure.color.html : '#DDDDDD'
+    MeasureColor.color(measure).html
   end
 
 end
@@ -97,10 +115,10 @@ class Sonar::HtmlOutput < Treemap::HtmlOutput
 
     html = ""
     html += "<div id=\"node-#{node.id}\" style=\""
-    html += "overflow: hidden; position:absolute;"
-    html += "left: #{node.bounds.x1}px; top: #{node.bounds.y1}px;"
-    html += "width: #{node.bounds.width}px; height: #{node.bounds.height}px;"
-    html += "background-color: #FFF;"
+    html += "overflow:hidden;position:absolute;"
+    html += "left:#{node.bounds.x1}px; top:#{node.bounds.y1}px;"
+    html += "width:#{node.bounds.width}px;height: #{node.bounds.height}px;"
+    html += "background-color:#FFF;"
     html += "\" class=\"node\">"
     html += "<div id=\"link_node-#{node.id}\" style='margin: 2px;background-color: #{node.color}; height: #{node.bounds.height-4}px; border: 1px solid #{node.color};' "
     if node.url && @details_at_depth==node.depth
index 7b80d4858c8035c8db63d683309ff83e18782067..d79914f4be3ca50bb580b0762a2930845b6ca10b 100644 (file)
@@ -27,13 +27,13 @@ class Sonar::TreemapBuilder
   def self.size_metrics(options={})
     exclude_user_managed=options[:exclude_user_managed]||false
     Metric.all.select{ |metric| 
-      metric.enabled && !metric.hidden && metric.numeric? && !metric.domain.blank? && (!exclude_user_managed || !metric.user_managed?)
+      metric.treemap_size? && (!exclude_user_managed || !metric.user_managed?)
     }.sort
   end
 
   def self.color_metrics
     Metric.all.select{ |metric|
-      metric.enabled && !metric.hidden && ((metric.numeric? && metric.worst_value && metric.best_value) || metric.val_type==Metric::VALUE_TYPE_LEVEL)
+      metric.treemap_color?
     }.sort
   end
   
index 983ffb96c7ded412115bebd986aec2b57f1eeae1..ee040078283460c812070007b80f0dcb25b6f70a 100644 (file)
@@ -10,12 +10,12 @@ color_metric=metrics[1]
 <script>
 var treemap_width = $('treemap').getDimensions().width - 15;
 var treemap_height = document.viewport.getDimensions().height - 220;
-function load_treemap(size_metric, color_metric, hide_form) {
+function load_treemap(size_metric, color_metric, hide_form, variation_index) {
   $('treemap_loading').show();
   $('treemap').hide();
-  <%= remote_function :update => 'treemap', :url => {:action => 'treemap', :id => @filter.id},
+  <%= remote_function :update => 'treemap', :url => {:action => 'treemap', :id => @filter.id, :show_periods => true},
    :complete => "$('treemap_loading').hide();$('treemap').show();",
-   :with => "'width=' + treemap_width + '&height=' + treemap_height + '&size_metric=' + size_metric + '&color_metric=' + color_metric + '&hide_form=' + hide_form" %>
+   :with => "'width=' + treemap_width + '&height=' + treemap_height + '&size_metric=' + size_metric + '&color_metric=' + color_metric + '&hide_form=' + hide_form + '&var=' + variation_index" %>
 }
-load_treemap('<%= size_metric.key -%>', '<%= color_metric.key -%>', <%= edit_mode -%>);
+load_treemap('<%= size_metric.key -%>', '<%= color_metric.key -%>', <%= edit_mode -%>, <%= params[:var].to_i -%>);
 </script>
\ No newline at end of file
index 901852cabe5557c8134ff2509ec6bec6cb02db68..96bb7d2c6a67f8769028562648f877427654ffae 100644 (file)
@@ -1,4 +1,4 @@
 <%= render :partial => 'filters/tabs', :locals => {:selected_tab=> (@active && @active.filter ? @active.filter.id : nil) } %>
 <div class="tabs-panel">
-<%= render :partial => "filters/#{@filter.default_view}", :locals => {:edit_mode => false } if @filter %>
+<%= render :partial => "filters/#{@filter.default_view}", :locals => {:edit_mode => false} if @filter %>
 </div>
\ No newline at end of file
index dbe6685989b5e59cfa7796da3e0f388054314e0c..4fa5b299e5908c87f78a09c43e43ff503ef45762 100644 (file)
       <%= select_tag 'color_metric', options_grouped_by_domain(Sonar::TreemapBuilder.color_metrics, @color_metric.key),
         :id => 'color_metric', :class => 'small', :onchange => "load_treemap(this.form.size_metric.value,this.form.color_metric.value, false);return false;" %>
     </td>
+    <% if params[:show_periods]=='true' %>
+    <td class="sep"> </td>
+    <td>
+      <span class="comments">Period:</span>
+      <br/>
+      <select name="var" onchange="submit()" class="small">
+        <option value="">None</option>
+        <% period_names.each_with_index do |name, index| %>
+           <option value="<%= index+1 -%>" <%= 'selected' if params[:var].to_i==index+1 -%>><%= name -%></value>
+        <% end %>
+      </select>
+    </td>
+    <% end %>
   </tr>
 </table>
 </form>