import org.sonar.plugins.core.security.ApplyProjectRolesDecorator;
import org.sonar.plugins.core.sensors.*;
import org.sonar.plugins.core.testdetailsviewer.TestsViewerDefinition;
-import org.sonar.plugins.core.timemachine.DifferentialValueDecorator;
+import org.sonar.plugins.core.timemachine.VariationDecorator;
import org.sonar.plugins.core.timemachine.PeriodLocator;
import org.sonar.plugins.core.timemachine.TendencyDecorator;
import org.sonar.plugins.core.timemachine.TimeMachineConfiguration;
// time machine
extensions.add(TendencyDecorator.class);
extensions.add(PeriodLocator.class);
- extensions.add(DifferentialValueDecorator.class);
+ extensions.add(VariationDecorator.class);
extensions.add(TimeMachineConfiguration.class);
return extensions;
+++ /dev/null
-/*
- * Sonar, open source software quality management 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
- */
-package org.sonar.plugins.core.timemachine;
-
-import com.google.common.collect.Maps;
-import org.apache.commons.configuration.Configuration;
-import org.apache.commons.lang.ObjectUtils;
-import org.sonar.api.batch.*;
-import org.sonar.api.database.DatabaseSession;
-import org.sonar.api.database.model.MeasureModel;
-import org.sonar.api.database.model.Snapshot;
-import org.sonar.api.measures.*;
-import org.sonar.api.qualitymodel.Characteristic;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Resource;
-import org.sonar.api.resources.ResourceUtils;
-import org.sonar.api.rules.RulePriority;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-@DependedUpon(DecoratorBarriers.END_OF_TIME_MACHINE)
-public class DifferentialValueDecorator implements Decorator {
-
- private Snapshot[] projectTargetSnapshots;
- private Map<Integer, Metric> metricByIds;
- private DatabaseSession session;
-
- public DifferentialValueDecorator(DatabaseSession session, PeriodLocator periodLocator, Configuration configuration, MetricFinder metricFinder) {
- this.session = session;
- Snapshot snapshot = periodLocator.locate(5);
- projectTargetSnapshots = new Snapshot[]{snapshot};
- initMetrics(metricFinder.findAll());
- }
-
- /**
- * only for unit tests
- */
- DifferentialValueDecorator(DatabaseSession session, Snapshot[] projectTargetSnapshots, Collection<Metric> metrics) {
- this.session = session;
- this.projectTargetSnapshots = projectTargetSnapshots;
- initMetrics(metrics);
- }
-
- private void initMetrics(Collection<Metric> metrics) {
- this.metricByIds = Maps.newHashMap();
- for (Metric metric : metrics) {
- if (metric.isNumericType()) {
- metricByIds.put(metric.getId(), metric);
- }
- }
- }
-
- public boolean shouldExecuteOnProject(Project project) {
- return true;
- }
-
- @DependsUpon
- public Collection<Metric> dependsUponMetrics() {
- return metricByIds.values();
- }
-
- static boolean shouldCalculateDiffValues(Resource resource) {
- // measures on files are currently purged, so past measures are not available
- return !ResourceUtils.isEntity(resource);
- }
-
- public void decorate(Resource resource, DecoratorContext context) {
- if (shouldCalculateDiffValues(resource)) {
- for (int index = 0; index < projectTargetSnapshots.length; index++) {
- Snapshot projectTargetSnapshot = projectTargetSnapshots[index];
- if (projectTargetSnapshot != null) {
- calculateDiffValues(resource, context, index, projectTargetSnapshot);
- }
- }
- }
- }
-
- private void calculateDiffValues(Resource resource, DecoratorContext context, int index, Snapshot projectTargetSnapshot) {
- List<MeasureModel> pastMeasures = selectPastMeasures(resource.getId(), projectTargetSnapshot);
- compareWithPastMeasures(context, index, pastMeasures);
- }
-
- void compareWithPastMeasures(DecoratorContext context, int index, List<MeasureModel> pastMeasures) {
- Map<MeasureKey, MeasureModel> pastMeasuresByKey = Maps.newHashMap();
- for (MeasureModel pastMeasure : pastMeasures) {
- pastMeasuresByKey.put(new MeasureKey(pastMeasure), pastMeasure);
- }
-
- // for each measure, search equivalent past measure
- for (Measure measure : context.getMeasures(MeasuresFilters.all())) {
- // compare with past measure
- MeasureModel pastMeasure = pastMeasuresByKey.get(new MeasureKey(measure));
- if (updateDiffValue(measure, pastMeasure, index)) {
- context.saveMeasure(measure);
- }
- }
- }
-
- boolean updateDiffValue(Measure measure, MeasureModel pastMeasure, int index) {
- boolean updated = false;
- if (pastMeasure != null && pastMeasure.getValue() != null && measure.getValue() != null) {
- double diff = (measure.getValue().doubleValue() - pastMeasure.getValue().doubleValue());
- updated = true;
- switch (index) {
- case 0:
- measure.setDiffValue1(diff);
- break;
- case 1:
- measure.setDiffValue2(diff);
- break;
- case 2:
- measure.setDiffValue3(diff);
- break;
- default:
- updated = false;
- }
- }
- return updated;
- }
-
- List<MeasureModel> selectPastMeasures(int resourceId, Snapshot projectTargetSnapshot) {
- // improvements : keep query in cache ? select only some columns ?
- // TODO support measure on rules and characteristics
- String hql = "select m from " + MeasureModel.class.getSimpleName() + " m, " + Snapshot.class.getSimpleName() + " s " +
- "where m.snapshotId=s.id and m.metricId in (:metricIds) and m.ruleId=null and m.rulePriority=null and m.rulesCategoryId=null and m.characteristic=null "
- + "and (s.rootId=:rootSnapshotId or s.id=:rootSnapshotId) and s.resourceId=:resourceId and s.status=:status";
- return session.createQuery(hql)
- .setParameter("metricIds", metricByIds.keySet())
- .setParameter("rootSnapshotId", ObjectUtils.defaultIfNull(projectTargetSnapshot.getRootId(), projectTargetSnapshot.getId()))
- .setParameter("resourceId", resourceId)
- .setParameter("status", Snapshot.STATUS_PROCESSED)
- .getResultList();
- }
-
- @Override
- public String toString() {
- return getClass().toString();
- }
-
- static class MeasureKey {
- Integer metricId;
- Integer ruleId;
- Integer categoryId;
- RulePriority priority;
- Characteristic characteristic;
-
- MeasureKey(MeasureModel model) {
- metricId = model.getMetricId();
- ruleId = model.getRuleId();
- categoryId = model.getRulesCategoryId();
- priority = model.getRulePriority();
- characteristic = model.getCharacteristic();
- }
-
- MeasureKey(Measure measure) {
- metricId = measure.getMetric().getId();
- characteristic = measure.getCharacteristic();
- // TODO merge RuleMeasure into Measure
- if (measure instanceof RuleMeasure) {
- RuleMeasure rm = (RuleMeasure) measure;
- categoryId = rm.getRuleCategory();
- ruleId = (rm.getRule()==null ? null : rm.getRule().getId());
- priority = rm.getRulePriority();
- }
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- MeasureKey that = (MeasureKey) o;
-
- if (categoryId != null ? !categoryId.equals(that.categoryId) : that.categoryId != null) return false;
- if (characteristic != null ? !characteristic.equals(that.characteristic) : that.characteristic != null)
- return false;
- if (!metricId.equals(that.metricId)) return false;
- if (priority != that.priority) return false;
- if (ruleId != null ? !ruleId.equals(that.ruleId) : that.ruleId != null) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = metricId.hashCode();
- result = 31 * result + (ruleId != null ? ruleId.hashCode() : 0);
- result = 31 * result + (categoryId != null ? categoryId.hashCode() : 0);
- result = 31 * result + (priority != null ? priority.hashCode() : 0);
- result = 31 * result + (characteristic != null ? characteristic.hashCode() : 0);
- return result;
- }
- }
-}
--- /dev/null
+/*
+ * Sonar, open source software quality management 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
+ */
+package org.sonar.plugins.core.timemachine;
+
+import com.google.common.collect.Maps;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.lang.ObjectUtils;
+import org.sonar.api.batch.*;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.MeasureModel;
+import org.sonar.api.database.model.Snapshot;
+import org.sonar.api.measures.*;
+import org.sonar.api.qualitymodel.Characteristic;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
+import org.sonar.api.rules.RulePriority;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+@DependedUpon(DecoratorBarriers.END_OF_TIME_MACHINE)
+public class VariationDecorator implements Decorator {
+
+ private Snapshot[] projectTargetSnapshots;
+ private Map<Integer, Metric> metricByIds;
+ private DatabaseSession session;
+
+ public VariationDecorator(DatabaseSession session, PeriodLocator periodLocator, Configuration configuration, MetricFinder metricFinder) {
+ this.session = session;
+ Snapshot snapshot = periodLocator.locate(5);
+ projectTargetSnapshots = new Snapshot[]{snapshot};
+ initMetrics(metricFinder.findAll());
+ }
+
+ /**
+ * only for unit tests
+ */
+ VariationDecorator(DatabaseSession session, Snapshot[] projectTargetSnapshots, Collection<Metric> metrics) {
+ this.session = session;
+ this.projectTargetSnapshots = projectTargetSnapshots;
+ initMetrics(metrics);
+ }
+
+ private void initMetrics(Collection<Metric> metrics) {
+ this.metricByIds = Maps.newHashMap();
+ for (Metric metric : metrics) {
+ if (metric.isNumericType()) {
+ metricByIds.put(metric.getId(), metric);
+ }
+ }
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ @DependsUpon
+ public Collection<Metric> dependsUponMetrics() {
+ return metricByIds.values();
+ }
+
+ static boolean shouldCalculateDiffValues(Resource resource) {
+ // measures on files are currently purged, so past measures are not available
+ return !ResourceUtils.isEntity(resource);
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (shouldCalculateDiffValues(resource)) {
+ for (int index = 0; index < projectTargetSnapshots.length; index++) {
+ Snapshot projectTargetSnapshot = projectTargetSnapshots[index];
+ if (projectTargetSnapshot != null) {
+ calculateDiffValues(resource, context, index, projectTargetSnapshot);
+ }
+ }
+ }
+ }
+
+ private void calculateDiffValues(Resource resource, DecoratorContext context, int index, Snapshot projectTargetSnapshot) {
+ List<MeasureModel> pastMeasures = selectPastMeasures(resource.getId(), projectTargetSnapshot);
+ compareWithPastMeasures(context, index, pastMeasures);
+ }
+
+ void compareWithPastMeasures(DecoratorContext context, int index, List<MeasureModel> pastMeasures) {
+ Map<MeasureKey, MeasureModel> pastMeasuresByKey = Maps.newHashMap();
+ for (MeasureModel pastMeasure : pastMeasures) {
+ pastMeasuresByKey.put(new MeasureKey(pastMeasure), pastMeasure);
+ }
+
+ // for each measure, search equivalent past measure
+ for (Measure measure : context.getMeasures(MeasuresFilters.all())) {
+ // compare with past measure
+ MeasureModel pastMeasure = pastMeasuresByKey.get(new MeasureKey(measure));
+ if (updateDiffValue(measure, pastMeasure, index)) {
+ context.saveMeasure(measure);
+ }
+ }
+ }
+
+ boolean updateDiffValue(Measure measure, MeasureModel pastMeasure, int index) {
+ boolean updated = false;
+ if (pastMeasure != null && pastMeasure.getValue() != null && measure.getValue() != null) {
+ double diff = (measure.getValue().doubleValue() - pastMeasure.getValue().doubleValue());
+ updated = true;
+ switch (index) {
+ case 0:
+ measure.setDiffValue1(diff);
+ break;
+ case 1:
+ measure.setDiffValue2(diff);
+ break;
+ case 2:
+ measure.setDiffValue3(diff);
+ break;
+ default:
+ updated = false;
+ }
+ }
+ return updated;
+ }
+
+ List<MeasureModel> selectPastMeasures(int resourceId, Snapshot projectTargetSnapshot) {
+ // improvements : keep query in cache ? select only some columns ?
+ // TODO support measure on rules and characteristics
+ String hql = "select m from " + MeasureModel.class.getSimpleName() + " m, " + Snapshot.class.getSimpleName() + " s " +
+ "where m.snapshotId=s.id and m.metricId in (:metricIds) and m.ruleId=null and m.rulePriority=null and m.rulesCategoryId=null and m.characteristic=null "
+ + "and (s.rootId=:rootSnapshotId or s.id=:rootSnapshotId) and s.resourceId=:resourceId and s.status=:status";
+ return session.createQuery(hql)
+ .setParameter("metricIds", metricByIds.keySet())
+ .setParameter("rootSnapshotId", ObjectUtils.defaultIfNull(projectTargetSnapshot.getRootId(), projectTargetSnapshot.getId()))
+ .setParameter("resourceId", resourceId)
+ .setParameter("status", Snapshot.STATUS_PROCESSED)
+ .getResultList();
+ }
+
+ @Override
+ public String toString() {
+ return getClass().toString();
+ }
+
+ static class MeasureKey {
+ Integer metricId;
+ Integer ruleId;
+ Integer categoryId;
+ RulePriority priority;
+ Characteristic characteristic;
+
+ MeasureKey(MeasureModel model) {
+ metricId = model.getMetricId();
+ ruleId = model.getRuleId();
+ categoryId = model.getRulesCategoryId();
+ priority = model.getRulePriority();
+ characteristic = model.getCharacteristic();
+ }
+
+ MeasureKey(Measure measure) {
+ metricId = measure.getMetric().getId();
+ characteristic = measure.getCharacteristic();
+ // TODO merge RuleMeasure into Measure
+ if (measure instanceof RuleMeasure) {
+ RuleMeasure rm = (RuleMeasure) measure;
+ categoryId = rm.getRuleCategory();
+ ruleId = (rm.getRule()==null ? null : rm.getRule().getId());
+ priority = rm.getRulePriority();
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ MeasureKey that = (MeasureKey) o;
+
+ if (categoryId != null ? !categoryId.equals(that.categoryId) : that.categoryId != null) return false;
+ if (characteristic != null ? !characteristic.equals(that.characteristic) : that.characteristic != null)
+ return false;
+ if (!metricId.equals(that.metricId)) return false;
+ if (priority != that.priority) return false;
+ if (ruleId != null ? !ruleId.equals(that.ruleId) : that.ruleId != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = metricId.hashCode();
+ result = 31 * result + (ruleId != null ? ruleId.hashCode() : 0);
+ result = 31 * result + (categoryId != null ? categoryId.hashCode() : 0);
+ result = 31 * result + (priority != null ? priority.hashCode() : 0);
+ result = 31 * result + (characteristic != null ? characteristic.hashCode() : 0);
+ return result;
+ }
+ }
+}
\r
@WidgetCategory({"Tests"})\r
@Description("Reports on units tests and code coverage by unit tests.")\r
+@SupportVariationDashboard\r
public class CodeCoverageWidget extends AbstractRubyTemplate implements RubyRailsWidget {\r
public String getId() {\r
return "code_coverage";\r
import org.sonar.api.web.AbstractRubyTemplate;\r
import org.sonar.api.web.Description;\r
import org.sonar.api.web.RubyRailsWidget;\r
+import org.sonar.api.web.SupportVariationDashboard;\r
\r
@Description("Reports on copy/paste and documentation")\r
+@SupportVariationDashboard\r
public class CommentsDuplicationsWidget extends AbstractRubyTemplate implements RubyRailsWidget {\r
public String getId() {\r
return "comments_duplications";\r
import org.sonar.api.web.AbstractRubyTemplate;\r
import org.sonar.api.web.Description;\r
import org.sonar.api.web.RubyRailsWidget;\r
+import org.sonar.api.web.SupportVariationDashboard;\r
\r
@Description("Reports on complexity, average complexity and complexity distribution.")\r
+@SupportVariationDashboard\r
public class ComplexityWidget extends AbstractRubyTemplate implements RubyRailsWidget {\r
public String getId() {\r
return "complexity";\r
\r
@WidgetCategory({"Rules"})\r
@Description("Reports violations and compliance index on coding standards.")\r
+@SupportVariationDashboard\r
public class RulesWidget extends AbstractRubyTemplate implements RubyRailsWidget {\r
public String getId() {\r
return "rules";\r
import org.sonar.api.web.AbstractRubyTemplate;\r
import org.sonar.api.web.Description;\r
import org.sonar.api.web.RubyRailsWidget;\r
+import org.sonar.api.web.SupportVariationDashboard;\r
\r
@Description("Reports general metrics on the size of the project.")\r
+@SupportVariationDashboard\r
public class SizeWidget extends AbstractRubyTemplate implements RubyRailsWidget {\r
public String getId() {\r
return "size";\r
<td valign="top" width="50%" nowrap>
<div class="dashbox">
<p class="title">Code coverage</p>
- <p><span class="big"><%= format_measure(code_coverage_measure, :suffix => '', :url => url_for_drilldown(Metric::COVERAGE), :default => '-') %> <%= tendency_icon(code_coverage_measure, false) %></span></p>
+ <p><span class="big"><%= format_measure(code_coverage_measure, :suffix => '', :url => url_for_drilldown(Metric::COVERAGE), :default => '-') %> <%= trend_icon(code_coverage_measure, :big => true) %></span></p>
<% line_coverage=measure(Metric::LINE_COVERAGE)
if line_coverage %>
- <p><%= format_measure(line_coverage, :suffix => ' line coverage', :url => url_for_drilldown(Metric::UNCOVERED_LINES, :highlight => Metric::LINE_COVERAGE)) %> <%= tendency_icon(line_coverage) %></p>
+ <p><%= format_measure(line_coverage, :suffix => ' line coverage', :url => url_for_drilldown(Metric::UNCOVERED_LINES, :highlight => Metric::LINE_COVERAGE)) %> <%= trend_icon(line_coverage) %></p>
<% end %>
<% branch_coverage=measure(Metric::BRANCH_COVERAGE)
if branch_coverage %>
- <p><%= format_measure(branch_coverage, :suffix => ' branch coverage', :url => url_for_drilldown(Metric::UNCOVERED_CONDITIONS, :highlight => Metric::BRANCH_COVERAGE)) %> <%= tendency_icon(branch_coverage) %></p>
+ <p><%= format_measure(branch_coverage, :suffix => ' branch coverage', :url => url_for_drilldown(Metric::UNCOVERED_CONDITIONS, :highlight => Metric::BRANCH_COVERAGE)) %> <%= trend_icon(branch_coverage) %></p>
<% end %>
- <p><%= format_measure(tests_measure, :suffix => ' tests', :url => url_for_drilldown(Metric::TESTS)) %> <%= tendency_icon(tests_measure) %></p>
+ <p><%= format_measure(tests_measure, :suffix => ' tests', :url => url_for_drilldown(Metric::TESTS)) %> <%= trend_icon(tests_measure) %></p>
<% skipped_measure=measure(Metric::SKIPPED_TESTS)
if skipped_measure && skipped_measure.value>0
%>
- <p>+<%= format_measure(skipped_measure, :suffix => ' skipped', :url => url_for_drilldown(Metric::SKIPPED_TESTS)) %> <%= tendency_icon(skipped_measure) %></p>
+ <p>+<%= format_measure(skipped_measure, :suffix => ' skipped', :url => url_for_drilldown(Metric::SKIPPED_TESTS)) %> <%= trend_icon(skipped_measure) %></p>
<% end %>
- <p><%= format_measure(Metric::TEST_EXECUTION_TIME, :suffix => '', :url => url_for_drilldown(Metric::TEST_EXECUTION_TIME)) %> <%= tendency_icon(measure(Metric::TEST_EXECUTION_TIME)) %></p>
+ <p><%= format_measure(Metric::TEST_EXECUTION_TIME, :suffix => '', :url => url_for_drilldown(Metric::TEST_EXECUTION_TIME)) %> <%= trend_icon(measure(Metric::TEST_EXECUTION_TIME)) %></p>
</div>
</td>
<td valign="top" width="50%" nowrap>
%>
<div class="dashbox">
<h3>Test success</h3>
- <p><span class="big"><%= format_measure(success_percentage, :suffix => '', :url => url_for_drilldown(success_percentage)) %> <%= tendency_icon(measure(Metric::TEST_SUCCESS_DENSITY), false) %></span></p>
+ <p><span class="big"><%= format_measure(success_percentage, :suffix => '', :url => url_for_drilldown(success_percentage)) %> <%= trend_icon(measure(Metric::TEST_SUCCESS_DENSITY), :big => true) %></span></p>
<p>
- <%= format_measure(Metric::TEST_FAILURES, :suffix => ' failures', :url => url_for_drilldown(Metric::TEST_FAILURES)) %> <%= tendency_icon(measure(Metric::TEST_FAILURES)) %>
+ <%= format_measure(Metric::TEST_FAILURES, :suffix => ' failures', :url => url_for_drilldown(Metric::TEST_FAILURES)) %> <%= trend_icon(measure(Metric::TEST_FAILURES)) %>
</p>
<p>
- <%= format_measure(Metric::TEST_ERRORS, :suffix => ' errors', :url => url_for_drilldown(Metric::TEST_ERRORS)) %> <%= tendency_icon(measure(Metric::TEST_ERRORS)) %>
+ <%= format_measure(Metric::TEST_ERRORS, :suffix => ' errors', :url => url_for_drilldown(Metric::TEST_ERRORS)) %> <%= trend_icon(measure(Metric::TEST_ERRORS)) %>
</p>
</div>
<% end %>
<% if (measure(Metric::COMMENT_LINES)) %>
<div class="dashbox">
<h3>Comments</h3>
- <p><span class="big"><%= format_measure(Metric::COMMENT_LINES_DENSITY, :suffix => '', :url => url_for_drilldown(Metric::COMMENT_LINES_DENSITY)) -%> <%= tendency_icon(Metric::COMMENT_LINES_DENSITY, false) -%></span></p>
- <p><%= format_measure(Metric::COMMENT_LINES, :suffix => ' lines', :url => url_for_drilldown(Metric::COMMENT_LINES)) -%> <%= tendency_icon(Metric::COMMENT_LINES) -%></p>
+ <p><span class="big"><%= format_measure(Metric::COMMENT_LINES_DENSITY, :suffix => '', :url => url_for_drilldown(Metric::COMMENT_LINES_DENSITY)) -%> <%= trend_icon(Metric::COMMENT_LINES_DENSITY, :big => true) -%></span></p>
+ <p><%= format_measure(Metric::COMMENT_LINES, :suffix => ' lines', :url => url_for_drilldown(Metric::COMMENT_LINES)) -%> <%= trend_icon(Metric::COMMENT_LINES) -%></p>
<%
comment_blank_lines=measure('comment_blank_lines')
if comment_blank_lines && comment_blank_lines.value>0
%>
- <p>+<%= format_measure(comment_blank_lines, :suffix => ' blank', :url => url_for_drilldown(comment_blank_lines)) -%> <%= tendency_icon(comment_blank_lines) -%></p>
+ <p>+<%= format_measure(comment_blank_lines, :suffix => ' blank', :url => url_for_drilldown(comment_blank_lines)) -%> <%= trend_icon(comment_blank_lines) -%></p>
<% end %>
- <p><%= format_measure(Metric::PUBLIC_DOCUMENTED_API_DENSITY, :suffix => ' docu. API', :url => url_for_drilldown(Metric::PUBLIC_UNDOCUMENTED_API, :highlight => Metric::PUBLIC_DOCUMENTED_API_DENSITY)) -%> <%= tendency_icon(Metric::PUBLIC_DOCUMENTED_API_DENSITY) -%></p>
- <p><%= format_measure(Metric::PUBLIC_UNDOCUMENTED_API, :suffix => ' undocu. API', :url => url_for_drilldown(Metric::PUBLIC_UNDOCUMENTED_API, :highlight => Metric::PUBLIC_UNDOCUMENTED_API)) -%> <%= tendency_icon(Metric::PUBLIC_UNDOCUMENTED_API) -%></p>
- <p><%= format_measure(Metric::COMMENTED_OUT_CODE_LINES, :suffix => ' commented LOCs', :url => url_for_drilldown(Metric::COMMENTED_OUT_CODE_LINES, :highlight => Metric::COMMENTED_OUT_CODE_LINES)) -%> <%= tendency_icon(Metric::COMMENTED_OUT_CODE_LINES) -%></p>
+ <p><%= format_measure(Metric::PUBLIC_DOCUMENTED_API_DENSITY, :suffix => ' docu. API', :url => url_for_drilldown(Metric::PUBLIC_UNDOCUMENTED_API, :highlight => Metric::PUBLIC_DOCUMENTED_API_DENSITY)) -%> <%= trend_icon(Metric::PUBLIC_DOCUMENTED_API_DENSITY) -%></p>
+ <p><%= format_measure(Metric::PUBLIC_UNDOCUMENTED_API, :suffix => ' undocu. API', :url => url_for_drilldown(Metric::PUBLIC_UNDOCUMENTED_API, :highlight => Metric::PUBLIC_UNDOCUMENTED_API)) -%> <%= trend_icon(Metric::PUBLIC_UNDOCUMENTED_API) -%></p>
+ <p><%= format_measure(Metric::COMMENTED_OUT_CODE_LINES, :suffix => ' commented LOCs', :url => url_for_drilldown(Metric::COMMENTED_OUT_CODE_LINES, :highlight => Metric::COMMENTED_OUT_CODE_LINES)) -%> <%= trend_icon(Metric::COMMENTED_OUT_CODE_LINES) -%></p>
</div>
<% end %>
</td>
<% if (measure(Metric::DUPLICATED_LINES_DENSITY)) %>
<div class="dashbox">
<h3>Duplications</h3>
- <p><span class="big"><%= format_measure(Metric::DUPLICATED_LINES_DENSITY, :suffix => '', :url => url_for_drilldown(Metric::DUPLICATED_LINES, :highlight => Metric::DUPLICATED_LINES_DENSITY)) -%> <%= tendency_icon(Metric::DUPLICATED_LINES_DENSITY, false) -%></span></p>
- <p><%= format_measure(Metric::DUPLICATED_LINES, :suffix => ' lines', :url => url_for_drilldown(Metric::DUPLICATED_LINES)) -%> <%= tendency_icon(Metric::DUPLICATED_LINES) -%></p>
- <p><%= format_measure(Metric::DUPLICATED_BLOCKS, :suffix => ' blocks', :url => url_for_drilldown(Metric::DUPLICATED_BLOCKS)) -%> <%= tendency_icon(Metric::DUPLICATED_BLOCKS) -%></p>
- <p><%= format_measure(Metric::DUPLICATED_FILES, :suffix => ' files', :url => url_for_drilldown(Metric::DUPLICATED_FILES)) -%> <%= tendency_icon(Metric::DUPLICATED_FILES) -%></p>
+ <p><span class="big"><%= format_measure(Metric::DUPLICATED_LINES_DENSITY, :suffix => '', :url => url_for_drilldown(Metric::DUPLICATED_LINES, :highlight => Metric::DUPLICATED_LINES_DENSITY)) -%> <%= trend_icon(Metric::DUPLICATED_LINES_DENSITY, :big => true) -%></span></p>
+ <p><%= format_measure(Metric::DUPLICATED_LINES, :suffix => ' lines', :url => url_for_drilldown(Metric::DUPLICATED_LINES)) -%> <%= trend_icon(Metric::DUPLICATED_LINES) -%></p>
+ <p><%= format_measure(Metric::DUPLICATED_BLOCKS, :suffix => ' blocks', :url => url_for_drilldown(Metric::DUPLICATED_BLOCKS)) -%> <%= trend_icon(Metric::DUPLICATED_BLOCKS) -%></p>
+ <p><%= format_measure(Metric::DUPLICATED_FILES, :suffix => ' files', :url => url_for_drilldown(Metric::DUPLICATED_FILES)) -%> <%= trend_icon(Metric::DUPLICATED_FILES) -%></p>
</div>
<% end %>
<h3>Complexity</h3>
<% if function_complexity %>
<p>
- <span class="big"><%= format_measure(function_complexity, :suffix => '', :url => url_for_drilldown(function_complexity)) -%> <%= tendency_icon(function_complexity, false) -%></span>/ method
+ <span class="big"><%= format_measure(function_complexity, :suffix => '', :url => url_for_drilldown(function_complexity)) -%> <%= trend_icon(function_complexity, :big => true) -%></span>/ method
</p>
<% end %>
<% if paragraph_complexity %>
<p>
- <span class="big"><%= format_measure(paragraph_complexity, :suffix => '', :url => url_for_drilldown(paragraph_complexity)) -%> <%= tendency_icon(paragraph_complexity, false) -%></span>/ paragraph
+ <span class="big"><%= format_measure(paragraph_complexity, :suffix => '', :url => url_for_drilldown(paragraph_complexity)) -%> <%= trend_icon(paragraph_complexity, :big => true) -%></span>/ paragraph
</p>
<% end %>
<% if class_complexity %>
<p>
- <span class="big"><%= format_measure(class_complexity, :suffix => '', :url => url_for_drilldown(class_complexity)) -%> <%= tendency_icon(class_complexity, false) -%></span>/ class
+ <span class="big"><%= format_measure(class_complexity, :suffix => '', :url => url_for_drilldown(class_complexity)) -%> <%= trend_icon(class_complexity, :big => true) -%></span>/ class
</p>
<% end %>
<% if file_complexity %>
<p>
- <span class="big"><%= format_measure(file_complexity, :suffix => '', :url => url_for_drilldown(file_complexity)) -%> <%= tendency_icon(file_complexity, false) -%></span>/ file
+ <span class="big"><%= format_measure(file_complexity, :suffix => '', :url => url_for_drilldown(file_complexity)) -%> <%= trend_icon(file_complexity, :big => true) -%></span>/ file
</p>
<% end %>
<% if complexity %>
<p>
- Total: <%= format_measure(complexity, :url => url_for_drilldown(complexity)) -%> <%= tendency_icon(complexity) -%>
+ Total: <%= format_measure(complexity, :url => url_for_drilldown(complexity)) -%> <%= trend_icon(complexity) -%>
</p>
<% end %>
</div>
if distributions.size>0
selected_distribution=distributions.first
end
- if selected_distribution
+ if selected_distribution && !dashboard_configuration.variation?
%>
<div class="dashbox" id="cmp_charts">
<script type='text/javascript'>
<div class="dashbox">
<h3>Rules compliance</h3>
<div class="big">
- <%= format_measure(Metric::VIOLATIONS_DENSITY, :url => url_for_drilldown(Metric::WEIGHTED_VIOLATIONS, {:highlight => Metric::WEIGHTED_VIOLATIONS})) -%> <%= tendency_icon(Metric::VIOLATIONS_DENSITY) -%>
+ <%= format_measure(Metric::VIOLATIONS_DENSITY, :url => url_for_drilldown(Metric::WEIGHTED_VIOLATIONS, {:highlight => Metric::WEIGHTED_VIOLATIONS})) -%> <%= trend_icon(Metric::VIOLATIONS_DENSITY) -%>
</div>
<%
maintainability=@snapshot.measure(Metric::MAINTAINABILITY)
<tr>
<td align="left" nowrap>Efficiency </td>
<td align="right" id="m_efficiency"><%= formatted_value(efficiency) %></td>
- <td nowrap> <%= tendency_icon(efficiency) -%></td>
+ <td nowrap> <%= trend_icon(efficiency) -%></td>
</tr>
<tr>
<td align="left" nowrap>Maintainability </td>
<td align="right" id="m_maintainability"><%= formatted_value(maintainability) %></td>
- <td nowrap> <%= tendency_icon(maintainability) -%></td>
+ <td nowrap> <%= trend_icon(maintainability) -%></td>
</tr>
<tr>
<td align="left" nowrap>Portability </td>
<td align="right" id="m_portability"><%= formatted_value(portability) %></td>
- <td nowrap> <%= tendency_icon(portability) -%></td>
+ <td nowrap> <%= trend_icon(portability) -%></td>
</tr>
<tr>
<td align="left" nowrap>Reliability </td>
<td align="right" id="m_reliability"><%= formatted_value(reliability) %></td>
- <td nowrap> <%= tendency_icon(reliability) -%></td>
+ <td nowrap> <%= trend_icon(reliability) -%></td>
</tr>
<tr>
<td align="left" nowrap>Usability </td>
<td align="right" id="m_usability"><%= formatted_value(usability) %></td>
- <td nowrap> <%= tendency_icon(usability) -%></td>
+ <td nowrap> <%= trend_icon(usability) -%></td>
</tr>
</table>
</span>
<div class="dashbox">
<h3>Violations</h3>
<div class="big">
- <%= format_measure(Metric::VIOLATIONS, :url => url_for(:controller => 'drilldown', :action => 'violations', :id => @project.key)) -%> <%= tendency_icon(Metric::VIOLATIONS) -%>
+ <%= format_measure(Metric::VIOLATIONS, :url => url_for(:controller => 'drilldown', :action => 'violations', :id => @project.key)) -%> <%= trend_icon(Metric::VIOLATIONS) -%>
</div>
</div>
<%
<td style="padding-left: 10px;" align="right">
<%= format_measure(blocker_violations) -%>
</td>
- <td width="1%"><%= tendency_icon(blocker_violations) -%></td>
+ <td width="1%"><%= trend_icon(blocker_violations) -%></td>
<td align="left" style="padding-bottom:2px; padding-top:2px;">
<%= barchart(:width => 60, :percent => (blocker_violations ? (100 * blocker_violations.value / max).to_i : 0), :color => '#777777') if max>0 %>
</td>
<td style="padding-left: 10px;" align="right">
<%= format_measure(critical_violations) -%>
</td>
- <td width="1%"><%= tendency_icon(critical_violations) -%></td>
+ <td width="1%"><%= trend_icon(critical_violations) -%></td>
<td align="left" style="padding-bottom:2px; padding-top:2px;">
<%= barchart(:width => 60, :percent => (critical_violations ? (100 * critical_violations.value / max).to_i : 0), :color => '#777777') if max>0 %>
</td>
<td style="padding-left: 10px;" align="right">
<%= format_measure(major_violations) -%>
</td>
- <td width="1%"><%= tendency_icon(major_violations) -%></td>
+ <td width="1%"><%= trend_icon(major_violations) -%></td>
<td align="left" style="padding-bottom:2px; padding-top:2px;">
<%= barchart(:width => 60, :percent => (major_violations ? (100 * major_violations.value / max).to_i : 0), :color => '#777777') if max>0 %>
</td>
<td style="padding-left: 10px;" align="right">
<%= format_measure(minor_violations) -%>
</td>
- <td width="1%"><%= tendency_icon(minor_violations) -%></td>
+ <td width="1%"><%= trend_icon(minor_violations) -%></td>
<td align="left" style="padding-bottom:2px; padding-top:2px;">
<%= barchart(:width => 60, :percent => (minor_violations ? (100 * minor_violations.value / max).to_i : 0), :color => '#777777') if max>0 %>
</td>
<td style="padding-left: 10px;" align="right">
<%= format_measure(info_violations) -%>
</td>
- <td width="1%"><%= tendency_icon(info_violations) -%></td>
+ <td width="1%"><%= trend_icon(info_violations) -%></td>
<td align="left" style="padding-bottom:2px; padding-top:2px;">
<%= barchart(:width => 60, :percent => (info_violations ? (100 * info_violations.value / max).to_i : 0), :color => '#777777') if max>0 %>
</td>
<h3>Lines of code</h3>
<% if measure('ncloc') %>
<p><span class="big">
- <%= format_measure('ncloc', :suffix => '', :url => url_for_drilldown('ncloc')) -%> <%= tendency_icon('ncloc', false) -%></span></p>
+ <%= format_measure('ncloc', :suffix => '', :url => url_for_drilldown('ncloc')) -%> <%= trend_icon('ncloc', :big => true) -%></span></p>
<%
generated_ncloc=measure('generated_ncloc')
if generated_ncloc && generated_ncloc.value>0
%>
- <p>+<%= format_measure(generated_ncloc, :suffix => ' generated', :url => url_for_drilldown(generated_ncloc)) -%> <%= tendency_icon(generated_ncloc) -%></p>
+ <p>+<%= format_measure(generated_ncloc, :suffix => ' generated', :url => url_for_drilldown(generated_ncloc)) -%> <%= trend_icon(generated_ncloc) -%></p>
<% end %>
- <p><%= format_measure('lines', :suffix => ' lines', :url => url_for_drilldown('lines')) -%> <%= tendency_icon('lines') -%></p>
+ <p><%= format_measure('lines', :suffix => ' lines', :url => url_for_drilldown('lines')) -%> <%= trend_icon('lines') -%></p>
<% else%>
- <p><span class="big"><%= format_measure('lines', :suffix => '', :url => url_for_drilldown('lines')) -%> <%= tendency_icon('lines', false) -%></span></p>
+ <p><span class="big"><%= format_measure('lines', :suffix => '', :url => url_for_drilldown('lines')) -%> <%= trend_icon('lines', :big => true) -%></span></p>
<% end %>
<%
generated_lines=measure('generated_lines')
if generated_lines && generated_lines.value>0
%>
- <p>incl. <%= format_measure(generated_lines, :suffix => ' generated', :url => url_for_drilldown(generated_lines)) -%> <%= tendency_icon(generated_lines) -%></p>
+ <p>incl. <%= format_measure(generated_lines, :suffix => ' generated', :url => url_for_drilldown(generated_lines)) -%> <%= trend_icon(generated_lines) -%></p>
<% end %>
<% if statements %>
<p>
- <%= format_measure(statements, :suffix => ' statements', :url => url_for_drilldown(statements)) -%> <%= tendency_icon(statements) -%>
+ <%= format_measure(statements, :suffix => ' statements', :url => url_for_drilldown(statements)) -%> <%= trend_icon(statements) -%>
</p>
<% end %>
<% if files && measure('classes') %>
- <p><%= format_measure(files, :suffix => ' files', :url => url_for_drilldown(files)) -%> <%= tendency_icon(files) -%></p>
+ <p><%= format_measure(files, :suffix => ' files', :url => url_for_drilldown(files)) -%> <%= trend_icon(files) -%></p>
<% end %>
</div>
</td>
<div class="dashbox">
<% if measure('classes') %>
<h3>Classes</h3>
- <p><span class="big"><%= format_measure('classes', :url => url_for_drilldown('classes')) -%> <%= tendency_icon('classes') -%></span></p>
- <p><%= format_measure('packages', :suffix => ' packages', :url => url_for_drilldown('packages')) -%> <%= tendency_icon('packages') -%></p>
+ <p><span class="big"><%= format_measure('classes', :url => url_for_drilldown('classes')) -%> <%= trend_icon('classes') -%></span></p>
+ <p><%= format_measure('packages', :suffix => ' packages', :url => url_for_drilldown('packages')) -%> <%= trend_icon('packages') -%></p>
<% else %>
<h3>Files</h3>
- <p><span class="big"><%= format_measure('files', :url => url_for_drilldown('files')) -%> <%= tendency_icon('files') -%></span></p>
- <p><%= format_measure('directories', :suffix => ' directories', :url => url_for_drilldown('directories')) -%> <%= tendency_icon('directories') -%></p>
+ <p><span class="big"><%= format_measure('files', :url => url_for_drilldown('files')) -%> <%= trend_icon('files') -%></span></p>
+ <p><%= format_measure('directories', :suffix => ' directories', :url => url_for_drilldown('directories')) -%> <%= trend_icon('directories') -%></p>
<% end %>
- <p><%= format_measure('functions', :suffix => ' methods', :url => url_for_drilldown('functions')) -%> <%= tendency_icon('functions') -%></p>
+ <p><%= format_measure('functions', :suffix => ' methods', :url => url_for_drilldown('functions')) -%> <%= trend_icon('functions') -%></p>
<% if (measure('accessors')) %>
- <p><%= format_measure('accessors', :prefix => '+', :suffix => ' accessors', :url => url_for_drilldown('accessors')) -%> <%= tendency_icon('accessors') -%></p>
+ <p><%= format_measure('accessors', :prefix => '+', :suffix => ' accessors', :url => url_for_drilldown('accessors')) -%> <%= trend_icon('accessors') -%></p>
<% end %>
<% if measure('paragraphs') %>
- <p><%= format_measure('paragraphs', :suffix => ' paragraphs', :url => url_for_drilldown('paragraphs')) -%> <%= tendency_icon('paragraphs') -%></p>
+ <p><%= format_measure('paragraphs', :suffix => ' paragraphs', :url => url_for_drilldown('paragraphs')) -%> <%= trend_icon('paragraphs') -%></p>
<% end %>
</div>
</td>
+++ /dev/null
-/*
- * Sonar, open source software quality management 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
- */
-package org.sonar.plugins.core.timemachine;
-
-import org.junit.Test;
-import org.sonar.api.database.model.MeasureModel;
-import org.sonar.api.database.model.Snapshot;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.resources.*;
-import org.sonar.jpa.test.AbstractDbUnitTestCase;
-
-import java.util.List;
-
-import static org.hamcrest.CoreMatchers.anyOf;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-public class DifferentialValueDecoratorTest extends AbstractDbUnitTestCase {
-
- private static final int PROJECT_SNAPSHOT_ID = 1000;
- private static final int PROJECT_ID = 1;
- private static final int FILE_ID = 3;
-
- @Test
- public void shouldSelectPastResourceMeasures() {
- setupData("shared");
-
- List<Metric> metrics = selectMetrics();
- Snapshot projectSnapshot = getSession().getSingleResult(Snapshot.class, "id", PROJECT_SNAPSHOT_ID);
-
- DifferentialValueDecorator decorator = new DifferentialValueDecorator(getSession(), new Snapshot[0], metrics);
- List<MeasureModel> measures = decorator.selectPastMeasures(FILE_ID, projectSnapshot);
- assertThat(measures.size(), is(2));
-
- for (MeasureModel measure : measures) {
- assertThat(measure.getId(), anyOf(is(5L), is(6L)));
- assertThat(measure.getValue(), anyOf(is(5.0), is(60.0)));
- }
- }
-
- @Test
- public void shouldSelectPastProjectMeasures() {
- setupData("shared");
-
- List<Metric> metrics = selectMetrics();
- Snapshot projectSnapshot = getSession().getSingleResult(Snapshot.class, "id", PROJECT_SNAPSHOT_ID);
-
- DifferentialValueDecorator decorator = new DifferentialValueDecorator(getSession(), new Snapshot[0], metrics);
- List<MeasureModel> measures = decorator.selectPastMeasures(PROJECT_ID, projectSnapshot);
- assertThat(measures.size(), is(2));
-
- for (MeasureModel measure : measures) {
- assertThat(measure.getId(), anyOf(is(1L), is(2L)));
- assertThat(measure.getValue(), anyOf(is(60.0), is(80.0)));
- }
- }
-
- @Test
- public void shouldNotCalculateDiffValuesOnFiles() {
- assertThat(DifferentialValueDecorator.shouldCalculateDiffValues(new Project("foo")), is(true));
- assertThat(DifferentialValueDecorator.shouldCalculateDiffValues(new JavaPackage("org.foo")), is(true));
- assertThat(DifferentialValueDecorator.shouldCalculateDiffValues(new Directory("org/foo")), is(true));
-
- assertThat(DifferentialValueDecorator.shouldCalculateDiffValues(new JavaFile("org.foo.Bar")), is(false));
- assertThat(DifferentialValueDecorator.shouldCalculateDiffValues(new JavaFile("org.foo.Bar", true)), is(false));
- assertThat(DifferentialValueDecorator.shouldCalculateDiffValues(new File("org/foo/Bar.php")), is(false));
- }
-
- private List<Metric> selectMetrics() {
- return getSession().getResults(Metric.class);
- }
-}
--- /dev/null
+/*
+ * Sonar, open source software quality management 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
+ */
+package org.sonar.plugins.core.timemachine;
+
+import org.junit.Test;
+import org.sonar.api.database.model.MeasureModel;
+import org.sonar.api.database.model.Snapshot;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.*;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class VariationDecoratorTest extends AbstractDbUnitTestCase {
+
+ private static final int PROJECT_SNAPSHOT_ID = 1000;
+ private static final int PROJECT_ID = 1;
+ private static final int FILE_ID = 3;
+
+ @Test
+ public void shouldSelectPastResourceMeasures() {
+ setupData("shared");
+
+ List<Metric> metrics = selectMetrics();
+ Snapshot projectSnapshot = getSession().getSingleResult(Snapshot.class, "id", PROJECT_SNAPSHOT_ID);
+
+ VariationDecorator decorator = new VariationDecorator(getSession(), new Snapshot[0], metrics);
+ List<MeasureModel> measures = decorator.selectPastMeasures(FILE_ID, projectSnapshot);
+ assertThat(measures.size(), is(2));
+
+ for (MeasureModel measure : measures) {
+ assertThat(measure.getId(), anyOf(is(5L), is(6L)));
+ assertThat(measure.getValue(), anyOf(is(5.0), is(60.0)));
+ }
+ }
+
+ @Test
+ public void shouldSelectPastProjectMeasures() {
+ setupData("shared");
+
+ List<Metric> metrics = selectMetrics();
+ Snapshot projectSnapshot = getSession().getSingleResult(Snapshot.class, "id", PROJECT_SNAPSHOT_ID);
+
+ VariationDecorator decorator = new VariationDecorator(getSession(), new Snapshot[0], metrics);
+ List<MeasureModel> measures = decorator.selectPastMeasures(PROJECT_ID, projectSnapshot);
+ assertThat(measures.size(), is(2));
+
+ for (MeasureModel measure : measures) {
+ assertThat(measure.getId(), anyOf(is(1L), is(2L)));
+ assertThat(measure.getValue(), anyOf(is(60.0), is(80.0)));
+ }
+ }
+
+ @Test
+ public void shouldNotCalculateDiffValuesOnFiles() {
+ assertThat(VariationDecorator.shouldCalculateDiffValues(new Project("foo")), is(true));
+ assertThat(VariationDecorator.shouldCalculateDiffValues(new JavaPackage("org.foo")), is(true));
+ assertThat(VariationDecorator.shouldCalculateDiffValues(new Directory("org/foo")), is(true));
+
+ assertThat(VariationDecorator.shouldCalculateDiffValues(new JavaFile("org.foo.Bar")), is(false));
+ assertThat(VariationDecorator.shouldCalculateDiffValues(new JavaFile("org.foo.Bar", true)), is(false));
+ assertThat(VariationDecorator.shouldCalculateDiffValues(new File("org/foo/Bar.php")), is(false));
+ }
+
+ private List<Metric> selectMetrics() {
+ return getSession().getResults(Metric.class);
+ }
+}
+++ /dev/null
-<dataset>
-
- <metrics id="1" name="ncloc" VAL_TYPE="INT" DESCRIPTION="[null]" domain="[null]" short_name=""
- enabled="true" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/>
- <metrics id="2" name="coverage" VAL_TYPE="INT" DESCRIPTION="[null]" domain="[null]" short_name=""
- enabled="true" worst_value="0" optimized_best_value="true" best_value="100" direction="1" hidden="false"/>
-
-
- <rules_categories id="1" name="Efficiency" description="[null]"/>
- <rules_categories id="6" name="Usability" description="[null]"/>
-
- <rules id="30" name="Check Header" rules_category_id="6" plugin_rule_key="com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck"
- plugin_config_key="Checker/Treewalker/HeaderCheck" plugin_name="checkstyle" description="[null]" priority="4" enabled="true"
- cardinality="SINGLE" parent_id="[null]"/>
-
- <rules id="31" name="Equals Avoid Null" rules_category_id="6" plugin_rule_key="com.puppycrawl.tools.checkstyle.checks.coding.EqualsAvoidNullCheck"
- plugin_config_key="Checker/TreeWalker/EqualsAvoidNull" plugin_name="checkstyle" description="[null]" priority="4" enabled="true"
- cardinality="SINGLE" parent_id="[null]"/>
-
- <!-- project -->
- <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="project" name="project"
- root_id="[null]"
- description="[null]"
- enabled="true" language="java" copy_resource_id="[null]"/>
-
- <!-- package -->
- <projects long_name="[null]" id="2" scope="DIR" qualifier="PAC" kee="project:org.foo" name="org.foo"
- root_id="1"
- description="[null]"
- enabled="true" language="java" copy_resource_id="[null]"/>
-
- <!-- file -->
- <projects long_name="org.foo.Bar" id="3" scope="FIL" qualifier="CLA" kee="project:org.foo.Bar"
- name="Bar" root_id="[null]"
- description="[null]"
- enabled="true" language="java" copy_resource_id="[null]"/>
-
-
- <!-- snapshots -->
- <snapshots id="1000" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
- scope="PRJ" qualifier="TRK" created_at="2008-11-01 13:58:00.00" version="[null]" path=""
- status="P" islast="false" depth="0" />
-
- <snapshots id="1001" project_id="2" parent_snapshot_id="1000" root_project_id="1" root_snapshot_id="1000"
- scope="DIR" qualifier="PAC" created_at="2008-11-01 13:58:00.00" version="[null]" path="1000."
- status="P" islast="false" depth="1" />
-
- <snapshots id="1002" project_id="3" parent_snapshot_id="1001" root_project_id="1" root_snapshot_id="1000"
- scope="FIL" qualifier="CLA" created_at="2008-11-01 13:58:00.00" version="[null]" path="1000.1001."
- status="P" islast="false" depth="2" />
-
-
- <!-- project measures -->
- <project_measures id="1" VALUE="60" METRIC_ID="1" SNAPSHOT_ID="1000" alert_text="[null]" RULES_CATEGORY_ID="[null]"
- RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
- alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
- diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/>
-
- <project_measures id="2" VALUE="80" METRIC_ID="2" SNAPSHOT_ID="1000" alert_text="[null]" RULES_CATEGORY_ID="[null]"
- RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
- alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
- diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/>
-
- <!-- package measures -->
- <project_measures id="3" VALUE="20" METRIC_ID="1" SNAPSHOT_ID="1001" alert_text="[null]" RULES_CATEGORY_ID="[null]"
- RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
- alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
- diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/>
-
- <project_measures id="4" VALUE="70" METRIC_ID="2" SNAPSHOT_ID="1001" alert_text="[null]" RULES_CATEGORY_ID="[null]"
- RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
- alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
- diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/>
-
- <!-- file measures -->
- <project_measures id="5" VALUE="5" METRIC_ID="1" SNAPSHOT_ID="1002" alert_text="[null]" RULES_CATEGORY_ID="[null]"
- RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
- alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
- diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/>
-
- <project_measures id="6" VALUE="60" METRIC_ID="2" SNAPSHOT_ID="1002" alert_text="[null]" RULES_CATEGORY_ID="[null]"
- RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
- alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
- diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/>
-</dataset>
\ No newline at end of file
--- /dev/null
+<dataset>
+
+ <metrics id="1" name="ncloc" VAL_TYPE="INT" DESCRIPTION="[null]" domain="[null]" short_name=""
+ enabled="true" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/>
+ <metrics id="2" name="coverage" VAL_TYPE="INT" DESCRIPTION="[null]" domain="[null]" short_name=""
+ enabled="true" worst_value="0" optimized_best_value="true" best_value="100" direction="1" hidden="false"/>
+
+
+ <rules_categories id="1" name="Efficiency" description="[null]"/>
+ <rules_categories id="6" name="Usability" description="[null]"/>
+
+ <rules id="30" name="Check Header" rules_category_id="6" plugin_rule_key="com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck"
+ plugin_config_key="Checker/Treewalker/HeaderCheck" plugin_name="checkstyle" description="[null]" priority="4" enabled="true"
+ cardinality="SINGLE" parent_id="[null]"/>
+
+ <rules id="31" name="Equals Avoid Null" rules_category_id="6" plugin_rule_key="com.puppycrawl.tools.checkstyle.checks.coding.EqualsAvoidNullCheck"
+ plugin_config_key="Checker/TreeWalker/EqualsAvoidNull" plugin_name="checkstyle" description="[null]" priority="4" enabled="true"
+ cardinality="SINGLE" parent_id="[null]"/>
+
+ <!-- project -->
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="project" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- package -->
+ <projects long_name="[null]" id="2" scope="DIR" qualifier="PAC" kee="project:org.foo" name="org.foo"
+ root_id="1"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- file -->
+ <projects long_name="org.foo.Bar" id="3" scope="FIL" qualifier="CLA" kee="project:org.foo.Bar"
+ name="Bar" root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+
+ <!-- snapshots -->
+ <snapshots id="1000" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+ scope="PRJ" qualifier="TRK" created_at="2008-11-01 13:58:00.00" version="[null]" path=""
+ status="P" islast="false" depth="0" />
+
+ <snapshots id="1001" project_id="2" parent_snapshot_id="1000" root_project_id="1" root_snapshot_id="1000"
+ scope="DIR" qualifier="PAC" created_at="2008-11-01 13:58:00.00" version="[null]" path="1000."
+ status="P" islast="false" depth="1" />
+
+ <snapshots id="1002" project_id="3" parent_snapshot_id="1001" root_project_id="1" root_snapshot_id="1000"
+ scope="FIL" qualifier="CLA" created_at="2008-11-01 13:58:00.00" version="[null]" path="1000.1001."
+ status="P" islast="false" depth="2" />
+
+
+ <!-- project measures -->
+ <project_measures id="1" VALUE="60" METRIC_ID="1" SNAPSHOT_ID="1000" alert_text="[null]" RULES_CATEGORY_ID="[null]"
+ RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
+ diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/>
+
+ <project_measures id="2" VALUE="80" METRIC_ID="2" SNAPSHOT_ID="1000" alert_text="[null]" RULES_CATEGORY_ID="[null]"
+ RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
+ diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/>
+
+ <!-- package measures -->
+ <project_measures id="3" VALUE="20" METRIC_ID="1" SNAPSHOT_ID="1001" alert_text="[null]" RULES_CATEGORY_ID="[null]"
+ RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
+ diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/>
+
+ <project_measures id="4" VALUE="70" METRIC_ID="2" SNAPSHOT_ID="1001" alert_text="[null]" RULES_CATEGORY_ID="[null]"
+ RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
+ diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/>
+
+ <!-- file measures -->
+ <project_measures id="5" VALUE="5" METRIC_ID="1" SNAPSHOT_ID="1002" alert_text="[null]" RULES_CATEGORY_ID="[null]"
+ RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
+ diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/>
+
+ <project_measures id="6" VALUE="60" METRIC_ID="2" SNAPSHOT_ID="1002" alert_text="[null]" RULES_CATEGORY_ID="[null]"
+ RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
+ diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/>
+</dataset>
\ No newline at end of file
--- /dev/null
+/*
+ * Sonar, open source software quality management 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
+ */
+package org.sonar.api.web;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotated widgets support the variation view of dashboards
+ *
+ * @since 2.5
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface SupportVariationDashboard {
+
+}
}
}
- public List<ViewProxy<Widget>> getWidgets(String resourceScope, String resourceQualifier, String resourceLanguage) {
- return getContainer().getComponent(Views.class).getWidgets(resourceScope, resourceQualifier, resourceLanguage);
+ public List<ViewProxy<Widget>> getWidgets(String resourceScope, String resourceQualifier, String resourceLanguage, boolean differentialDashboard) {
+ return getContainer().getComponent(Views.class).getWidgets(resourceScope, resourceQualifier, resourceLanguage, differentialDashboard);
}
public List<ViewProxy<Widget>> getWidgets() {
private WidgetLayoutType widgetLayout = WidgetLayoutType.DEFAULT;
private boolean isDefaultTab = false;
private boolean isWidget = false;
+ private boolean supportsVariationDashboard = false;
public ViewProxy(final V view) {
this.view = view;
widgetLayout = layoutAnnotation.value();
}
+ if (AnnotationUtils.getClassAnnotation(view, SupportVariationDashboard.class)!=null) {
+ supportsVariationDashboard = true;
+ }
+
isWidget = (view instanceof Widget);
}
return !ArrayUtils.isEmpty(widgetProperties);
}
+ public boolean supportsVariationDashboard() {
+ return supportsVariationDashboard;
+ }
+
public boolean hasRequiredProperties() {
boolean requires = false;
for (WidgetProperty property : widgetProperties) {
*/
package org.sonar.server.ui;
+import com.google.common.collect.Lists;
import org.apache.commons.lang.ArrayUtils;
import org.sonar.api.ServerComponent;
import org.sonar.api.web.Page;
}
public List<ViewProxy<Page>> getPages(String section, String resourceScope, String resourceQualifier, String resourceLanguage) {
- List<ViewProxy<Page>> result = new ArrayList<ViewProxy<Page>>();
+ List<ViewProxy<Page>> result = Lists.newArrayList();
for (ViewProxy<Page> proxy : pages) {
if (accept(proxy, section, resourceScope, resourceQualifier, resourceLanguage)) {
result.add(proxy);
return widgetsPerId.get(id);
}
- public List<ViewProxy<Widget>> getWidgets(String resourceScope, String resourceQualifier, String resourceLanguage) {
- List<ViewProxy<Widget>> result = new ArrayList<ViewProxy<Widget>>();
+ public List<ViewProxy<Widget>> getWidgets(String resourceScope, String resourceQualifier, String resourceLanguage, boolean variationDashboard) {
+ List<ViewProxy<Widget>> result = Lists.newArrayList();
for (ViewProxy<Widget> proxy : widgets) {
- if (accept(proxy, null, resourceScope, resourceQualifier, resourceLanguage)) {
+ if (accept(proxy, null, resourceScope, resourceQualifier, resourceLanguage) && (!variationDashboard || proxy.supportsVariationDashboard())) {
result.add(proxy);
}
}
}
public List<ViewProxy<Widget>> getWidgets() {
- return new ArrayList<ViewProxy<Widget>>(widgets);
+ return Lists.newArrayList(widgets);
}
private static boolean accept(ViewProxy proxy, String section, String resourceScope, String resourceQualifier, String resourceLanguage) {
private
def init
- @dashboard_configuration = Sonar::DashboardConfiguration.new
+ @dashboard_configuration = Sonar::ComponentsConfiguration.new
@column_id = params[:id]
end
SECTION = Navigation::SECTION_RESOURCE
def index
- @dashboard_configuration = Sonar::DashboardConfiguration.new
+ @dashboard_configuration = Sonar::ComponentsConfiguration.new
@project = Project.by_key(params[:id])
return access_denied unless has_role?(:user, @project)
end
end
@dashboard=(@active ? @active.dashboard : nil)
+ @dashboard_configuration=Api::DashboardConfiguration.new(@dashboard, :variation_index => params[:var])
end
def load_resource
end
def load_authorized_widget_definitions()
- @widget_definitions = java_facade.getWidgets(@resource.scope, @resource.qualifier, @resource.language)
+ @widget_definitions = java_facade.getWidgets(@resource.scope, @resource.qualifier, @resource.language, @dashboard_configuration.variation?)
@widget_definitions=@widget_definitions.select do |widget|
authorized=widget.getUserRoles().size==0
unless authorized
redirect_to home_path
end
- def load_widgets
- @widgets = java_facade.getWidgets(@project.scope, @project.qualifier, @project.language)
- @widgets=@widgets.select do |widget|
- authorized=widget.getUserRoles().size==0
- unless authorized
- widget.getUserRoles().each do |role|
- authorized=(role=='user') || (role=='viewer') || has_role?(role, @project)
- break if authorized
- end
- end
- authorized
- end
- end
end
\ No newline at end of file
end
end
- def tendency_icon(metric_or_measure, small=true, no_tendency_img=true)
- if metric_or_measure.is_a? ProjectMeasure
- m = metric_or_measure
- elsif @snapshot
- m = @snapshot.measure(metric_or_measure)
- end
-
- suffix = (small ? '-small' : '')
- if small.nil? || m.nil? || m.tendency.nil? || m.tendency==0
- return no_tendency_img ? "" : image_tag("transparent.gif", :width => small ? "16" : "18", :alt => "")
- end
- filename = m.tendency.to_s
- case m.tendency_qualitative
- when 0
- filename+= '-black'
- when -1
- filename+= '-red'
- when 1
- filename+= '-green'
- end
- image_tag("tendency/#{filename}#{suffix}.png")
+ # deprecated since 2.5. Use trend_icon() instead
+ def tendency_icon(metric_or_measure, small=true, no_tendency_img=true)
+ return trend_icon(metric_or_measure, {:big => !small})
end
def boolean_icon(boolean_value, options={})
end
end
+ #
# Display a measure
#
# === Optional parameters
# * <tt>:prefix</tt> - add a prefix. Default is ''.
# * <tt>:suffix</tt> - add a suffix. Default is ''.
# * <tt>:default</tt> - text to return if metric or measure not found. Default is blank string.
+ # * <tt>:mode</tt> - display the measure value (:value) or the variation (:variation). The default mode is :auto, according to the mode selected in the dashboard
+ # * <tt>:variation</tt> - the configuration index between 1 and 3. Only when :mode => :variation
#
# === Examples
#
# format_measure('ncloc')
# format_measure('ncloc', {:suffix => '', :url => url_for_drilldown('ncloc'), :default => '-'})
+ #
def format_measure(metric_or_measure, options={})
html=''
link_rel=''
show_link= !options[:url].blank?
+ variation_mode = false
if m.metric.val_type==Metric::VALUE_TYPE_LEVEL
html=image_tag("levels/#{m.data.downcase}.png") unless m.data.blank?
else
- html=m.formatted_value
+ mode=options[:mode]||:auto
+ if mode==:value
+ html=m.formatted_value
+ elsif mode==:variation
+ html=m.formatted_var_value(options[:variation_index]||1)
+ variation_mode = true
+ else
+ if @dashboard_configuration && @dashboard_configuration.variation?
+ html=m.formatted_variation_value(@dashboard_configuration.variation_index)
+ variation_mode = true
+ else
+ html=m.formatted_value
+ end
+ end
end
alert_class=''
alert_link = false
style = ''
- if !(m.alert_status.blank?)
- alert_class="class='alert_#{m.alert_status}'" unless m.metric.val_type==Metric::VALUE_TYPE_LEVEL
- link_rel=h(m.alert_text)
- show_link=true
- alert_link = true
-
- elsif m.metric.val_type==Metric::VALUE_TYPE_RATING && m.color
- style = "style='background-color: #{m.color.html};padding: 2px 5px'"
+ if !variation_mode
+ if !(m.alert_status.blank?)
+ alert_class="class='alert_#{m.alert_status}'" unless m.metric.val_type==Metric::VALUE_TYPE_LEVEL
+ link_rel=h(m.alert_text)
+ show_link=true
+ alert_link = true
+
+ elsif m.metric.val_type==Metric::VALUE_TYPE_RATING && m.color
+ style = "style='background-color: #{m.color.html};padding: 2px 5px'"
+ end
end
span_id=''
end
+ #
+ #
# link to the current page with the given resource. If file, then open a popup to display resource viewers.
+ #
+ #
def link_to_resource(resource, name=nil, options={})
if resource.display_dashboard?
if options[:dashboard]
end
end
+
+ #
+ #
# JFree Eastwood is a partial implementation of Google Chart Api
+ #
+ #
def gchart(parameters, options={})
image_tag("#{ApplicationController.root_context}/gchart?#{parameters}", options)
end
+
+ #
+ #
# Piechart for a distribution string or measure (foo=1;bar=2)
+ #
+ #
def piechart(distribution, options={})
chart = ""
data=nil
link_to_remote('', :url => { :controller => 'favourites', :action => 'toggle', :id => resource_id, :elt => html_id},
:method => :post, :html => {:class => initial_class, :id => html_id, :alt => initial_tooltip, :title => initial_tooltip})
end
+
+ #
+ #
+ # Display the trend icon :
+ #
+ # === Optional parameters
+ # * big: true|false. Default is false.
+ # * force: true|false. By default trend icons are hidden when the dashboard variation mode is selected.
+ #
+ # === Examples
+ # trend_icon('ncloc')
+ # trend_icon(measure('ncloc'))
+ # trend_icon('ncloc', :big => true)
+ #
+ def trend_icon(metric_or_measure, options={})
+ if metric_or_measure.is_a? ProjectMeasure
+ m = metric_or_measure
+ elsif @snapshot
+ m = @snapshot.measure(metric_or_measure)
+ end
+
+ if @dashboard_configuration && @dashboard_configuration.variation?
+ return nil unless options[:force]
+ end
+
+ big=options[:big]||false
+ if m.nil? || m.tendency.nil? || m.tendency==0
+ return image_tag("transparent.gif", :width => big ? "18" : "16", :alt => "")
+ end
+ filename = m.tendency.to_s
+
+ case m.tendency_qualitative
+ when 0
+ filename+= '-black'
+ when -1
+ filename+= '-red'
+ when 1
+ filename+= '-green'
+ end
+ suffix = (big ? '' : '-small')
+ image_tag("tendency/#{filename}#{suffix}.png")
+ end
end
--- /dev/null
+#
+# 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 Api::DashboardConfiguration
+
+ attr_accessor :dashboard, :variation_index, :variation
+
+ def initialize(dashboard, options={})
+ @dashboard=dashboard
+ @variation_index=options[:variation_index].to_i
+ @variation=(@variation_index>0)
+ end
+
+ def name
+ @dashboard.name
+ end
+
+ def description
+ @dashboard.description
+ end
+
+ def layout
+ @dashboard.column_layout
+ end
+
+ def number_of_columns
+ @dashboard.number_of_columns
+ end
+
+ def variation?
+ @variation
+ end
+
+end
end
end
+ def formatted_variation_value(variation_index)
+ variation=nil
+ case variation_index
+ when 1
+ variation=diff_value_1
+ when 2
+ variation=diff_value_2
+ when 3
+ variation=diff_value_3
+ end
+ if variation
+ label=format_numeric_value(variation)
+ variation<0 ? label : "+#{label}"
+ else
+ nil
+ end
+ end
+
def millisecs_formatted_value( value )
# bugfix with jruby 1.0 release does not support % for BigDecimal
value = value.to_i if value.kind_of? BigDecimal
[Metric::VALUE_TYPE_INT, Metric::VALUE_TYPE_FLOAT, Metric::VALUE_TYPE_PERCENT, Metric::VALUE_TYPE_MILLISEC].include?(metric.val_type)
end
+
+ def format_numeric_value(val)
+ if metric.nil?
+ return val.to_s
+ end
+
+ case metric().val_type
+ when Metric::VALUE_TYPE_INT
+ number_with_precision(val, :precision => 0)
+ when Metric::VALUE_TYPE_FLOAT
+ number_with_precision(val, :precision => 1)
+ when Metric::VALUE_TYPE_PERCENT
+ number_to_percentage(val, {:precision => 1})
+ when Metric::VALUE_TYPE_MILLISEC
+ millisecs_formatted_value( val )
+ else
+ val.to_s
+ end
+ end
+
def validate_date
if not measure_date
errors.add_to_base('A valid date must be provided')
--- /dev/null
+#
+# 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 Sonar::ComponentsConfiguration
+
+ COLUMN_SEPARATOR = '.'
+ COLUMNS_SEPARATOR = ';'
+
+ COLUMNS_SELECTED_KEY = 'sonar.core.projectsdashboard.columns'
+ COLUMNS_DEFAULT_SORT_KEY = 'sonar.core.projectsdashboard.defaultSortedColumn'
+ TREEMAP_ENABLED_KEY = 'sonar.core.projectsdashboard.showTreemap'
+
+ def initialize
+ @sorted_column_id=Property.value(COLUMNS_DEFAULT_SORT_KEY) || Sonar::ColumnsView::TYPE_PROJECT
+ @enabled_treemap=(Property.value(TREEMAP_ENABLED_KEY) || 'true')=='true'
+ @text_columns=Property.value(COLUMNS_SELECTED_KEY) || default_text_columns
+ end
+
+ def selected_columns
+ columns_from_text(@text_columns)
+ end
+
+ def homepage_metrics()
+ metrics = selected_columns.select{|col| col.metric_column?}.collect{|col| Metric.by_name(col.id)}
+ metrics<<Metric.by_name(Metric::ALERT_STATUS)
+ metrics.uniq.compact
+ end
+
+ def addeable_columns
+ addeable_columns = available_columns
+ addeable_columns.each_key { |domain|
+ domain_addeable_columns = addeable_columns[domain]
+ domain_addeable_columns.delete_if { |columns_view|
+ deleteable = false
+ selected_columns.each do |selected_column|
+ deleteable = true if columns_view.name == selected_column.name
+ end
+ deleteable
+ }
+ }
+ addeable_columns
+ end
+
+ def find_selected_column(column_id)
+ selected_columns.detect {|column| column.id == column_id}
+ end
+
+ def add_column(column)
+ columns = selected_columns + [column]
+ Property.set(COLUMNS_SELECTED_KEY, columns_to_text(columns))
+ end
+
+ def remove_column(column)
+ columns = selected_columns.reject {|col| col.id == column.id}
+ Property.set(COLUMNS_SELECTED_KEY, columns_to_text(columns))
+ end
+
+ def move_column(column, direction)
+ position = column.position
+ columns = selected_columns.reject {|col| col.id == column.id}
+ new_position = (direction == "left") ? position - 1 : position + 1
+ columns = columns.insert(new_position, column)
+ Property.set(COLUMNS_SELECTED_KEY, columns_to_text(columns))
+ end
+
+ def find_available_column(column_id)
+ available_columns.each_pair do |domain, columns|
+ columns.each do |column|
+ if column.id == column_id
+ return column
+ end
+ end
+ end
+ nil
+ end
+
+ def sorted_column_id
+ @sorted_column_id
+ end
+
+ def sorted_by_project_name?
+ @sorted_column_id=='project'
+ end
+
+ def set_column_sort_default(column_id)
+ Property.set(COLUMNS_DEFAULT_SORT_KEY, column_id)
+ end
+
+ def treemap_enabled?
+ @enabled_treemap
+ end
+
+ def toggle_treemap_enabled
+ Property.set(TREEMAP_ENABLED_KEY, !treemap_enabled?)
+ end
+
+
+ @@available_columns = nil
+
+ DEFAULT_DOMAIN='General'
+
+ def available_columns
+ if @@available_columns.nil?
+ @@available_columns = {}
+
+ @@available_columns[DEFAULT_DOMAIN] = []
+ col = Sonar::ColumnsView.new
+ col.name = "Links"
+ col.id = 'links'
+ col.col_type = Sonar::ColumnsView::TYPE_LINKS
+ @@available_columns[DEFAULT_DOMAIN] << col
+
+ col = Sonar::ColumnsView.new
+ col.name = "Build time"
+ col.id = 'build_time'
+ col.col_type = Sonar::ColumnsView::TYPE_BUILD_TIME
+ @@available_columns[DEFAULT_DOMAIN] << col
+
+ col = Sonar::ColumnsView.new
+ col.name = "Language"
+ col.id = 'language'
+ col.col_type = Sonar::ColumnsView::TYPE_LANGUAGE
+ @@available_columns[DEFAULT_DOMAIN] << col
+
+ col = Sonar::ColumnsView.new
+ col.name = "Version"
+ col.id = 'version'
+ col.col_type = Sonar::ColumnsView::TYPE_VERSION
+ @@available_columns[DEFAULT_DOMAIN] << col
+
+ Metric.all.select {|m| m.display?}.each do |metric|
+ col = Sonar::ColumnsView.new
+ col.name = metric.short_name
+ col.id = metric.name
+ col.col_type = Sonar::ColumnsView::TYPE_METRIC
+
+ if col.name
+ if metric.domain
+ @@available_columns[metric.domain] ||= []
+ @@available_columns[metric.domain] << col
+ end
+ end
+ end
+
+ @@available_columns.each_value {|columns|
+ columns.sort! { |x, y|
+ x.name = "" if x.name.nil?
+ y.name = "" if y.name.nil?
+ x.name <=> y.name if x.name and y.name
+ }
+ }
+
+ end
+ # must return a copy of the hash !
+ available_columns_clone = {}
+ @@available_columns.each_pair {|domain, columns|
+ available_columns_clone[domain] = columns.clone
+ }
+ available_columns_clone
+ end
+
+
+ protected
+
+ @@default_columns=nil
+
+ def default_text_columns
+ unless @@default_columns
+ @@default_columns = ""
+ @@default_columns << Sonar::ColumnsView::TYPE_METRIC + COLUMN_SEPARATOR + Metric::VIOLATIONS_DENSITY + COLUMNS_SEPARATOR
+ @@default_columns << Sonar::ColumnsView::TYPE_METRIC + COLUMN_SEPARATOR + Metric::COVERAGE + COLUMNS_SEPARATOR
+ @@default_columns << Sonar::ColumnsView::TYPE_BUILD_TIME + COLUMN_SEPARATOR + 'build_time' + COLUMNS_SEPARATOR
+ @@default_columns << Sonar::ColumnsView::TYPE_LINKS + COLUMN_SEPARATOR + "links"
+ end
+ @@default_columns
+ end
+
+ def columns_from_text(text)
+ columns = []
+ text.split(COLUMNS_SEPARATOR).each_with_index do |column_text, position|
+ column = column_from_text(column_text)
+ if column
+ column.position = position
+ column.sort_default = (column.id==@sorted_column_id)
+ columns << column
+ end
+ end
+ columns
+ end
+
+ def column_from_text(text)
+ column_split = text.split(COLUMN_SEPARATOR)
+ column = Sonar::ColumnsView.new
+ column.id = column_split[1]
+ column.col_type = column_split[0]
+ available_col=find_available_column(column.id)
+ if available_col
+ column.name = available_col.name
+ column
+ else
+ nil
+ end
+ end
+
+ def columns_to_text(columns)
+ text = ""
+ columns.each do |column|
+ text << column_to_text(column) + COLUMNS_SEPARATOR
+ end
+ text
+ end
+
+ def column_to_text(column)
+ text = column.col_type + COLUMN_SEPARATOR + column.id
+ end
+
+end
+++ /dev/null
-#
-# 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 Sonar::DashboardConfiguration
-
- COLUMN_SEPARATOR = '.'
- COLUMNS_SEPARATOR = ';'
-
- COLUMNS_SELECTED_KEY = 'sonar.core.projectsdashboard.columns'
- COLUMNS_DEFAULT_SORT_KEY = 'sonar.core.projectsdashboard.defaultSortedColumn'
- TREEMAP_ENABLED_KEY = 'sonar.core.projectsdashboard.showTreemap'
-
- def initialize
- @sorted_column_id=Property.value(COLUMNS_DEFAULT_SORT_KEY) || Sonar::ColumnsView::TYPE_PROJECT
- @enabled_treemap=(Property.value(TREEMAP_ENABLED_KEY) || 'true')=='true'
- @text_columns=Property.value(COLUMNS_SELECTED_KEY) || default_text_columns
- end
-
- def selected_columns
- columns_from_text(@text_columns)
- end
-
- def homepage_metrics()
- metrics = selected_columns.select{|col| col.metric_column?}.collect{|col| Metric.by_name(col.id)}
- metrics<<Metric.by_name(Metric::ALERT_STATUS)
- metrics.uniq.compact
- end
-
- def addeable_columns
- addeable_columns = available_columns
- addeable_columns.each_key { |domain|
- domain_addeable_columns = addeable_columns[domain]
- domain_addeable_columns.delete_if { |columns_view|
- deleteable = false
- selected_columns.each do |selected_column|
- deleteable = true if columns_view.name == selected_column.name
- end
- deleteable
- }
- }
- addeable_columns
- end
-
- def find_selected_column(column_id)
- selected_columns.detect {|column| column.id == column_id}
- end
-
- def add_column(column)
- columns = selected_columns + [column]
- Property.set(COLUMNS_SELECTED_KEY, columns_to_text(columns))
- end
-
- def remove_column(column)
- columns = selected_columns.reject {|col| col.id == column.id}
- Property.set(COLUMNS_SELECTED_KEY, columns_to_text(columns))
- end
-
- def move_column(column, direction)
- position = column.position
- columns = selected_columns.reject {|col| col.id == column.id}
- new_position = (direction == "left") ? position - 1 : position + 1
- columns = columns.insert(new_position, column)
- Property.set(COLUMNS_SELECTED_KEY, columns_to_text(columns))
- end
-
- def find_available_column(column_id)
- available_columns.each_pair do |domain, columns|
- columns.each do |column|
- if column.id == column_id
- return column
- end
- end
- end
- nil
- end
-
- def sorted_column_id
- @sorted_column_id
- end
-
- def sorted_by_project_name?
- @sorted_column_id=='project'
- end
-
- def set_column_sort_default(column_id)
- Property.set(COLUMNS_DEFAULT_SORT_KEY, column_id)
- end
-
- def treemap_enabled?
- @enabled_treemap
- end
-
- def toggle_treemap_enabled
- Property.set(TREEMAP_ENABLED_KEY, !treemap_enabled?)
- end
-
-
- @@available_columns = nil
-
- DEFAULT_DOMAIN='General'
-
- def available_columns
- if @@available_columns.nil?
- @@available_columns = {}
-
- @@available_columns[DEFAULT_DOMAIN] = []
- col = Sonar::ColumnsView.new
- col.name = "Links"
- col.id = 'links'
- col.col_type = Sonar::ColumnsView::TYPE_LINKS
- @@available_columns[DEFAULT_DOMAIN] << col
-
- col = Sonar::ColumnsView.new
- col.name = "Build time"
- col.id = 'build_time'
- col.col_type = Sonar::ColumnsView::TYPE_BUILD_TIME
- @@available_columns[DEFAULT_DOMAIN] << col
-
- col = Sonar::ColumnsView.new
- col.name = "Language"
- col.id = 'language'
- col.col_type = Sonar::ColumnsView::TYPE_LANGUAGE
- @@available_columns[DEFAULT_DOMAIN] << col
-
- col = Sonar::ColumnsView.new
- col.name = "Version"
- col.id = 'version'
- col.col_type = Sonar::ColumnsView::TYPE_VERSION
- @@available_columns[DEFAULT_DOMAIN] << col
-
- Metric.all.select {|m| m.display?}.each do |metric|
- col = Sonar::ColumnsView.new
- col.name = metric.short_name
- col.id = metric.name
- col.col_type = Sonar::ColumnsView::TYPE_METRIC
-
- if col.name
- if metric.domain
- @@available_columns[metric.domain] ||= []
- @@available_columns[metric.domain] << col
- end
- end
- end
-
- @@available_columns.each_value {|columns|
- columns.sort! { |x, y|
- x.name = "" if x.name.nil?
- y.name = "" if y.name.nil?
- x.name <=> y.name if x.name and y.name
- }
- }
-
- end
- # must return a copy of the hash !
- available_columns_clone = {}
- @@available_columns.each_pair {|domain, columns|
- available_columns_clone[domain] = columns.clone
- }
- available_columns_clone
- end
-
-
- protected
-
- @@default_columns=nil
-
- def default_text_columns
- unless @@default_columns
- @@default_columns = ""
- @@default_columns << Sonar::ColumnsView::TYPE_METRIC + COLUMN_SEPARATOR + Metric::VIOLATIONS_DENSITY + COLUMNS_SEPARATOR
- @@default_columns << Sonar::ColumnsView::TYPE_METRIC + COLUMN_SEPARATOR + Metric::COVERAGE + COLUMNS_SEPARATOR
- @@default_columns << Sonar::ColumnsView::TYPE_BUILD_TIME + COLUMN_SEPARATOR + 'build_time' + COLUMNS_SEPARATOR
- @@default_columns << Sonar::ColumnsView::TYPE_LINKS + COLUMN_SEPARATOR + "links"
- end
- @@default_columns
- end
-
- def columns_from_text(text)
- columns = []
- text.split(COLUMNS_SEPARATOR).each_with_index do |column_text, position|
- column = column_from_text(column_text)
- if column
- column.position = position
- column.sort_default = (column.id==@sorted_column_id)
- columns << column
- end
- end
- columns
- end
-
- def column_from_text(text)
- column_split = text.split(COLUMN_SEPARATOR)
- column = Sonar::ColumnsView.new
- column.id = column_split[1]
- column.col_type = column_split[0]
- available_col=find_available_column(column.id)
- if available_col
- column.name = available_col.name
- column
- else
- nil
- end
- end
-
- def columns_to_text(columns)
- text = ""
- columns.each do |column|
- text << column_to_text(column) + COLUMNS_SEPARATOR
- end
- text
- end
-
- def column_to_text(column)
- text = column.col_type + COLUMN_SEPARATOR + column.id
- end
-
-end
<%
begin
- widget_body=render :inline => definition.getTarget().getTemplate(), :locals => {:widget_properties => widget.properties_as_hash, :widget => widget, :dashboard => @dashboard}
+ widget_body=render :inline => definition.getTarget().getTemplate(), :locals => {:widget_properties => widget.properties_as_hash, :widget => widget, :dashboard_configuration => @dashboard_configuration}
rescue => error
logger.error("Can not render widget #{definition.getId()}: " + error)
logger.error(error.backtrace.join("\n"))
<% if widget.configured %>
<%
begin
- widget_body=render :inline => definition.getTarget().getTemplate(), :locals => {:widget_properties => widget.properties_as_hash, :widget => widget, :dashboard => @dashboard}
+ widget_body=render :inline => definition.getTarget().getTemplate(), :locals => {:widget_properties => widget.properties_as_hash, :widget => widget, :dashboard_configuration => @dashboard_configuration}
rescue => error
logger.error("Can not render widget #{definition.getId()}: " + error)
logger.error(error.backtrace.join("\n"))
private
def self.migrate_columns_selected
- dashboard_configuration = Sonar::DashboardConfiguration.new(nil)
+ dashboard_configuration = Sonar::ComponentsConfiguration.new(nil)
columns_text = ""
ColumnsView052.find(:all).each do |col|
column = find_column_by_name_or_by_id(col.name, dashboard_configuration)
- columns_text << column.col_type + Sonar::DashboardConfiguration::COLUMN_SEPARATOR + column.id + Sonar::DashboardConfiguration::COLUMNS_SEPARATOR if column
+ columns_text << column.col_type + Sonar::ComponentsConfiguration::COLUMN_SEPARATOR + column.id + Sonar::ComponentsConfiguration::COLUMNS_SEPARATOR if column
end
- create_property(Sonar::DashboardConfiguration::COLUMNS_SELECTED_KEY.to_s, columns_text)
+ create_property(Sonar::ComponentsConfiguration::COLUMNS_SELECTED_KEY.to_s, columns_text)
end
def self.migrate_column_default_sort
- dashboard_configuration = Sonar::DashboardConfiguration.new(nil)
+ dashboard_configuration = Sonar::ComponentsConfiguration.new(nil)
default_sort_column = ColumnsView052.find(:first, :conditions => {:sort_default => true})
column = find_column_by_name_or_by_id(default_sort_column.name, dashboard_configuration) if default_sort_column
- create_property(Sonar::DashboardConfiguration::COLUMNS_DEFAULT_SORT_KEY, column.id) if column
+ create_property(Sonar::ComponentsConfiguration::COLUMNS_DEFAULT_SORT_KEY, column.id) if column
end
def self.migrate_treemap_enable
treemap_enable = ColumnsView052.find(:first, :conditions => {:name => "Treemap"})
is_enabled = treemap_enable ? "true" : "false"
- create_property(Sonar::DashboardConfiguration::TREEMAP_ENABLED_KEY, is_enabled)
+ create_property(Sonar::ComponentsConfiguration::TREEMAP_ENABLED_KEY, is_enabled)
end
@Test
public void getWidgets() {
final Views views = new Views(VIEWS);
- List<ViewProxy<Widget>> widgets = views.getWidgets(null, null, null);
+ List<ViewProxy<Widget>> widgets = views.getWidgets(null, null, null, false);
assertThat(widgets.size(), is(1));
assertThat(widgets.get(0).getTarget(), is(FakeWidget.class));
}
@Test
public void sortViewsByTitle() {
final Views views = new Views(new View[]{new FakeWidget("ccc", "ccc"), new FakeWidget("aaa", "aaa"), new FakeWidget("bbb", "bbb")});
- List<ViewProxy<Widget>> widgets = views.getWidgets(null, null, null);
+ List<ViewProxy<Widget>> widgets = views.getWidgets(null, null, null, false);
assertThat(widgets.size(), is(3));
assertThat(widgets.get(0).getId(), is("aaa"));
assertThat(widgets.get(1).getId(), is("bbb"));
@Test
public void prefixTitleByNumberToDisplayFirst() {
final Views views = new Views(new View[]{new FakeWidget("other", "Other"), new FakeWidget("1id", "1widget"), new FakeWidget("2id", "2widget")});
- List<ViewProxy<Widget>> widgets = views.getWidgets(null, null, null);
+ List<ViewProxy<Widget>> widgets = views.getWidgets(null, null, null, false);
assertThat(widgets.size(), is(3));
assertThat(widgets.get(0).getId(), is("1id"));
assertThat(widgets.get(1).getId(), is("2id"));