defect-density-heatmap 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in defect-density-heatmap.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,8 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ desc "Run all specs"
4
+ Spec::Rake::SpecTask.new(:spec) do |t|
5
+ t.spec_files = FileList['spec/**/*_spec.rb']
6
+ t.spec_opts = ['--color']
7
+ end
data/bin/heatmap ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ code_directory = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+ $LOAD_PATH.unshift(code_directory) unless $LOAD_PATH.include?(code_directory)
5
+ require 'command_line_runner'
6
+ CommandLineRunner.run
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "heatmap/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "defect-density-heatmap"
7
+ s.version = Heatmap::VERSION
8
+ s.authors = ["Brandon Carlson"]
9
+ s.email = ["bcarlso@gmail.com"]
10
+ s.homepage = "http://github.com/bcarlso/defect-density-heatmap"
11
+ s.summary = %q{Generates a 'heatmap' from files changed in the SCM system.}
12
+ s.description = %q{Generates a tag cloud based on project file churn.}
13
+
14
+ s.rubyforge_project = "defect-density-heatmap"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "rspec"
23
+ s.add_development_dependency "guard"
24
+ # s.add_runtime_dependency "rest-client"
25
+ end
@@ -0,0 +1,34 @@
1
+ require 'optparse'
2
+ require 'heatmap_formatter'
3
+
4
+ class CommandLineRunner
5
+ def self.run
6
+ options = {
7
+ :input_stream => STDIN,
8
+ :output_stream => STDOUT,
9
+ :regular_expression => "(?<total>(\\d+)),(?<defects>(\\d+)),(?<filename>(.+))"
10
+ }
11
+
12
+ OptionParser.new do | opts |
13
+ opts.banner = "Usage: heatmap [options]"
14
+
15
+ opts.on("-r", "--record-format pattern", "Regular expression with named captures describing the record") do | regex |
16
+ options[:regular_expression] = regex
17
+ end
18
+
19
+ opts.on("-f", "--file inputfile", "File containing commit data") do | filename, f2 |
20
+ options[:input_stream] = File.new(filename, 'r')
21
+ end
22
+
23
+ opts.on("-o", "--outfile outputfile", "File to write output to") do | filename |
24
+ options[:output_stream] = File.new(filename, 'w')
25
+ end
26
+ end.parse!
27
+
28
+ formatter = HeatmapFormatter.new(options[:regular_expression],
29
+ options[:input_stream],
30
+ options[:output_stream])
31
+
32
+ formatter.render
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ require 'heatmap/version'
2
+
3
+ module Heatmap
4
+ # Your code goes here...
5
+ end
data/lib/heatmap.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "heatmap/version"
2
+
3
+ module Heatmap
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,3 @@
1
+ module Heatmap
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,71 @@
1
+ class HeatmapFormatter
2
+ def initialize(line_format, input_stream, output_stream)
3
+ @output_stream = output_stream
4
+
5
+ format = Regexp.new(line_format)
6
+
7
+ items_to_render = parse(input_stream, format)
8
+ @entries = items_to_render.sort do | first, second |
9
+ File.basename(first[:filename]) <=> File.basename(second[:filename])
10
+ end
11
+
12
+ @floor, ceiling = (@entries.collect { | e | e[:total].to_i }).minmax
13
+ @range = (ceiling - @floor)
14
+ end
15
+
16
+ def render
17
+ tags = @entries.inject("") do | result, e |
18
+ result + format_entry(e[:filename], e[:total].to_i, e[:defects])
19
+ end
20
+
21
+ html = <<-EOS
22
+ <html>
23
+ <head>
24
+ <title>SCM Heatmap</title>
25
+ <style type='text/css'>
26
+ body { font-family: sans-serif; }
27
+ ol li { display: inline; margin: 2px; }
28
+ </style>
29
+ </head>
30
+ <body>
31
+ <ol>
32
+ #{tags}
33
+ </ol>
34
+ </body>
35
+ </html>
36
+ EOS
37
+
38
+ @output_stream << html
39
+ end
40
+
41
+ def format_entry(filename, changes, defects)
42
+ style = compute_style(changes, defects)
43
+ title = "#{File.dirname(filename)} Changes: #{changes} Defects: #{defects}"
44
+ "<li style='#{style}' title='#{title}'>#{File.basename(filename)} </li>"
45
+ end
46
+
47
+ def size_for(number)
48
+ ((((number - @floor).to_f / @range) * 10).to_i * 3) + 6
49
+ end
50
+
51
+ private
52
+
53
+ def parse(stream, format)
54
+ stream.collect() do | line |
55
+ format.match(line)
56
+ end
57
+ end
58
+
59
+ def compute_style(total_changes, total_defects)
60
+ transparency = calculate_transparency(total_changes, total_defects)
61
+ "font-size: #{size_for(total_changes)}; background-color: rgba(255, 0, 0, #{transparency});"
62
+ end
63
+
64
+ def determine_relative_size_for(value)
65
+ @size_calculator.size_for(value)
66
+ end
67
+
68
+ def calculate_transparency(total_changes, total_defects)
69
+ total_defects.to_f / total_changes.to_f
70
+ end
71
+ end
@@ -0,0 +1,56 @@
1
+ 21,21,src/main/java/com/example/File1.java
2
+ 8,2,src/main/java/com/example/File2.java
3
+ 3,0,src/main/java/com/example/File3.java
4
+ 23,10,src/main/java/com/example/File4.java
5
+ 22,17,src/main/java/com/example/File5.java
6
+ 21,13,src/main/java/com/example/File6.java
7
+ 20,12,src/main/java/com/example/File7.java
8
+ 16,16,src/main/java/com/example/File8.java
9
+ 20,12,src/main/java/com/example/File9.java
10
+ 15,9,src/main/java/com/example/File10.java
11
+ 3,1,src/main/java/com/example/File11.java
12
+ 3,3,src/main/java/com/example/File12.java
13
+ 11,11,src/main/java/com/example/File13.java
14
+ 9,7,src/main/java/com/example/File14.java
15
+ 9,6,src/main/java/com/example/File15.java
16
+ 15,11,src/main/java/com/example/File16.java
17
+ 4,4,src/main/java/com/example/File17.java
18
+ 2,0,src/main/java/com/example/File18.java
19
+ 20,20,src/main/java/com/example/File19.java
20
+ 4,0,src/main/java/com/example/File20.java
21
+ 1,0,src/main/java/com/example/File21.java
22
+ 11,0,src/main/java/com/example/File22.java
23
+ 21,11,src/main/java/com/example/File23.java
24
+ 8,5,src/main/java/com/example/File24.java
25
+ 20,2,src/main/java/com/example/File25.java
26
+ 11,8,src/main/java/com/example/File26.java
27
+ 1,0,src/main/java/com/example/File27.java
28
+ 22,14,src/main/java/com/example/File28.java
29
+ 22,16,src/main/java/com/example/File29.java
30
+ 17,10,src/main/java/com/example/File30.java
31
+ 17,8,src/main/java/com/example/File31.java
32
+ 24,16,src/main/java/com/example/File32.java
33
+ 8,4,src/main/java/com/example/File33.java
34
+ 9,0,src/main/java/com/example/File34.java
35
+ 1,0,src/main/java/com/example/File35.java
36
+ 14,8,src/main/java/com/example/File36.java
37
+ 12,6,src/main/java/com/example/File37.java
38
+ 24,6,src/main/java/com/example/File38.java
39
+ 11,10,src/main/java/com/example/File39.java
40
+ 8,4,src/main/java/com/example/File40.java
41
+ 10,10,src/main/java/com/example/File41.java
42
+ 19,4,src/main/java/com/example/File42.java
43
+ 8,1,src/main/java/com/example/File43.java
44
+ 8,2,src/main/java/com/example/File44.java
45
+ 11,7,src/main/java/com/example/File45.java
46
+ 21,4,src/main/java/com/example/File46.java
47
+ 22,19,src/main/java/com/example/File47.java
48
+ 12,8,src/main/java/com/example/File48.java
49
+ 5,0,src/main/java/com/example/File49.java
50
+ 19,6,src/main/java/com/example/File50.java
51
+ 16,12,src/main/java/com/example/File51.java
52
+ 3,1,src/main/java/com/example/File52.java
53
+ 1,0,src/main/java/com/example/File53.java
54
+ 1,1,src/main/java/com/example/File54.java
55
+ 16,2,src/main/java/com/example/File55.java
56
+ 5,3,src/main/java/com/example/File56.java
@@ -0,0 +1,60 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe HeatmapFormatter do
4
+ before(:each) do
5
+ example_data = <<-EOS
6
+ 3:0 src/main/resources/com/example/SomeFile.java: DE3981,DE4386
7
+ 12:6 src/main/java/com/example/AnotherFile.java: US11743
8
+ 6:6 src/main/java/com/example/YetAnotherFile.java: DE3317,DE4077,US12070
9
+ EOS
10
+
11
+ @input_stream = StringIO.new(example_data)
12
+ @output_stream = StringIO.new
13
+
14
+ @formatter = HeatmapFormatter.new("(?<total>(\\d+)):(?<defects>(\\d+)) (?<filename>(.+)):.+",
15
+ @input_stream,
16
+ @output_stream)
17
+ end
18
+
19
+ it "Orders the tag cloud based on filename" do
20
+ @formatter.render
21
+ @output_stream.string.should match(".*AnotherFile.*SomeFile.*YetAnotherFile")
22
+ end
23
+
24
+
25
+ it "Renders each file as a tag" do
26
+ results = @formatter.format_entry 'path/to/SomeFile.java', 3, 0
27
+ results.should match("<li style='.+'>SomeFile.java </li>")
28
+ end
29
+
30
+ it "Adds file details to the title attribute for display when hovered over" do
31
+ results = @formatter.format_entry 'src/main/java/com/example/SomeFile.java', 3, 2
32
+ results.should have_title_containing("src/main/java/com/example Changes: 3 Defects: 2")
33
+ end
34
+
35
+ it "Adjusts the font size of each tag relative to the number of changes in the file" do
36
+ file_with( :changes => 3 ).should have_font_size(6)
37
+ file_with( :changes => 6 ).should have_font_size(15)
38
+ file_with( :changes => 12 ).should have_font_size(36)
39
+ end
40
+
41
+ it "Adjusts the background color of each tag based on the number of changes that were defects" do
42
+ file_with( :changes => 3 ).should match("background-color: rgba\\(255, 0, 0, 0.0\\)")
43
+
44
+ file_with( :changes => 3,
45
+ :defects => 3 ).should match("background-color: rgba\\(255, 0, 0, 1.0\\)")
46
+
47
+ file_with( :changes => 10,
48
+ :defects => 5 ).should match("background-color: rgba\\(255, 0, 0, 0.5\\)")
49
+ end
50
+
51
+
52
+ it "Generates a range of font sizes based on a min/max" do
53
+ @formatter.size_for(12).should == 36
54
+ @formatter.size_for(11).should == 30
55
+ @formatter.size_for(8).should == 21
56
+ @formatter.size_for(7).should == 18
57
+ @formatter.size_for(4).should == 9
58
+ @formatter.size_for(3).should == 6
59
+ end
60
+ end
@@ -0,0 +1,32 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require 'rspec'
5
+ require 'heatmap_formatter'
6
+
7
+ RSpec.configure do | config |
8
+ config.color_enabled = true
9
+ end
10
+
11
+ RSpec::Matchers.define :have_font_size do | expected |
12
+ match do | actual |
13
+ actual =~ Regexp.new(".*font-size: #{expected};.*")
14
+ end
15
+ end
16
+
17
+ RSpec::Matchers.define :have_color do | expected |
18
+ match do | actual |
19
+ actual =~ Regexp.new(".*color: #{expected};.*")
20
+ end
21
+ end
22
+
23
+ RSpec::Matchers.define :have_title_containing do | expected |
24
+ match do | actual |
25
+ actual =~ Regexp.new(".*title='.*#{expected}.*'.*")
26
+ end
27
+ end
28
+
29
+ def file_with(details)
30
+ @formatter.format_entry 'SomeFile.java', details[:changes], details[:defects] || 0
31
+ end
32
+
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: defect-density-heatmap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Brandon Carlson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-04 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70219516054120 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70219516054120
25
+ - !ruby/object:Gem::Dependency
26
+ name: guard
27
+ requirement: &70219516053700 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70219516053700
36
+ description: Generates a tag cloud based on project file churn.
37
+ email:
38
+ - bcarlso@gmail.com
39
+ executables:
40
+ - heatmap
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - .gitignore
45
+ - Gemfile
46
+ - Guardfile
47
+ - Rakefile
48
+ - bin/heatmap
49
+ - defect-density-heatmap.gemspec
50
+ - lib/command_line_runner.rb
51
+ - lib/defect-density-heatmap.rb
52
+ - lib/heatmap.rb
53
+ - lib/heatmap/version.rb
54
+ - lib/heatmap_formatter.rb
55
+ - spec/example-data.csv
56
+ - spec/heatmap_formatter_spec.rb
57
+ - spec/spec_helper.rb
58
+ homepage: http://github.com/bcarlso/defect-density-heatmap
59
+ licenses: []
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project: defect-density-heatmap
78
+ rubygems_version: 1.8.6
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: Generates a 'heatmap' from files changed in the SCM system.
82
+ test_files:
83
+ - spec/example-data.csv
84
+ - spec/heatmap_formatter_spec.rb
85
+ - spec/spec_helper.rb