extensions.add(EventsWidget.class);
extensions.add(CustomMeasuresWidget.class);
extensions.add(TimelineWidget.class);
+ extensions.add(TimeMachineWidget.class);
// chart
extensions.add(XradarChart.class);
--- /dev/null
+/*
+ * 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
--- /dev/null
+<%
+ # 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<<row
+ rows_by_metric_id[measure.metric_id]=row
+ end
+
+ #optimization : avoid eager loading of snapshots
+ measure.snapshot=snapshot_by_id[measure.snapshot_id]
+ row.add_measure(measure)
+ end
+ end
+
+ rows.sort!
+%>
+
+<div class="widget-matrix">
+
+<table class="widget-matrix">
+
+ <thead>
+ <tr>
+ <th valign="top" nowrap="nowrap" style="background-color: #FFFFFF; border: 0px;">
+ </th>
+ <% snapshots.each do |snapshot| %>
+ <th nowrap="nowrap" align="right" valign="top">
+ <%= l snapshot.created_at.to_date %>
+ <br/>
+ <% snapshot.user_events.each do |event| %>
+ <b><%= event.name %></b>
+ <br/>
+ <% end %>
+ </th>
+ <% end %>
+ <th> </th>
+ </tr>
+ </thead>
+
+ <tbody>
+ <%
+ previous_domain=''
+ rows.select{|row| row.metric.val_type != Metric::VALUE_TYPE_DISTRIB}.each do |row|
+ if previous_domain != row.domain
+ %>
+ <tr>
+ <td class="title" colspan="<%= snapshots.size + 2 -%>"><%= row.domain %></td>
+ </tr>
+ <%
+ end
+ previous_domain = row.domain
+ %>
+ <tr class="<%= cycle("even", "odd", :name => row.domain) -%>">
+ <td width="1%" nowrap="nowrap" valign="top">
+ <%= row.metric.short_name %>
+ </td>
+ <%
+ snapshots.each do |snapshot|
+ measure=row.measure(snapshot)
+ %>
+ <td align="right" width="1%" nowrap="nowrap"><%= format_measure(measure, :skip_span_id => true) %></td>
+ <% end %>
+ <td>
+ <%
+ sparkline_url=row.sparkline_url
+ if displaySparkLine && sparkline_url
+ %>
+ <%= image_tag(sparkline_url) %>
+ <% end %>
+ </td>
+ </tr>
+ <% end %>
+ </tbody>
+
+</table>
+
+</div>
\ No newline at end of file
<%= javascript_include_tag 'protovis-sonar' %>
<%
+ # Retrieve widget settings
metric_data_map = {}
metric_name_map = {}
(1..3).each do |index|
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 = {}
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"] %>
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
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
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;