You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

snapshot.rb 9.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. #
  2. # SonarQube, open source software quality management tool.
  3. # Copyright (C) 2008-2014 SonarSource
  4. # mailto:contact AT sonarsource DOT com
  5. #
  6. # SonarQube is free software; you can redistribute it and/or
  7. # modify it under the terms of the GNU Lesser General Public
  8. # License as published by the Free Software Foundation; either
  9. # version 3 of the License, or (at your option) any later version.
  10. #
  11. # SonarQube is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. # Lesser General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU Lesser General Public License
  17. # along with this program; if not, write to the Free Software Foundation,
  18. # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. #
  20. class Snapshot < ActiveRecord::Base
  21. include Resourceable
  22. acts_as_tree :foreign_key => 'parent_snapshot_id'
  23. belongs_to :project
  24. belongs_to :root_project, :class_name => 'Project', :foreign_key => 'root_project_id'
  25. belongs_to :parent_snapshot, :class_name => 'Snapshot', :foreign_key => 'parent_snapshot_id'
  26. belongs_to :root_snapshot, :class_name => 'Snapshot', :foreign_key => 'root_snapshot_id'
  27. belongs_to :characteristic
  28. has_many :measures, :class_name => 'ProjectMeasure', :conditions => 'rule_id IS NULL AND characteristic_id IS NULL AND person_id IS NULL'
  29. has_many :rulemeasures, :class_name => 'ProjectMeasure', :conditions => 'rule_id IS NOT NULL AND characteristic_id IS NULL AND person_id IS NULL', :include => 'rule'
  30. has_many :characteristic_measures, :class_name => 'ProjectMeasure', :conditions => 'rule_id IS NULL AND characteristic_id IS NOT NULL AND person_id IS NULL'
  31. has_many :person_measures, :class_name => 'ProjectMeasure', :conditions => 'rule_id IS NULL AND characteristic_id IS NULL AND person_id IS NOT NULL'
  32. has_many :events, :dependent => :destroy, :order => 'event_date DESC'
  33. STATUS_UNPROCESSED = 'U'
  34. STATUS_PROCESSED = 'P'
  35. def created_at
  36. long_to_date(:created_at)
  37. end
  38. def build_date
  39. long_to_date(:build_date)
  40. end
  41. def period1_date
  42. long_to_date(:period1_date)
  43. end
  44. def period2_date
  45. long_to_date(:period2_date)
  46. end
  47. def period3_date
  48. long_to_date(:period3_date)
  49. end
  50. def period4_date
  51. long_to_date(:period4_date)
  52. end
  53. def period5_date
  54. long_to_date(:period5_date)
  55. end
  56. def long_to_date(attribute)
  57. date_in_long = read_attribute(attribute)
  58. Time.at(date_in_long/1000) if date_in_long
  59. end
  60. def self.for_timemachine_matrix(resource)
  61. # http://jira.sonarsource.com/browse/SONAR-1850
  62. # Conditions on scope and qualifier are required to exclude library snapshots.
  63. # Use-case :
  64. # 1. project A 2.0 is analyzed -> new snapshot A with qualifier TRK
  65. # 2. project B, which depends on A 1.0, is analyzed -> new snapshot A 1.0 with qualifier LIB.
  66. # 3. project A has 2 snapshots : the first one with qualifier=TRK has measures, the second one with qualifier LIB has no measures. Its version must not be used in time machine
  67. # That's why the 2 following SQL requests check the qualifiers (and optionally scopes, just to be sure)
  68. snapshots=Snapshot.find(:all, :conditions => ["snapshots.project_id=? AND events.snapshot_id=snapshots.id AND snapshots.status=? AND snapshots.scope=? AND snapshots.qualifier=?", resource.id, STATUS_PROCESSED, resource.scope, resource.qualifier],
  69. :include => 'events',
  70. :order => 'snapshots.created_at ASC')
  71. snapshots<<resource.last_snapshot if snapshots.empty?
  72. snapshots=snapshots[-5, 5] if snapshots.size>=5
  73. snapshots.insert(0, Snapshot.find(:first,
  74. :conditions => ["project_id=? AND status=? AND scope=? AND qualifier=?", resource.id, STATUS_PROCESSED, resource.scope, resource.qualifier],
  75. :include => 'project', :order => 'snapshots.created_at ASC', :limit => 1))
  76. snapshots.compact.uniq
  77. end
  78. def self.for_timemachine_widget(resource, number_of_columns, options={})
  79. if number_of_columns == 1
  80. # Display only the latest snapshot
  81. return [resource.last_snapshot]
  82. end
  83. # Get 1rst & latests snapshots of the period
  84. snapshot_conditions = ["snapshots.project_id=? AND snapshots.status=? AND snapshots.scope=? AND snapshots.qualifier=?", resource.id, STATUS_PROCESSED, resource.scope, resource.qualifier]
  85. if options[:from]
  86. snapshot_conditions[0] += " AND snapshots.created_at>=?"
  87. snapshot_conditions << options[:from].to_i * 1000
  88. end
  89. first_snapshot=Snapshot.find(:first, :conditions => snapshot_conditions, :order => 'snapshots.created_at ASC')
  90. last_snapshot=resource.last_snapshot
  91. if first_snapshot==last_snapshot
  92. return [last_snapshot]
  93. end
  94. # Look for the number_of_columns-2 last snapshots to display (they must have 'Version' events)
  95. version_snapshots = []
  96. if number_of_columns > 2
  97. snapshot_conditions[0] += " AND events.snapshot_id=snapshots.id AND events.category='Version' AND snapshots.id NOT IN (?)"
  98. snapshot_conditions << [first_snapshot.id, last_snapshot.id]
  99. version_snapshots=Snapshot.find(:all, :conditions => snapshot_conditions, :include => 'events', :order => 'snapshots.created_at ASC').last(number_of_columns-2)
  100. end
  101. return [first_snapshot] + version_snapshots + [last_snapshot]
  102. end
  103. def last?
  104. islast
  105. end
  106. def root_snapshot
  107. @root_snapshot ||=
  108. (root_snapshot_id ? Snapshot.find(root_snapshot_id) : self)
  109. end
  110. def project_snapshot
  111. @project_snapshot ||=
  112. begin
  113. if scope==Project::SCOPE_SET
  114. self
  115. elsif parent_snapshot_id
  116. parent_snapshot.project_snapshot
  117. else
  118. nil
  119. end
  120. end
  121. end
  122. def root?
  123. parent_snapshot_id.nil?
  124. end
  125. def descendants
  126. children.map(&:descendants).flatten + children
  127. end
  128. def user_events
  129. categories=EventCategory.categories(true)
  130. category_names=categories.map { |cat| cat.name }
  131. Event.find(:all, :conditions => ["snapshot_id=? AND category IS NOT NULL", id], :order => 'event_date desc').select do |event|
  132. category_names.include?(event.category)
  133. end
  134. end
  135. def event(category)
  136. result=events.select { |e| e.category==category }
  137. if result.empty?
  138. nil
  139. else
  140. result.first
  141. end
  142. end
  143. def measure(metric)
  144. unless metric.is_a? Metric
  145. metric=Metric.by_key(metric)
  146. end
  147. metric ? measures_hash[metric.id] : nil
  148. end
  149. def person_measure(metric, person_id)
  150. person_measures.each do |m|
  151. return m if m.metric_id==metric.id && m.person_id==person_id
  152. end
  153. nil
  154. end
  155. def characteristic_measure(metric, characteristic)
  156. characteristic_measures.each do |m|
  157. return m if m.metric_id==metric.id && m.characteristic==characteristic
  158. end
  159. nil
  160. end
  161. def f_measure(metric)
  162. m=measure(metric)
  163. m && m.formatted_value
  164. end
  165. def rule_measures(metrics=nil, rule=nil)
  166. if metrics
  167. metric_ids=[metrics].flatten.map { |metric| metric.id }
  168. end
  169. if metrics || rule
  170. rulemeasures.select do |m|
  171. (metric_ids.nil? || metric_ids.include?(m.metric_id)) && (rule.nil? || m.rule_id==rule.id)
  172. end
  173. else
  174. rulemeasures
  175. end
  176. end
  177. def rule_measure(metric, rule)
  178. rulemeasures.each do |m|
  179. return m if m.metric_id==metric.id && m.rule_id==rule.id
  180. end
  181. nil
  182. end
  183. def self.snapshot_by_date(resource_id, date)
  184. if resource_id && date
  185. Snapshot.find(:first, :conditions => ['created_at>=? and created_at<? and project_id=?', date.beginning_of_day.to_i*1000, date.end_of_day.to_i*1000, resource_id], :order => 'created_at desc')
  186. else
  187. nil
  188. end
  189. end
  190. def resource
  191. project
  192. end
  193. def resource_id
  194. project_id
  195. end
  196. def periods?
  197. (period1_mode || period2_mode || period3_mode || period4_mode || period5_mode) != nil
  198. end
  199. def resource_id_for_authorization
  200. root_project_id || project_id
  201. end
  202. def path_name
  203. result=''
  204. if root_snapshot_id && root_snapshot_id!=id
  205. result += root_snapshot.project.long_name
  206. result += ' &raquo; '
  207. if root_snapshot.depth<self.depth-2
  208. result += ' ... &raquo; '
  209. end
  210. if parent_snapshot_id && root_snapshot_id!=parent_snapshot_id
  211. result += "#{parent_snapshot.project.long_name} &raquo; "
  212. end
  213. end
  214. result += project.long_name
  215. result
  216. end
  217. def period_mode(period_index)
  218. project_snapshot.send "period#{period_index}_mode"
  219. end
  220. def period_param(period_index)
  221. project_snapshot.send "period#{period_index}_param"
  222. end
  223. def period_datetime(period_index)
  224. project_snapshot.send "period#{period_index}_date"
  225. end
  226. def metrics
  227. @metrics ||=
  228. begin
  229. measures_hash.keys.map { |metric_id| Metric::by_id(metric_id) }.uniq.compact
  230. end
  231. end
  232. # metrics of all the available measures
  233. def metric_keys
  234. @metric_keys ||=
  235. begin
  236. metrics.map { |m| m.name }
  237. end
  238. end
  239. def has_source
  240. SnapshotSource.count('id', :conditions => "snapshot_id = #{id}") > 0
  241. end
  242. private
  243. def measures_hash
  244. @measures_hash ||=
  245. begin
  246. hash = {}
  247. measures.each do |measure|
  248. hash[measure.metric_id]=measure
  249. end
  250. hash
  251. end
  252. end
  253. end