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 +4 -0
- data/Gemfile +4 -0
- data/Guardfile +8 -0
- data/Rakefile +7 -0
- data/bin/heatmap +6 -0
- data/defect-density-heatmap.gemspec +25 -0
- data/lib/command_line_runner.rb +34 -0
- data/lib/defect-density-heatmap.rb +5 -0
- data/lib/heatmap.rb +5 -0
- data/lib/heatmap/version.rb +3 -0
- data/lib/heatmap_formatter.rb +71 -0
- data/spec/example-data.csv +56 -0
- data/spec/heatmap_formatter_spec.rb +60 -0
- data/spec/spec_helper.rb +32 -0
- metadata +85 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/Rakefile
ADDED
data/bin/heatmap
ADDED
@@ -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
|
data/lib/heatmap.rb
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|