defect-density-heatmap 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|