how_is 11.0.0 → 12.0.0
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.
- checksums.yaml +4 -4
- data/.rspec +0 -4
- data/.travis.yml +1 -4
- data/CHANGELOG.md +24 -0
- data/README.md +21 -7
- data/Rakefile +50 -18
- data/fixtures/vcr_cassettes/how-is-example-empty-repository.yml +946 -0
- data/fixtures/vcr_cassettes/how-is-example-repository.yml +956 -0
- data/fixtures/vcr_cassettes/how-is-with-config-file.yml +23877 -0
- data/lib/how_is/analyzer.rb +17 -2
- data/lib/how_is/report/base_report.rb +129 -0
- data/lib/how_is/report/html.rb +27 -13
- data/lib/how_is/report/json.rb +4 -2
- data/lib/how_is/report.rb +2 -87
- data/lib/how_is/version.rb +1 -1
- metadata +7 -6
- data/.rspec-ignore-tags +0 -2
- data/fixtures/vcr_cassettes/how_is-example-repository.yml +0 -951
- data/fixtures/vcr_cassettes/how_is-with-config-file.yml +0 -23582
data/lib/how_is/analyzer.rb
CHANGED
@@ -44,6 +44,9 @@ module HowIs
|
|
44
44
|
|
45
45
|
oldest_issue: issue_or_pull_to_hash(oldest_for(issues)),
|
46
46
|
oldest_pull: issue_or_pull_to_hash(oldest_for(pulls)),
|
47
|
+
|
48
|
+
newest_issue: issue_or_pull_to_hash(newest_for(issues)),
|
49
|
+
newest_pull: issue_or_pull_to_hash(newest_for(pulls)),
|
47
50
|
)
|
48
51
|
end
|
49
52
|
|
@@ -147,12 +150,24 @@ module HowIs
|
|
147
150
|
"approximately #{value}"
|
148
151
|
end
|
149
152
|
|
150
|
-
|
153
|
+
def sort_iops_by_created_at(issues_or_pulls)
|
154
|
+
issues_or_pulls.sort_by {|x| DateTime.parse(x['created_at']) }
|
155
|
+
end
|
156
|
+
|
157
|
+
# Given an Array of issues or pulls, return the oldest.
|
151
158
|
# Returns nil if no issues or pulls are provided.
|
152
159
|
def oldest_for(issues_or_pulls)
|
153
160
|
return nil if issues_or_pulls.empty?
|
154
161
|
|
155
|
-
issues_or_pulls
|
162
|
+
sort_iops_by_created_at(issues_or_pulls).first
|
163
|
+
end
|
164
|
+
|
165
|
+
# Given an Array of issues or pulls, return the newest.
|
166
|
+
# Returns nil if no issues or pulls are provided.
|
167
|
+
def newest_for(issues_or_pulls)
|
168
|
+
return nil if issues_or_pulls.empty?
|
169
|
+
|
170
|
+
sort_iops_by_created_at(issues_or_pulls).last
|
156
171
|
end
|
157
172
|
|
158
173
|
# Given an issue or PR, returns the date it was created.
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module HowIs
|
4
|
+
##
|
5
|
+
# Subclasses of BaseReport represent complete reports.
|
6
|
+
class BaseReport < Struct.new(:analysis)
|
7
|
+
def generate_report_text!
|
8
|
+
# title, text, header, horizontal_bar_graph, etc,
|
9
|
+
# append to @r, which is returned at the end of the function.
|
10
|
+
|
11
|
+
title "How is #{analysis.repository}?"
|
12
|
+
|
13
|
+
# DateTime#new_offset(0) sets the timezone to UTC. I think it does this
|
14
|
+
# without changing anything besides the timezone, but who knows, 'cause
|
15
|
+
# new_offset is entirely undocumented! (Even though it's used in the
|
16
|
+
# DateTime documentation!)
|
17
|
+
#
|
18
|
+
# TODO: Stop pretending everyone who runs how_is is in UTC.
|
19
|
+
text "Monthly report, ending on #{DateTime.now.new_offset(0).strftime('%B %e, %Y')}."
|
20
|
+
|
21
|
+
text github_pulse_summary
|
22
|
+
|
23
|
+
header "Pull Requests"
|
24
|
+
issue_or_pr_summary "pull", "pull request"
|
25
|
+
|
26
|
+
header "Issues"
|
27
|
+
issue_or_pr_summary "issue", "issue"
|
28
|
+
|
29
|
+
header "Issues Per Label"
|
30
|
+
issues_per_label = analysis.issues_with_label.to_a.sort_by { |(k, v)| v['total'].to_i }.reverse
|
31
|
+
issues_per_label.map! do |label, hash|
|
32
|
+
[label, hash['total'], hash['link']]
|
33
|
+
end
|
34
|
+
issues_per_label << ["(No label)", analysis.issues_with_no_label['total'], nil]
|
35
|
+
horizontal_bar_graph issues_per_label
|
36
|
+
|
37
|
+
# See comment at beginning of function.
|
38
|
+
@r
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
def format
|
44
|
+
raise NotImplementedError
|
45
|
+
end
|
46
|
+
|
47
|
+
def title(_text)
|
48
|
+
raise NotImplementedError
|
49
|
+
end
|
50
|
+
|
51
|
+
def header(_text)
|
52
|
+
raise NotImplementedError
|
53
|
+
end
|
54
|
+
|
55
|
+
def text(_text)
|
56
|
+
raise NotImplementedError
|
57
|
+
end
|
58
|
+
|
59
|
+
def link(_text, url)
|
60
|
+
raise NotImplementedError
|
61
|
+
end
|
62
|
+
|
63
|
+
def unordered_list(arr)
|
64
|
+
raise NotImplementedError
|
65
|
+
end
|
66
|
+
|
67
|
+
def horizontal_bar_graph(data)
|
68
|
+
raise NotImplementedError
|
69
|
+
end
|
70
|
+
|
71
|
+
def monthly_summary
|
72
|
+
raise NotImplementedError
|
73
|
+
end
|
74
|
+
|
75
|
+
def export
|
76
|
+
raise NotImplementedError
|
77
|
+
end
|
78
|
+
|
79
|
+
def export_file(file)
|
80
|
+
raise NotImplementedError
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_h
|
84
|
+
analysis.to_h
|
85
|
+
end
|
86
|
+
alias :to_hash :to_h
|
87
|
+
|
88
|
+
def to_json
|
89
|
+
JSON.pretty_generate(to_h)
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
def github_pulse_summary
|
94
|
+
@pulse ||= HowIs::Pulse.new(analysis.repository)
|
95
|
+
@pulse.send("#{format}_summary")
|
96
|
+
end
|
97
|
+
|
98
|
+
def pluralize(text, number)
|
99
|
+
number == 1 ? text : "#{text}s"
|
100
|
+
end
|
101
|
+
|
102
|
+
def are_is(number)
|
103
|
+
number == 1 ? "is" : "are"
|
104
|
+
end
|
105
|
+
|
106
|
+
def issue_or_pr_summary(type, type_label)
|
107
|
+
date_format = "%b %e, %Y"
|
108
|
+
a = analysis
|
109
|
+
|
110
|
+
number_of_type = a.send("number_of_#{type}s")
|
111
|
+
|
112
|
+
type_link = a.send("#{type}s_url")
|
113
|
+
oldest = a.send("oldest_#{type}")
|
114
|
+
newest = a.send("newest_#{type}")
|
115
|
+
|
116
|
+
if number_of_type == 0
|
117
|
+
text "There are #{link("no #{type_label}s open", type_link)}."
|
118
|
+
else
|
119
|
+
text "There #{are_is(number_of_type)} #{link("#{number_of_type} #{pluralize(type_label, number_of_type)} open", type_link)}."
|
120
|
+
|
121
|
+
unordered_list [
|
122
|
+
"Average age: #{a.send("average_#{type}_age")}.",
|
123
|
+
"#{link('Oldest ' + type_label, oldest['html_url'])} was opened on #{oldest['date'].strftime(date_format)}.",
|
124
|
+
"#{link('Newest ' + type_label, newest['html_url'])} was opened on #{newest['date'].strftime(date_format)}.",
|
125
|
+
]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/lib/how_is/report/html.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'cgi'
|
2
|
-
require 'how_is/
|
2
|
+
require 'how_is/report/base_report'
|
3
3
|
|
4
4
|
module HowIs
|
5
5
|
class HtmlReport < BaseReport
|
@@ -9,29 +9,42 @@ module HowIs
|
|
9
9
|
|
10
10
|
def title(_text)
|
11
11
|
@title = _text
|
12
|
-
@r += "<h1>#{_text}</h1
|
12
|
+
@r += "\n<h1>#{_text}</h1>\n"
|
13
13
|
end
|
14
14
|
|
15
15
|
def header(_text)
|
16
|
-
@r += "<h2>#{_text}</h2
|
16
|
+
@r += "\n<h2>#{_text}</h2>\n"
|
17
17
|
end
|
18
18
|
|
19
19
|
def link(_text, url)
|
20
20
|
%Q[<a href="#{url}">#{_text}</a>]
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
24
|
-
|
23
|
+
def text(_text)
|
24
|
+
@r += "<p>#{_text}</p>\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
def unordered_list(arr)
|
28
|
+
@r += "\n<ul>\n"
|
29
|
+
arr.each do |item|
|
30
|
+
@r += " <li>#{item}</li>\n"
|
31
|
+
end
|
32
|
+
@r += "</ul>\n\n"
|
25
33
|
end
|
26
34
|
|
27
35
|
def horizontal_bar_graph(data)
|
36
|
+
if data.length == 1 && data[0][0] == "(No label)"
|
37
|
+
text "There are no open issues to graph."
|
38
|
+
return
|
39
|
+
end
|
40
|
+
|
28
41
|
biggest = data.map { |x| x[1] }.max
|
29
42
|
get_percentage = ->(number_of_issues) { number_of_issues * 100 / biggest }
|
30
43
|
|
31
44
|
longest_label_length = data.map(&:first).map(&:length).max
|
32
45
|
label_width = "#{longest_label_length}ch"
|
33
46
|
|
34
|
-
@r +=
|
47
|
+
@r += "<table class=\"horizontal-bar-graph\">\n"
|
35
48
|
data.each do |row|
|
36
49
|
percentage = get_percentage.(row[1])
|
37
50
|
|
@@ -46,22 +59,23 @@ module HowIs
|
|
46
59
|
<td style="width: #{label_width}">#{label_text}</td>
|
47
60
|
<td><span class="fill" style="width: #{percentage}%">#{row[1]}</span></td>
|
48
61
|
</tr>
|
62
|
+
|
49
63
|
EOF
|
50
64
|
end
|
51
|
-
@r += "</table
|
65
|
+
@r += "</table>\n"
|
52
66
|
end
|
53
67
|
|
54
|
-
def
|
55
|
-
|
68
|
+
def monthly_summary
|
69
|
+
pulse.html_summary
|
56
70
|
end
|
57
71
|
|
58
|
-
def export
|
72
|
+
def export
|
59
73
|
@r = ''
|
60
|
-
|
74
|
+
generate_report_text!
|
61
75
|
end
|
62
76
|
|
63
|
-
def export_file(file
|
64
|
-
report = export
|
77
|
+
def export_file(file)
|
78
|
+
report = export
|
65
79
|
|
66
80
|
File.open(file, 'w') do |f|
|
67
81
|
f.puts <<-EOF
|
data/lib/how_is/report/json.rb
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
+
require 'how_is/report/base_report'
|
2
|
+
|
1
3
|
module HowIs
|
2
4
|
class JsonReport < BaseReport
|
3
5
|
def format
|
4
6
|
:json
|
5
7
|
end
|
6
8
|
|
7
|
-
def export
|
9
|
+
def export
|
8
10
|
to_json
|
9
11
|
end
|
10
12
|
|
11
|
-
def export_file(file
|
13
|
+
def export_file(file)
|
12
14
|
File.open(file, 'w') do |f|
|
13
15
|
f.write export
|
14
16
|
end
|
data/lib/how_is/report.rb
CHANGED
@@ -8,102 +8,17 @@ module HowIs
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
##
|
12
|
-
# Represents a completed report.
|
13
|
-
class BaseReport < Struct.new(:analysis)
|
14
|
-
def format
|
15
|
-
raise NotImplementedError
|
16
|
-
end
|
17
|
-
|
18
|
-
def github_pulse_summary
|
19
|
-
@pulse ||= HowIs::Pulse.new(analysis.repository)
|
20
|
-
@pulse.send("#{format}_summary")
|
21
|
-
end
|
22
|
-
|
23
|
-
def to_h
|
24
|
-
analysis.to_h
|
25
|
-
end
|
26
|
-
alias :to_hash :to_h
|
27
|
-
|
28
|
-
def to_json
|
29
|
-
to_h.to_json
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
def pluralize(text, number)
|
34
|
-
number == 1 ? text : "#{text}s"
|
35
|
-
end
|
36
|
-
|
37
|
-
def are_is(number)
|
38
|
-
number == 1 ? "is" : "are"
|
39
|
-
end
|
40
|
-
|
41
|
-
def issue_or_pr_summary(type, type_label)
|
42
|
-
oldest_date_format = "%b %e, %Y"
|
43
|
-
a = analysis
|
44
|
-
|
45
|
-
number_of_type = a.send("number_of_#{type}s")
|
46
|
-
|
47
|
-
type_link = a.send("#{type}s_url")
|
48
|
-
oldest = a.send("oldest_#{type}")
|
49
|
-
|
50
|
-
if number_of_type == 0
|
51
|
-
"There are #{link("no #{type_label}s open", type_link)}."
|
52
|
-
else
|
53
|
-
"There #{are_is(number_of_type)} #{link("#{number_of_type} #{pluralize(type_label, number_of_type)} open", type_link)}. " +
|
54
|
-
"The average #{type_label} age is #{a.send("average_#{type}_age")}, and the " +
|
55
|
-
"#{link("oldest", oldest['html_url'])} was opened on #{oldest['date'].strftime(oldest_date_format)}."
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
11
|
class Report
|
61
12
|
require 'how_is/report/json'
|
62
13
|
require 'how_is/report/html'
|
63
14
|
|
64
|
-
# The way this entire class works is there is a REPORT_BLOCK proc which
|
65
|
-
# calls various methods to create the report, and REPORT_BLOCK is eventually
|
66
|
-
# instance_exec'd by the various *Report (HtmlReport, previously PdfReport)
|
67
|
-
# classes, which define the methods required.
|
68
|
-
#
|
69
|
-
# The *Report class inherit from BaseReport, which implements the common
|
70
|
-
# methods for them.
|
71
|
-
|
72
|
-
REPORT_BLOCK = proc do
|
73
|
-
title "How is #{analysis.repository}?"
|
74
|
-
|
75
|
-
# DateTime#new_offset(0) sets the timezone to UTC. I think it does this
|
76
|
-
# without changing anything besides the timezone, but who knows, 'cause
|
77
|
-
# new_offset is entirely undocumented! (Even though it's used in the
|
78
|
-
# DateTime documentation!)
|
79
|
-
#
|
80
|
-
# TODO: Stop pretending everyone who runs how_is is in UTC.
|
81
|
-
text "Monthly report, ending on #{DateTime.now.new_offset(0).strftime('%B %e, %Y')}."
|
82
|
-
|
83
|
-
text github_pulse_summary
|
84
|
-
|
85
|
-
header "Pull Requests"
|
86
|
-
text issue_or_pr_summary "pull", "pull request"
|
87
|
-
|
88
|
-
header "Issues"
|
89
|
-
text issue_or_pr_summary "issue", "issue"
|
90
|
-
|
91
|
-
header "Issues Per Label"
|
92
|
-
issues_per_label = analysis.issues_with_label.to_a.sort_by { |(k, v)| v['total'].to_i }.reverse
|
93
|
-
issues_per_label.map! do |label, hash|
|
94
|
-
[label, hash['total'], hash['link']]
|
95
|
-
end
|
96
|
-
issues_per_label << ["(No label)", analysis.issues_with_no_label['total'], nil]
|
97
|
-
horizontal_bar_graph issues_per_label
|
98
|
-
end
|
99
|
-
|
100
15
|
##
|
101
16
|
# Export a report to a file.
|
102
17
|
def self.export_file(analysis, file)
|
103
18
|
format = file.split('.').last
|
104
19
|
report = get_report_class(format).new(analysis)
|
105
20
|
|
106
|
-
report.export_file(file
|
21
|
+
report.export_file(file)
|
107
22
|
end
|
108
23
|
|
109
24
|
##
|
@@ -111,7 +26,7 @@ module HowIs
|
|
111
26
|
def self.export(analysis, format = HowIs::DEFAULT_FORMAT)
|
112
27
|
report = get_report_class(format).new(analysis)
|
113
28
|
|
114
|
-
report.export
|
29
|
+
report.export
|
115
30
|
end
|
116
31
|
|
117
32
|
private
|
data/lib/how_is/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: how_is
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 12.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ellen Marie Dash
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-11
|
11
|
+
date: 2016-12-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: github_api
|
@@ -202,7 +202,6 @@ extra_rdoc_files: []
|
|
202
202
|
files:
|
203
203
|
- ".gitignore"
|
204
204
|
- ".rspec"
|
205
|
-
- ".rspec-ignore-tags"
|
206
205
|
- ".travis.yml"
|
207
206
|
- CHANGELOG.md
|
208
207
|
- CODE_OF_CONDUCT.md
|
@@ -213,8 +212,9 @@ files:
|
|
213
212
|
- bin/console
|
214
213
|
- bin/setup
|
215
214
|
- exe/how_is
|
216
|
-
- fixtures/vcr_cassettes/
|
217
|
-
- fixtures/vcr_cassettes/
|
215
|
+
- fixtures/vcr_cassettes/how-is-example-empty-repository.yml
|
216
|
+
- fixtures/vcr_cassettes/how-is-example-repository.yml
|
217
|
+
- fixtures/vcr_cassettes/how-is-with-config-file.yml
|
218
218
|
- how_is.gemspec
|
219
219
|
- lib/how_is.rb
|
220
220
|
- lib/how_is/analyzer.rb
|
@@ -223,6 +223,7 @@ files:
|
|
223
223
|
- lib/how_is/fetcher.rb
|
224
224
|
- lib/how_is/pulse.rb
|
225
225
|
- lib/how_is/report.rb
|
226
|
+
- lib/how_is/report/base_report.rb
|
226
227
|
- lib/how_is/report/html.rb
|
227
228
|
- lib/how_is/report/json.rb
|
228
229
|
- lib/how_is/version.rb
|
@@ -247,7 +248,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
247
248
|
version: '0'
|
248
249
|
requirements: []
|
249
250
|
rubyforge_project:
|
250
|
-
rubygems_version: 2.
|
251
|
+
rubygems_version: 2.4.5.2
|
251
252
|
signing_key:
|
252
253
|
specification_version: 4
|
253
254
|
summary: Quantify the health of a GitHub repository is.
|
data/.rspec-ignore-tags
DELETED