reviewlette 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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