From: Fabrice Bellingard Date: Thu, 25 Aug 2011 14:29:16 +0000 (+0200) Subject: SONAR-2701 New "Time Machine" widget X-Git-Tag: 2.11^2~128 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=b667702b4608edd0dc8f461a165a33cabb7e08a7;p=sonarqube.git SONAR-2701 New "Time Machine" widget Allows to display measures of required metrics by past version --- diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java index 4a30f0daac8..fd6b705e6bc 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java @@ -215,6 +215,7 @@ public class CorePlugin extends SonarPlugin { extensions.add(EventsWidget.class); extensions.add(CustomMeasuresWidget.class); extensions.add(TimelineWidget.class); + extensions.add(TimeMachineWidget.class); // chart extensions.add(XradarChart.class); diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimeMachineWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimeMachineWidget.java new file mode 100644 index 00000000000..5d8af7e9a35 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimeMachineWidget.java @@ -0,0 +1,58 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * 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 + */ +package org.sonar.plugins.core.widgets; + +import org.sonar.api.web.AbstractRubyTemplate; +import org.sonar.api.web.RubyRailsWidget; +import org.sonar.api.web.WidgetProperties; +import org.sonar.api.web.WidgetProperty; +import org.sonar.api.web.WidgetPropertyType; + +@WidgetProperties( + { + @WidgetProperty(key = "numberOfVersions", type = WidgetPropertyType.INTEGER, defaultValue = "4"), + @WidgetProperty(key = "displaySparkLine", type = WidgetPropertyType.BOOLEAN), + @WidgetProperty(key = "metric1", type = WidgetPropertyType.METRIC, defaultValue = "ncloc"), + @WidgetProperty(key = "metric2", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric3", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric4", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric5", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric6", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric7", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric8", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric9", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric10", type = WidgetPropertyType.METRIC) + } +) +public class TimeMachineWidget extends AbstractRubyTemplate implements RubyRailsWidget { + public String getId() { + return "time_machine"; + } + + public String getTitle() { + return "Time Machine"; + } + + @Override + protected String getTemplatePath() { + return "/org/sonar/plugins/core/widgets/time_machine.html.erb"; + //return "/Users/fbellingard/Documents/Sonar/repos/sonar/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/time_machine.html.erb"; + } +} \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/time_machine.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/time_machine.html.erb new file mode 100644 index 00000000000..e1d0aee470f --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/time_machine.html.erb @@ -0,0 +1,114 @@ +<% + # Retrieve widget settings + metric_ids = [] + (1..10).each do |index| + metric=widget_properties["metric#{index}"] + if metric + metric_ids << metric.id + end + end + if metric_ids.empty? + # No metric has been selected, it's the first time the widget is displayed: 'ncloc' is the default metric + ncloc = Metric.find(:first, :conditions => "name = 'ncloc'") + metric_ids << ncloc.id + end + numberOfVersions = widget_properties["numberOfVersions"].to_i == 0 ? 4 : widget_properties["numberOfVersions"].to_i + displaySparkLine = widget_properties["displaySparkLine"] + + # Retrieve the measures for each metric on each snapshot + options = {} + from_date = dashboard_configuration.from_datetime + if from_date + options[:from] = from_date + end + snapshots=Snapshot.for_timemachine_widget(@resource, numberOfVersions, options) + sids = snapshots.collect{|s| s.id}.uniq + measures=ProjectMeasure.find(:all, :conditions => {:snapshot_id => sids, :metric_id => metric_ids}) + + # And prepare the rows to display + snapshot_by_id={} + snapshots.each do |s| + snapshot_by_id[s.id]=s + end + rows_by_metric_id={} + rows=[] + measures.each do |measure| + next unless measure.metric + + if measure.metric.timemachine? && (measure.value || measure.text_value) + row=rows_by_metric_id[measure.metric_id] + unless row + row=Sonar::TimemachineRow.new(measure.metric) + rows< + +
+ + + + + + + <% snapshots.each do |snapshot| %> + + <% end %> + + + + + + <% + previous_domain='' + rows.select{|row| row.metric.val_type != Metric::VALUE_TYPE_DISTRIB}.each do |row| + if previous_domain != row.domain + %> + + + + <% + end + previous_domain = row.domain + %> + row.domain) -%>"> + + <% + snapshots.each do |snapshot| + measure=row.measure(snapshot) + %> + + <% end %> + + + <% end %> + + +
+ + <%= l snapshot.created_at.to_date %> +
+ <% snapshot.user_events.each do |event| %> + <%= event.name %> +
+ <% end %> +
<%= row.domain %>
+ <%= row.metric.short_name %> + <%= format_measure(measure, :skip_span_id => true) %> + <% + sparkline_url=row.sparkline_url + if displaySparkLine && sparkline_url + %> + <%= image_tag(sparkline_url) %> + <% end %> +
+ +
\ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/timeline.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/timeline.html.erb index ad44fbac17a..806d77f7a7c 100644 --- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/timeline.html.erb +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/timeline.html.erb @@ -1,5 +1,6 @@ <%= javascript_include_tag 'protovis-sonar' %> <% + # Retrieve widget settings metric_data_map = {} metric_name_map = {} (1..3).each do |index| @@ -11,10 +12,11 @@ end if metric_data_map.empty? # No metric has been selected, it's the first time the widget is displayed: 'ncloc' is the default metric - ncloc = Metric.find(:all, :conditions => "name = 'ncloc'").first + ncloc = Metric.find(:first, :conditions => "name = 'ncloc'") metric_data_map[ncloc.id] = [] metric_name_map[ncloc.id] = message('metric.ncloc.name') end + chartHeight = widget_properties["chartHeight"].to_i == 0 ? "null" : widget_properties["chartHeight"] # Retrieve metric trend information options = {} @@ -98,9 +100,7 @@ js_translations = "{" js_translations += "\"date\":\"" + message("date") + "\"" js_translations += "}" - - # Check if the widget height was specified - chartHeight = widget_properties["chartHeight"].to_i == 0 ? "null" : widget_properties["chartHeight"] + %> <% if widget_properties["chartTitle"] %> diff --git a/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties b/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties index c0754fd8e2f..97560a509c0 100644 --- a/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties +++ b/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties @@ -514,6 +514,9 @@ widget.timeline.name=Timeline widget.timeline.description=Displays up to 3 metrics on a history chart. widget.timeline.timeline_not_displayed=The timeline won't be displayed as currently only 1 snapshot is available. +widget.time_machine.name=Time Machine +widget.time_machine.description=Displays up to 10 metrics in a table, showing their value for a specified number of past snapshots. + widget.ckjm.name=Chidamber & Kemerer widget.ckjm.description=Reports on LCOM4 and RFC average and distribution. widget.ckjm.lcom4=LCOM4 diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/snapshot.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/snapshot.rb index 5a6fd0ea838..bc1d8d5e9ce 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/snapshot.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/snapshot.rb @@ -67,6 +67,26 @@ class Snapshot < ActiveRecord::Base snapshots.compact.uniq end + + def self.for_timemachine_widget(resource, number_of_versions, options={}) + conditions = ["events.category=? AND events.resource_id=?", "Version", resource.id] + if (options[:from]) + conditions[0] += " AND events.event_date>=?" + conditions << options[:from] + end + + events = Event.find(:all, :conditions => conditions, :order => 'events.event_date ASC') + events_to_display = events + if number_of_versions < events.size + events_to_display = [events.first] + events[events.size-number_of_versions+1 .. events.size-1] + end + sids = [] + events_to_display.each() do |event| + sids << event.snapshot_id + end + + snapshots=Snapshot.find(:all, :conditions => ["snapshots.id IN (?)", sids], :include => 'events', :order => 'snapshots.created_at ASC') + end def last? islast diff --git a/sonar-server/src/main/webapp/stylesheets/style.css b/sonar-server/src/main/webapp/stylesheets/style.css index 39f88114558..00f2bfb027f 100644 --- a/sonar-server/src/main/webapp/stylesheets/style.css +++ b/sonar-server/src/main/webapp/stylesheets/style.css @@ -1792,6 +1792,37 @@ table.matrix tbody td.title { padding: 5px 0 0 5px; } +div.widget-matrix { + overflow:auto; +} + +table.widget-matrix { + font-size: 12px; +} + +table.widget-matrix thead { + background-color: #CAE3F2; +} + +table.widget-matrix thead th { + text-align: right; + border: 1px solid #4b9fd5; + padding: 2px; + font-size: 11px; +} + +table.widget-matrix tbody td.title { + border: none; + font-weight: bold; + padding: 5px 0 0 0px; +} + +table.widget-matrix tbody td { + border: 1px solid #ddd; + margin: 0; + padding: 1px 0 1px 10px; +} + a.nolink, .dashbox a, .dashbox a:visited { text-decoration: none;