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.
@@ -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
- # Given an Array of issues or pulls, return the creation date of the oldest.
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.sort_by {|x| DateTime.parse(x['created_at']) }.first
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
@@ -1,5 +1,5 @@
1
1
  require 'cgi'
2
- require 'how_is/pulse'
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 monthly_summary
24
- pulse.html_summary
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 += '<table class="horizontal-bar-graph">'
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 text(_text)
55
- @r += "<p>#{_text}</p>"
68
+ def monthly_summary
69
+ pulse.html_summary
56
70
  end
57
71
 
58
- def export(&block)
72
+ def export
59
73
  @r = ''
60
- instance_exec(&block)
74
+ generate_report_text!
61
75
  end
62
76
 
63
- def export_file(file, &block)
64
- report = export(&block)
77
+ def export_file(file)
78
+ report = export
65
79
 
66
80
  File.open(file, 'w') do |f|
67
81
  f.puts <<-EOF
@@ -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(&block)
9
+ def export
8
10
  to_json
9
11
  end
10
12
 
11
- def export_file(file, &block)
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, &REPORT_BLOCK)
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(&REPORT_BLOCK)
29
+ report.export
115
30
  end
116
31
 
117
32
  private
@@ -1,3 +1,3 @@
1
1
  module HowIs
2
- VERSION = "11.0.0"
2
+ VERSION = "12.0.0"
3
3
  end
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: 11.0.0
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-13 00:00:00.000000000 Z
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/how_is-example-repository.yml
217
- - fixtures/vcr_cassettes/how_is-with-config-file.yml
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.6.8
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
@@ -1,2 +0,0 @@
1
- --format documentation
2
- --color