newsman 1.2.3 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8bff52319f7db052d667bb38fa31764a4204ddab745eeaaa1daf090d36a73789
4
- data.tar.gz: a7ccce39053631d627d210fea0897988b29ac24a62d9a17f98d83055777fbe40
3
+ metadata.gz: 7d8f8d600c711d2570fdff045de97d9162f5173e733b164c98ec667ad01e1158
4
+ data.tar.gz: 427198b752758475747275768a3db990f563d90d0853653ba81b1b03a1664257
5
5
  SHA512:
6
- metadata.gz: 638afa442dd92022d7966ea99405fea3838a6a92edd12c71f32d9a7cb8392c59287e647d8376524e9a97f61ea9ce3b74b5160b85b27940627d478528d76d3eef
7
- data.tar.gz: c8ee4f4a28435a66132f865046f222a2370361884d81d29536e1748778429cfd1a60581e6027448c0bbf5a491d55fda13e0d3f45af293e25b5fbe9d9fff5d5c2
6
+ metadata.gz: 4f1855fcef29439b336ef10eef275a927a5a4c47574c67275fd35e10bbe9a64b731081bcc80c0525b17d1c1b46c68759c14f05564de251f9e097b3708edebfd5
7
+ data.tar.gz: 3fde1f89ade004ccf2e3a77a86e94489590a933d3cf36e824a43d7ba314ddef53ff70235bee91e1a20edb3950804b1604caa72317aac099d95081b1941f1f7f1
@@ -57,6 +57,8 @@ class Docxout
57
57
  BULLET = /^\s{0,3}[-*]\s+(.+?)\s*$/
58
58
  # Matches markdown numbered list items, e.g. "1. item".
59
59
  NUMBERED = /^\s{0,3}\d+\.\s+(.+?)\s*$/
60
+ # Matches markdown bold markers, e.g. "**bold**" or "__bold__".
61
+ BOLD = /(\*\*|__)(.+?)\1/
60
62
 
61
63
  FONT = 'Times New Roman'
62
64
  # Word expresses font size in half-points, so 12pt is 24.
@@ -151,6 +153,6 @@ class Docxout
151
153
  def run_xml(line)
152
154
  "<w:r><w:rPr><w:rFonts w:ascii=\"#{FONT}\" w:hAnsi=\"#{FONT}\" w:cs=\"#{FONT}\"/>" \
153
155
  "<w:sz w:val=\"#{SIZE}\"/><w:szCs w:val=\"#{SIZE}\"/></w:rPr>" \
154
- "<w:t xml:space=\"preserve\">#{CGI.escapeHTML(line)}</w:t></w:r>"
156
+ "<w:t xml:space=\"preserve\">#{CGI.escapeHTML(line.gsub(BOLD, '\2'))}</w:t></w:r>"
155
157
  end
156
158
  end
@@ -24,8 +24,8 @@
24
24
 
25
25
  # This class represents a useful abstraction over Github API.
26
26
  class Github
27
- def initialize(token)
28
- @client = Octokit::Client.new(github_token: token)
27
+ def initialize(token, client: nil)
28
+ @client = client || Octokit::Client.new(github_token: token)
29
29
  end
30
30
 
31
31
  def pull_requests(username, repositories)
@@ -48,6 +48,24 @@ class Github
48
48
  end.select(&:important?)
49
49
  end
50
50
 
51
+ def issues_reviewed(username, repositories)
52
+ since = date_one_week_ago(Date.today)
53
+ reviewed_query = "is:issue involves:#{username} updated:>=#{since} #{repositories}"
54
+ closed_query = "is:issue is:closed involves:#{username} closed:>=#{since} #{repositories}"
55
+ puts "Searching issues reviewed using the following query: '#{reviewed_query}'"
56
+ reviewed = @client.search_issues(reviewed_query).total_count
57
+ puts "Searching issues closed using the following query: '#{closed_query}'"
58
+ closed = @client.search_issues(closed_query).total_count
59
+ IssueActivity.new(reviewed, closed)
60
+ end
61
+
62
+ def pull_requests_reviewed(username, repositories)
63
+ since = date_one_week_ago(Date.today)
64
+ query = "is:pr reviewed-by:#{username} updated:>=#{since} #{repositories}"
65
+ puts "Searching pull requests reviewed using the following query: '#{query}'"
66
+ @client.search_issues(query).total_count
67
+ end
68
+
51
69
  def parse_pr(pull_request)
52
70
  title = pull_request.title.to_s
53
71
  repository = pull_request.repository_url.split('/').last
@@ -25,6 +25,21 @@ require 'json'
25
25
 
26
26
  IMPORTANT_ISSUE = 'soon'
27
27
 
28
+ # Represents how many issues a user reviewed (labeled, assigned, commented, etc.)
29
+ # and how many of those were closed, during a given period.
30
+ class IssueActivity
31
+ attr_reader :reviewed, :closed
32
+
33
+ def initialize(reviewed, closed)
34
+ @reviewed = reviewed
35
+ @closed = closed
36
+ end
37
+
38
+ def to_s
39
+ "Issues reviewed: #{@reviewed} (labeled/assigned), #{@closed} closed"
40
+ end
41
+ end
42
+
28
43
  # This class represents a GitHub Issue abstraction created by a user.
29
44
  class Issue
30
45
  attr_accessor :title, :body, :repo, :number
@@ -65,9 +65,11 @@ end
65
65
 
66
66
  # Report items inner class.
67
67
  class ReportItems
68
- def initialize(prs, issues)
68
+ def initialize(prs, issues, issues_reviewed: nil, prs_reviewed: nil)
69
69
  @prs = prs || []
70
70
  @issues = issues || []
71
+ @issues_reviewed = issues_reviewed
72
+ @prs_reviewed = prs_reviewed
71
73
  end
72
74
 
73
75
  # Returns true if there are no pull requests or issues, false otherwise
@@ -78,6 +80,15 @@ class ReportItems
78
80
  def to_s
79
81
  prs_list = @prs.map(&:detailed_title).map { |obj| " - #{obj}\n" }.join
80
82
  issues_list = @issues.map(&:detailed_title).map { |obj| " - #{obj}\n" }.join
81
- "Closed Pull Requests:\n#{prs_list}\nOpen Issues:\n#{issues_list}"
83
+ "Closed Pull Requests:\n#{prs_list}\nOpen Issues:\n#{issues_list}#{activity}"
84
+ end
85
+
86
+ private
87
+
88
+ def activity
89
+ return '' unless @issues_reviewed || @prs_reviewed
90
+
91
+ lines = [@issues_reviewed&.to_s, (@prs_reviewed ? "PRs reviewed: #{@prs_reviewed}" : nil)].compact
92
+ "\n#{lines.join("\n")}\n"
82
93
  end
83
94
  end
data/lib/newsman.rb CHANGED
@@ -99,6 +99,8 @@ def generate
99
99
  github = Github.new(github_token)
100
100
  prs = github.pull_requests(github_username, github_repositories)
101
101
  issues = github.issues(github_username, github_repositories)
102
+ issues_reviewed = github.issues_reviewed(github_username, github_repositories)
103
+ prs_reviewed = github.pull_requests_reviewed(github_username, github_repositories)
102
104
  puts "\nNow lets test some aggregation using OpenAI\n\n"
103
105
  assistant = Assistant.new(openai_token, model: options[:model])
104
106
  # Build previous results
@@ -118,7 +120,7 @@ def generate
118
120
  reporter,
119
121
  reporter_position,
120
122
  options[:title],
121
- additional: ReportItems.new(prs, issues)
123
+ additional: ReportItems.new(prs, issues, issues_reviewed: issues_reviewed, prs_reviewed: prs_reviewed)
122
124
  )
123
125
  full_answer = assistant.format(report.build(
124
126
  answer,
data/test/test_docxout.rb CHANGED
@@ -86,6 +86,18 @@ class TestDocxout < Minitest::Test
86
86
  end
87
87
  end
88
88
 
89
+ def test_strips_markdown_bold_markers
90
+ Dir.mktmpdir do |temp_dir|
91
+ output = Docxout.new(temp_dir)
92
+ report = 'Deploys are **blocked** until review, __really__ blocked.'
93
+ path = File.join(temp_dir, output.print(report, 'volodya-lombrozo', 'gpt-3.5-turbo'))
94
+ document_xml = Zip::File.open(path) { |zip| zip.read('word/document.xml') }
95
+ assert_includes(document_xml, 'Deploys are blocked until review, really blocked.')
96
+ refute_includes(document_xml, '**')
97
+ refute_includes(document_xml, '__')
98
+ end
99
+ end
100
+
89
101
  def test_docx_package_includes_a_valid_numbering_part
90
102
  Dir.mktmpdir do |temp_dir|
91
103
  output = Docxout.new(temp_dir)
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright (c) 2024 Volodya Lombrozo
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the 'Software'), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in all
14
+ # copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
23
+
24
+ require 'minitest/autorun'
25
+ require_relative '../lib/newsman/issues'
26
+ require_relative '../lib/newsman/github'
27
+
28
+ # A fake Octokit client that records every query it receives and answers
29
+ # with a canned total_count, so we can assert on the query strings we build
30
+ # without touching the network.
31
+ class FakeOctokit
32
+ Result = Struct.new(:total_count, :items)
33
+
34
+ def initialize(counts)
35
+ @counts = counts
36
+ @queries = []
37
+ end
38
+
39
+ attr_reader :queries
40
+
41
+ def search_issues(query)
42
+ @queries << query
43
+ Result.new(@counts.fetch(query, 0), [])
44
+ end
45
+ end
46
+
47
+ class TestGithub < Minitest::Test
48
+ def test_issues_reviewed_builds_involves_and_closed_queries
49
+ client = FakeOctokit.new({})
50
+ github = Github.new('token', client: client)
51
+ github.issues_reviewed('volodya-lombrozo', 'repo:objectionary/eo')
52
+ assert(client.queries.any? { |q| q.include?('is:issue') && q.include?('involves:volodya-lombrozo') })
53
+ assert(client.queries.any? do |q|
54
+ q.include?('is:issue') && q.include?('is:closed') && q.include?('involves:volodya-lombrozo')
55
+ end)
56
+ end
57
+
58
+ def test_issues_reviewed_returns_reviewed_and_closed_counts
59
+ since = date_one_week_ago(Date.today)
60
+ reviewed_query = "is:issue involves:volodya-lombrozo updated:>=#{since} repo:objectionary/eo"
61
+ closed_query = "is:issue is:closed involves:volodya-lombrozo closed:>=#{since} repo:objectionary/eo"
62
+ client = FakeOctokit.new(reviewed_query => 7, closed_query => 3)
63
+ github = Github.new('token', client: client)
64
+ activity = github.issues_reviewed('volodya-lombrozo', 'repo:objectionary/eo')
65
+ assert_equal 7, activity.reviewed
66
+ assert_equal 3, activity.closed
67
+ assert_equal 'Issues reviewed: 7 (labeled/assigned), 3 closed', activity.to_s
68
+ end
69
+
70
+ def test_pull_requests_reviewed_builds_reviewed_by_query
71
+ since = date_one_week_ago(Date.today)
72
+ query = "is:pr reviewed-by:volodya-lombrozo updated:>=#{since} repo:objectionary/eo"
73
+ client = FakeOctokit.new(query => 5)
74
+ github = Github.new('token', client: client)
75
+ assert_equal 5, github.pull_requests_reviewed('volodya-lombrozo', 'repo:objectionary/eo')
76
+ end
77
+ end
data/test/test_report.rb CHANGED
@@ -73,4 +73,17 @@ class TestReport < Minitest::Test
73
73
  actual = ReportItems.new(prs, issues).to_s
74
74
  assert_equal expected, actual
75
75
  end
76
+
77
+ def test_report_items_with_activity_metrics
78
+ expected = <<~EXPECTED
79
+ Closed Pull Requests:
80
+
81
+ Open Issues:
82
+
83
+ Issues reviewed: 7 (labeled/assigned), 3 closed
84
+ PRs reviewed: 5
85
+ EXPECTED
86
+ actual = ReportItems.new([], [], issues_reviewed: IssueActivity.new(7, 3), prs_reviewed: 5).to_s
87
+ assert_equal expected, actual
88
+ end
76
89
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: newsman
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.3
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Volodya Lombrozo
@@ -201,6 +201,7 @@ files:
201
201
  - lib/newsman/txt_output.rb
202
202
  - test/test_assistant.rb
203
203
  - test/test_docxout.rb
204
+ - test/test_github.rb
204
205
  - test/test_htmlout.rb
205
206
  - test/test_issue.rb
206
207
  - test/test_pdd_issue.rb