reviewlette 0.0.6

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.
@@ -0,0 +1,77 @@
1
+ require 'sequel'
2
+ require 'fileutils'
3
+ module Reviewlette
4
+
5
+ class Database
6
+
7
+ FileUtils.mkdir_p("#{File.join(ENV['HOME'])}/.config/reviewlette/") unless Dir.exists?("#{ENV['HOME']}/.config/reviewlette")
8
+ FileUtils.cp ("#{File.dirname(__FILE__)}/../../reviewlette.db"), ("#{File.join(Dir.home)}" + '/.config/reviewlette/') unless File.exists?(("#{File.join(Dir.home)}" + '/.config/reviewlette/reviewlette.db'))
9
+
10
+ @path = "#{File.join(ENV['HOME'])}/.config/reviewlette"
11
+ DATABASE = Sequel.connect("sqlite://#{@path}/reviewlette.db")
12
+
13
+ attr_accessor :reviewer, :reviews
14
+
15
+ def initialize
16
+ @reviewer = DATABASE.from(:reviewer)
17
+ @reviews = DATABASE.from(:reviews)
18
+ end
19
+
20
+ def count_up(reviewer)
21
+ pr_reviewer = @reviewer.where(:trello_name => reviewer).select(:trello_name).first.values.first
22
+ counter = @reviewer.where(:trello_name => pr_reviewer).select(:reviews).first.values.first
23
+ @reviewer.where(:trello_name => reviewer).update(:reviews => counter.next)
24
+ end
25
+
26
+ def add_pr_to_db(pr_name, reviewer)
27
+ @reviews.insert(:name => pr_name, :reviewer => reviewer, :created_at => Date.today)
28
+ count_up(reviewer)
29
+ end
30
+
31
+ def get_users_tel_entries
32
+ @reviewer.map([:tel_name]).flatten.select{|user| user unless user.nil?}
33
+ end
34
+
35
+ def get_users_gh_entries
36
+ @reviewer.map([:gh_name]).flatten.select{|user| user unless user.nil?}
37
+ end
38
+
39
+ def get_users_trello_entries
40
+ @reviewer.where(:vacation => 'false').map([:trello_name]).flatten.select{|user| user unless user.nil?}
41
+ end
42
+
43
+ def count_reviews(reviewer)
44
+ @reviews.where(:reviewer => reviewer).count
45
+ end
46
+
47
+ def find_gh_name_by_trello_name(trello_name)
48
+ @reviewer.where(:trello_name => trello_name).select(:gh_name).first.values.first
49
+ end
50
+
51
+ def set_vacation_flag(reviewer, state)
52
+ @reviewer.where(:tel_name => reviewer).update(:vacation => state)
53
+ end
54
+
55
+ def conscruct_graph_struct
56
+ data = []
57
+ get_users_trello_entries.each do |x|
58
+ data.push({ label: x, value: count_reviews(x) })
59
+ end
60
+ data
61
+ end
62
+
63
+ def conscruct_line_data
64
+ data = []
65
+ date_range = (Date.today - 7 )..(Date.today)
66
+ get_users_trello_entries.each do |name|
67
+ date_range.each do |date|
68
+ abc = {}
69
+ abc[:created_at] = date
70
+ abc[name] = @reviews.where(:reviewer => name, :created_at => date).select(:created_at).count
71
+ data.push(abc)
72
+ end
73
+ end
74
+ data
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,7 @@
1
+ module Reviewlette
2
+ class AlreadyAssignedException < Exception
3
+ end
4
+
5
+ class NoTrelloCardException < Exception
6
+ end
7
+ end
@@ -0,0 +1,45 @@
1
+ require 'yaml'
2
+ require 'octokit'
3
+
4
+ module Reviewlette
5
+
6
+ class GithubConnection
7
+
8
+ GITHUB_CONFIG = YAML.load_file("#{File.dirname(__FILE__)}/../../config/.github.yml")
9
+
10
+ attr_accessor :client, :repo
11
+
12
+ def initialize
13
+ gh_connection
14
+ end
15
+
16
+ def gh_connection
17
+ @client = Octokit::Client.new(:access_token => GITHUB_CONFIG['token'])
18
+ end
19
+
20
+ def get_branch_name(pr_id, repo)
21
+ @client.pull_requests(repo)[pr_id].head.ref
22
+ end
23
+
24
+ def list_pulls(repo)
25
+ @client.pull_requests(repo)
26
+ end
27
+
28
+ def pull_merged?(repo, number)
29
+ client.pull_merged?(repo, number)
30
+ end
31
+
32
+ def add_assignee(repo, number, title, body, name)
33
+ @client.update_issue(repo, number, title, body, :assignee => name)
34
+ end
35
+
36
+ def comment_on_issue(repo, number, name, trello_card_url)
37
+ @client.add_comment(repo, number, "@#{name} is your reviewer :thumbsup: check #{trello_card_url}")
38
+ end
39
+
40
+ def list_issues(repo)
41
+ @client.list_issues(repo)
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,62 @@
1
+ require 'json'
2
+ require_relative 'database'
3
+ module Reviewlette
4
+
5
+ class Graphgenerator
6
+
7
+ def initialize
8
+ @db = Reviewlette::Database.new
9
+ end
10
+
11
+ def write_to_graphs(filename, content)
12
+ File.open(filename, 'w') { |file| file.write(content) }
13
+ end
14
+
15
+
16
+ def model_graphs(data2, data, type)
17
+ @content = %Q|
18
+ <link rel="stylesheet" href="http://cdn.oesmith.co.uk/morris-0.5.1.css">
19
+ <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
20
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
21
+ <script src="http://cdn.oesmith.co.uk/morris-0.5.1.min.js"></script>
22
+ <meta http-equiv="refresh" content="15" />
23
+
24
+ <div id="Donut" style="height: 250px;"></div>
25
+ <div id="Line" style="height: 250px;"></div>
26
+ <div id="Bar" style="height: 250px;"></div>
27
+
28
+ <script>
29
+ new Morris.#{type}({
30
+ element: 'Donut',
31
+ data: #{data},
32
+ xkey: 'label',
33
+ colors: ['#80BFFF', '#F0F0F0', '#0000FF', '#00FFFF', '#FF00FF', '#C0C0C0'],
34
+ ykeys: ['value'],
35
+ labels: ['Value']
36
+ });
37
+ </script>
38
+
39
+ <script>
40
+ new Morris.Line({
41
+ element: 'Line',
42
+ data: #{data2},
43
+ xkey: 'created_at',
44
+ colors: ['#80BFFF', '#F0F0F0', '#0000FF', '#00FFFF', '#FF00FF', '#C0C0C0'],
45
+ ykeys: #{@db.get_users_trello_entries},
46
+ labels: #{@db.get_users_trello_entries}
47
+ });
48
+ </script>
49
+
50
+ <script>
51
+ new Morris.Bar({
52
+ element: 'Bar',
53
+ data: #{data},
54
+ xkey: 'label',
55
+ colors: ['#80BFFF', '#F0F0F0', '#0000FF', '#00FFFF', '#FF00FF', '#C0C0C0'],
56
+ ykeys: ['value'],
57
+ labels: ['Value']
58
+ });
59
+ </script>|
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,27 @@
1
+
2
+ require 'net/smtp'
3
+
4
+ module Supporter
5
+ class Mailer
6
+ def send_email(to,opts={})
7
+ opts[:server] ||= 'localhost'
8
+ opts[:from] ||= 'review@lette.com'
9
+ opts[:from_alias] ||= 'Reviewlette'
10
+ opts[:subject] ||= "Commanding Officer of the Week"
11
+ opts[:body] ||= ""
12
+
13
+ msg = <<END_OF_MESSAGE
14
+ From: #{opts[:from_alias]} <#{opts[:from]}>
15
+ To: <#{to}>
16
+ Subject: #{opts[:subject]}
17
+ #{opts[:body]}
18
+ END_OF_MESSAGE
19
+
20
+ Net::SMTP.start(opts[:server]) do |smtp|
21
+ smtp.send_message msg, opts[:from], to
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ ## make this more generic in order to use it either for COotW and PairProgramming sessions and reviews
@@ -0,0 +1,79 @@
1
+ require 'yaml'
2
+ require 'trello'
3
+ require 'logger'
4
+ require_relative 'database'
5
+ require_relative 'exceptions'
6
+
7
+ class Trello::Card
8
+
9
+ def assignees
10
+ @trello_connection = ::Reviewlette::TrelloConnection.new
11
+ member_ids.map{|id| @trello_connection.find_member_by_id(id)}
12
+ end
13
+ end
14
+
15
+ module Reviewlette
16
+
17
+ class TrelloConnection
18
+
19
+ attr_accessor :board
20
+
21
+ def initialize
22
+ setup_trello
23
+ end
24
+
25
+ def determine_reviewer(card)
26
+ raise AlreadyAssignedException, "Everyone on the team is assigned to the Card." if reviewer_exception_handler(card)
27
+ find_member_by_username(sample_reviewer(card))
28
+ end
29
+
30
+ def sample_reviewer(card)
31
+ (team - card.assignees.map(&:username)).sample
32
+ end
33
+
34
+ def reviewer_exception_handler(card)
35
+ (team - card.assignees.map(&:username)).count <= 0
36
+ end
37
+
38
+ def add_reviewer_to_card(reviewer, card)
39
+ card.add_member(reviewer) if reviewer
40
+ end
41
+
42
+ def comment_on_card(reviewer, card)
43
+ card.add_comment(reviewer) if reviewer
44
+ end
45
+
46
+ def move_card_to_list(card, column)
47
+ card.move_to_list(column)
48
+ end
49
+
50
+ def team
51
+ #where vacation is not false
52
+ @team ||= Reviewlette::Database.new.get_users_trello_entries
53
+ end
54
+
55
+ def find_column(column_name)
56
+ @board.lists.find {|x| x.name == column_name}
57
+ end
58
+
59
+ def find_member_by_username(username)
60
+ @board.members.find{|m| m.username == username}
61
+ end
62
+
63
+ def find_member_by_id(id)
64
+ @board.members.find{|m| m.id == id}
65
+ end
66
+
67
+ def find_card_by_id(id)
68
+ @board.cards.find{|c| c.short_id == id.to_i}
69
+ end
70
+
71
+ def setup_trello
72
+ Trello.configure do |config|
73
+ config.developer_public_key = ::Reviewlette::TRELLO_CONFIG1['consumerkey']
74
+ config.member_token = ::Reviewlette::TRELLO_CONFIG1['oauthtoken']
75
+ end
76
+ @board = Trello::Board.find(::Reviewlette::TRELLO_CONFIG1['board_id'])
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,38 @@
1
+ require 'net/telnet'
2
+ module Reviewlette
3
+
4
+ class Vacations
5
+
6
+ def self.find_vacations(username)
7
+ vacations = []
8
+ tn = Net::Telnet.new('Host' => 'present.suse.de', 'Port' => 9874, 'Binmode' => false)
9
+ collect = false
10
+ tn.cmd(username) do |data|
11
+ data.split("\n").each do |l|
12
+ collect = true if l =~ /^Absence/
13
+ next unless collect
14
+ if l[0,1] == "-"
15
+ collect = false
16
+ next
17
+ end
18
+ dates = []
19
+ l.split(" ").each do |date|
20
+ unless date =~ /#{Time.now.year}/
21
+ next
22
+ end
23
+ dates.push(date)
24
+ end
25
+ case dates.size
26
+ when 1
27
+ vacations.push("#{dates[0]}")
28
+ when 2
29
+ vacations.push("#{dates[0]} - #{dates[1]}")
30
+ else
31
+ end
32
+ end
33
+ end
34
+ tn.close
35
+ vacations
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module Reviewlette
2
+ VERSION = '0.0.6'
3
+ end
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'prophet'
3
+ system('bundle')
4
+ system('rspec')
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'reviewlette/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "reviewlette"
8
+ spec.version = Reviewlette::VERSION
9
+ spec.authors = ["jschmid1"]
10
+ spec.email = ["jschmid@suse.de"]
11
+ spec.summary = %q{Randomly assignes a reviewer to your Pullrequest and corresponding Trello Card.}
12
+ spec.description = %q{Easy, fair and trackable labor division in your team.}
13
+ spec.homepage = "http://rubygems.org/gems/reviewlette"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency 'ruby-trello', '=1.1.1'
22
+ spec.add_runtime_dependency 'octokit', '=3.1.0'
23
+ spec.add_runtime_dependency 'sequel', '=4.13.0'
24
+ spec.add_runtime_dependency 'sqlite3', '=1.3.9'
25
+ end
26
+
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Reviewlette::Database do
4
+
5
+
6
+ subject { Reviewlette::Database.new }
7
+
8
+ describe '#count_reviews' do
9
+
10
+ it 'counts the reviews done by a single user' do
11
+ to_be_counted = [1,2,3,4]
12
+ expect(subject.instance_variable_get(:@reviews)).to receive(:where).and_return to_be_counted
13
+ expect(to_be_counted).to receive(:count).and_return to_be_counted.count
14
+ subject.count_reviews(subject.reviewer.first.values[1])
15
+ end
16
+ end
17
+
18
+ describe '#get_users_gh_entries' do
19
+ it 'gets all github usernames in #Array' do
20
+ expect(subject.reviewer).to receive(:map).and_return [['jschmid']]
21
+ subject.get_users_gh_entries
22
+ end
23
+ end
24
+
25
+ describe '#add_pr_to_db' do
26
+
27
+ it 'writes the name of the pr to db' do
28
+ expect(subject.reviews).to receive(:insert)
29
+ expect(subject).to receive(:count_up).with(subject.reviewer.first.values[1])
30
+ subject.add_pr_to_db('review_123', subject.reviewer.first.values[1])
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,96 @@
1
+ require 'spec_helper'
2
+
3
+ describe Reviewlette::GithubConnection do
4
+
5
+ subject { Reviewlette::GithubConnection }
6
+
7
+ describe '.new' do
8
+
9
+ it 'sets up Github connection' do
10
+ config = Reviewlette::GithubConnection::GITHUB_CONFIG
11
+ expect(Octokit::Client).to receive(:new).with(:access_token => config['token'])
12
+ subject.new
13
+ end
14
+ end
15
+
16
+ describe '#pull_merged?' do
17
+ let( :connection ) { subject.new }
18
+
19
+ it 'checks if the pull is merged' do
20
+ allow(connection.client).to receive(:pull_merged?).with('true', 6).and_return true
21
+ expect(connection.pull_merged?('true', 6)).to be true
22
+ end
23
+
24
+ it 'checks if the pull is not merged' do
25
+ allow(connection.client).to receive(:pull_merged?).with('false', 5).and_return false
26
+ expect(connection.pull_merged?('false', 5)).to be false
27
+ end
28
+ end
29
+
30
+ describe '#add_assignee' do
31
+ let( :connection ) { subject.new }
32
+
33
+ it 'adds an assignee to the gh issue' do
34
+ params = [connection.repo, 4, 'title', 'body', 'name']
35
+ params2 = [connection.repo, 4, 'title', 'body', :assignee => 'name']
36
+ allow(connection.client).to receive(:update_issue).with(*params2).and_return true
37
+ expect(connection.add_assignee(*params)).to eq true
38
+ end
39
+
40
+ it 'fails to add an assignee to the gh issue' do
41
+ params = [connection.repo, 4, 'title', 'body', 'name']
42
+ params2 = [connection.repo, 4, 'title', 'body', :assignee => 'name']
43
+ allow(connection.client).to receive(:update_issue).with(*params2).and_return false
44
+ expect(connection.add_assignee(*params)).to eq false
45
+ end
46
+ end
47
+
48
+ describe '#comment_on_issue' do
49
+ let( :connection ) { subject.new }
50
+
51
+ it 'comments on a given issue' do
52
+ params = [connection.repo, 4, '@name is your reviewer :thumbsup: check url']
53
+ params2 = [connection.repo, 4, 'name', 'url']
54
+ allow(connection.client).to receive(:add_comment).with(*params).and_return true
55
+ expect(connection.comment_on_issue(*params2)).to eq true
56
+ end
57
+
58
+ it 'fails to comment on a given issue and fails' do
59
+ params = [connection.repo, 4, '@name is your reviewer :thumbsup: check url']
60
+ params2 = [connection.repo, 4, 'name', 'url']
61
+ allow(connection.client).to receive(:add_comment).with(*params).and_return false
62
+ expect(connection.comment_on_issue(*params2)).to eq false
63
+ end
64
+ end
65
+
66
+ describe '#list_issues' do
67
+ let( :connection ) { subject.new }
68
+
69
+ it 'fails to determine if an assignee is set' do
70
+ allow(connection.client).to receive_message_chain(:list_issues)
71
+ connection.list_issues(connection.repo)
72
+ end
73
+ end
74
+
75
+ describe '#list_pulls' do
76
+ let( :connection ) { subject.new }
77
+
78
+ it 'lists a pullrequests for a given repository' do
79
+ expect(connection.client).to receive(:pull_requests)
80
+ connection.list_pulls(connection.repo)
81
+ end
82
+ end
83
+
84
+ describe '#get_branch_name' do
85
+ let( :connection ) { subject.new }
86
+
87
+ it 'get branch name based on a repo and a pullrequest id' do
88
+ pulls = [double({ 'head' => double({ 'ref' => 'number'})})]
89
+ pr = pulls.first
90
+ expect(connection.client).to receive(:pull_requests).with(connection.repo).and_return pulls
91
+ expect(pulls).to receive(:[]).with(3).and_return pr
92
+ expect(pr).to receive(:head).and_return pr.head
93
+ connection.get_branch_name(3, connection.repo)
94
+ end
95
+ end
96
+ end