Merge pull request #1409 from riyad/update-votes
Update votes for issues and merge requests
This commit is contained in:
commit
40eec08c99
17 changed files with 256 additions and 66 deletions
|
@ -415,13 +415,48 @@ p.time {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.upvotes {
|
.votes {
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
font-weight: bold;
|
line-height: 15px;
|
||||||
color: #468847;
|
.progress {
|
||||||
text-align: right;
|
height: 4px;
|
||||||
padding: 4px;
|
margin: 0;
|
||||||
margin: 2px;
|
.bar {
|
||||||
|
float: left;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.bar-success {
|
||||||
|
background-color: #468847;
|
||||||
|
@include bg-gradient(#62C462, #51A351);
|
||||||
|
}
|
||||||
|
.bar-danger {
|
||||||
|
background-color: #B94A48;
|
||||||
|
@include bg-gradient(#EE5F5B, #BD362F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.upvotes {
|
||||||
|
display: inline-block;
|
||||||
|
color: #468847;
|
||||||
|
}
|
||||||
|
.downvotes {
|
||||||
|
display: inline-block;
|
||||||
|
color: #B94A48;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.votes-block {
|
||||||
|
margin: 14px 6px 6px 0;
|
||||||
|
.downvotes {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.votes-inline {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 8px;
|
||||||
|
.progress {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 0 2px;
|
||||||
|
width: 45px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix for readme code (stopped it from being yellow) */
|
/* Fix for readme code (stopped it from being yellow) */
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class Issue < ActiveRecord::Base
|
class Issue < ActiveRecord::Base
|
||||||
include IssueCommonality
|
include IssueCommonality
|
||||||
include Upvote
|
include Votes
|
||||||
|
|
||||||
acts_as_taggable_on :labels
|
acts_as_taggable_on :labels
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ require File.join(Rails.root, "app/models/commit")
|
||||||
|
|
||||||
class MergeRequest < ActiveRecord::Base
|
class MergeRequest < ActiveRecord::Base
|
||||||
include IssueCommonality
|
include IssueCommonality
|
||||||
include Upvote
|
include Votes
|
||||||
|
|
||||||
BROKEN_DIFF = "--broken-diff"
|
BROKEN_DIFF = "--broken-diff"
|
||||||
|
|
||||||
|
|
|
@ -105,6 +105,12 @@ class Note < ActiveRecord::Base
|
||||||
def upvote?
|
def upvote?
|
||||||
note.start_with?('+1') || note.start_with?(':+1:')
|
note.start_with?('+1') || note.start_with?(':+1:')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns true if this is a downvote note,
|
||||||
|
# otherwise false is returned
|
||||||
|
def downvote?
|
||||||
|
note.start_with?('-1') || note.start_with?(':-1:')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
# == Schema Information
|
# == Schema Information
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
module Upvote
|
|
||||||
# Return the number of +1 comments (upvotes)
|
|
||||||
def upvotes
|
|
||||||
notes.select(&:upvote?).size
|
|
||||||
end
|
|
||||||
end
|
|
32
app/roles/votes.rb
Normal file
32
app/roles/votes.rb
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
module Votes
|
||||||
|
# Return the number of +1 comments (upvotes)
|
||||||
|
def upvotes
|
||||||
|
notes.select(&:upvote?).size
|
||||||
|
end
|
||||||
|
|
||||||
|
def upvotes_in_percent
|
||||||
|
if votes_count.zero?
|
||||||
|
0
|
||||||
|
else
|
||||||
|
100.0 / votes_count * upvotes
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the number of -1 comments (downvotes)
|
||||||
|
def downvotes
|
||||||
|
notes.select(&:downvote?).size
|
||||||
|
end
|
||||||
|
|
||||||
|
def downvotes_in_percent
|
||||||
|
if votes_count.zero?
|
||||||
|
0
|
||||||
|
else
|
||||||
|
100.0 - upvotes_in_percent
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the total number of votes
|
||||||
|
def votes_count
|
||||||
|
upvotes + downvotes
|
||||||
|
end
|
||||||
|
end
|
|
@ -34,5 +34,5 @@
|
||||||
- else
|
- else
|
||||||
|
|
||||||
|
|
||||||
- if issue.upvotes > 0
|
- if issue.votes_count > 0
|
||||||
%span.badge.badge-success= "+#{issue.upvotes}"
|
= render 'votes/votes_inline', votable: issue
|
||||||
|
|
|
@ -8,22 +8,22 @@
|
||||||
%span.right
|
%span.right
|
||||||
- if can?(current_user, :admin_project, @project) || @issue.author == current_user
|
- if can?(current_user, :admin_project, @project) || @issue.author == current_user
|
||||||
- if @issue.closed
|
- if @issue.closed
|
||||||
= link_to 'Reopen', project_issue_path(@project, @issue, issue: {closed: false }, status_only: true), method: :put, class: "btn small"
|
= link_to 'Reopen', project_issue_path(@project, @issue, issue: {closed: false }, status_only: true), method: :put, class: "btn grouped success"
|
||||||
- else
|
- else
|
||||||
= link_to 'Close', project_issue_path(@project, @issue, issue: {closed: true }, status_only: true), method: :put, class: "btn small", title: "Close Issue"
|
= link_to 'Close', project_issue_path(@project, @issue, issue: {closed: true }, status_only: true), method: :put, class: "btn grouped danger", title: "Close Issue"
|
||||||
- if can?(current_user, :admin_project, @project) || @issue.author == current_user
|
- if can?(current_user, :admin_project, @project) || @issue.author == current_user
|
||||||
= link_to edit_project_issue_path(@project, @issue), class: "btn small" do
|
= link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do
|
||||||
%i.icon-edit
|
%i.icon-edit
|
||||||
Edit
|
Edit
|
||||||
|
|
||||||
%br
|
.right
|
||||||
- if @issue.upvotes > 0
|
.span3#votes= render 'votes/votes_block', votable: @issue
|
||||||
.upvotes#upvotes= "+#{pluralize @issue.upvotes, 'upvote'}"
|
|
||||||
|
|
||||||
.back_link
|
.back_link
|
||||||
= link_to project_issues_path(@project) do
|
= link_to project_issues_path(@project) do
|
||||||
← To issues list
|
← To issues list
|
||||||
|
|
||||||
|
|
||||||
.main_box
|
.main_box
|
||||||
.top_box_content
|
.top_box_content
|
||||||
%h4
|
%h4
|
||||||
|
|
|
@ -23,5 +23,6 @@
|
||||||
authored by #{merge_request.author_name}
|
authored by #{merge_request.author_name}
|
||||||
= time_ago_in_words(merge_request.created_at)
|
= time_ago_in_words(merge_request.created_at)
|
||||||
ago
|
ago
|
||||||
- if merge_request.upvotes > 0
|
|
||||||
%span.badge.badge-success= "+#{merge_request.upvotes}"
|
- if merge_request.votes_count > 0
|
||||||
|
= render 'votes/votes_inline', votable: merge_request
|
||||||
|
|
|
@ -23,10 +23,8 @@
|
||||||
%i.icon-edit
|
%i.icon-edit
|
||||||
Edit
|
Edit
|
||||||
|
|
||||||
%br
|
.right
|
||||||
- if @merge_request.upvotes > 0
|
.span3#votes= render 'votes/votes_block', votable: @merge_request
|
||||||
.upvotes#upvotes= "+#{pluralize @merge_request.upvotes, 'upvote'}"
|
|
||||||
|
|
||||||
|
|
||||||
.back_link
|
.back_link
|
||||||
= link_to project_merge_requests_path(@project) do
|
= link_to project_merge_requests_path(@project) do
|
||||||
|
|
6
app/views/votes/_votes_block.html.haml
Normal file
6
app/views/votes/_votes_block.html.haml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
.votes.votes-block
|
||||||
|
.progress
|
||||||
|
.bar.bar-success{style: "width: #{votable.upvotes_in_percent}%;"}
|
||||||
|
.bar.bar-danger{style: "width: #{votable.downvotes_in_percent}%;"}
|
||||||
|
.upvotes= "#{votable.upvotes} up"
|
||||||
|
.downvotes= "#{votable.downvotes} down"
|
6
app/views/votes/_votes_inline.html.haml
Normal file
6
app/views/votes/_votes_inline.html.haml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
.votes.votes-inline
|
||||||
|
.upvotes= votable.upvotes
|
||||||
|
.progress
|
||||||
|
.bar.bar-success{style: "width: #{votable.upvotes_in_percent}%;"}
|
||||||
|
.bar.bar-danger{style: "width: #{votable.downvotes_in_percent}%;"}
|
||||||
|
.downvotes= votable.downvotes
|
|
@ -12,7 +12,7 @@ describe Issue do
|
||||||
|
|
||||||
describe 'modules' do
|
describe 'modules' do
|
||||||
it { should include_module(IssueCommonality) }
|
it { should include_module(IssueCommonality) }
|
||||||
it { should include_module(Upvote) }
|
it { should include_module(Votes) }
|
||||||
end
|
end
|
||||||
|
|
||||||
subject { Factory.create(:issue) }
|
subject { Factory.create(:issue) }
|
||||||
|
|
|
@ -8,6 +8,6 @@ describe MergeRequest do
|
||||||
|
|
||||||
describe 'modules' do
|
describe 'modules' do
|
||||||
it { should include_module(IssueCommonality) }
|
it { should include_module(IssueCommonality) }
|
||||||
it { should include_module(Upvote) }
|
it { should include_module(Votes) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,6 +24,13 @@ describe Note do
|
||||||
it "recognizes a neutral note" do
|
it "recognizes a neutral note" do
|
||||||
note = Factory(:note, note: "This is not a +1 note")
|
note = Factory(:note, note: "This is not a +1 note")
|
||||||
note.should_not be_upvote
|
note.should_not be_upvote
|
||||||
|
note.should_not be_downvote
|
||||||
|
end
|
||||||
|
|
||||||
|
it "recognizes a neutral emoji note" do
|
||||||
|
note = build(:note, note: "I would :+1: this, but I don't want to")
|
||||||
|
note.should_not be_upvote
|
||||||
|
note.should_not be_downvote
|
||||||
end
|
end
|
||||||
|
|
||||||
it "recognizes a +1 note" do
|
it "recognizes a +1 note" do
|
||||||
|
@ -31,19 +38,19 @@ describe Note do
|
||||||
note.should be_upvote
|
note.should be_upvote
|
||||||
end
|
end
|
||||||
|
|
||||||
it "recognizes a -1 note as no vote" do
|
|
||||||
note = Factory(:note, note: "-1 for this")
|
|
||||||
note.should_not be_upvote
|
|
||||||
end
|
|
||||||
|
|
||||||
it "recognizes a +1 emoji as a vote" do
|
it "recognizes a +1 emoji as a vote" do
|
||||||
note = build(:note, note: ":+1: for this")
|
note = build(:note, note: ":+1: for this")
|
||||||
note.should be_upvote
|
note.should be_upvote
|
||||||
end
|
end
|
||||||
|
|
||||||
it "recognizes a neutral emoji note" do
|
it "recognizes a -1 note" do
|
||||||
note = build(:note, note: "I would :+1: this, but I don't want to")
|
note = Factory(:note, note: "-1 for this")
|
||||||
note.should_not be_upvote
|
note.should be_downvote
|
||||||
|
end
|
||||||
|
|
||||||
|
it "recognizes a -1 emoji as a vote" do
|
||||||
|
note = build(:note, note: ":-1: for this")
|
||||||
|
note.should be_downvote
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
describe Issue, "Upvote" do
|
|
||||||
let(:issue) { create(:issue) }
|
|
||||||
|
|
||||||
it "with no notes has a 0/0 score" do
|
|
||||||
issue.upvotes.should == 0
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should recognize non-+1 notes" do
|
|
||||||
issue.notes << create(:note, note: "No +1 here")
|
|
||||||
issue.should have(1).note
|
|
||||||
issue.notes.first.upvote?.should be_false
|
|
||||||
issue.upvotes.should == 0
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should recognize a single +1 note" do
|
|
||||||
issue.notes << create(:note, note: "+1 This is awesome")
|
|
||||||
issue.upvotes.should == 1
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should recognize multiple +1 notes" do
|
|
||||||
issue.notes << create(:note, note: "+1 This is awesome")
|
|
||||||
issue.notes << create(:note, note: "+1 I want this")
|
|
||||||
issue.upvotes.should == 2
|
|
||||||
end
|
|
||||||
end
|
|
132
spec/roles/votes_spec.rb
Normal file
132
spec/roles/votes_spec.rb
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Issue do
|
||||||
|
let(:issue) { create(:issue) }
|
||||||
|
|
||||||
|
describe "#upvotes" do
|
||||||
|
it "with no notes has a 0/0 score" do
|
||||||
|
issue.upvotes.should == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should recognize non-+1 notes" do
|
||||||
|
issue.notes << create(:note, note: "No +1 here")
|
||||||
|
issue.should have(1).note
|
||||||
|
issue.notes.first.upvote?.should be_false
|
||||||
|
issue.upvotes.should == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should recognize a single +1 note" do
|
||||||
|
issue.notes << create(:note, note: "+1 This is awesome")
|
||||||
|
issue.upvotes.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should recognize multiple +1 notes" do
|
||||||
|
issue.notes << create(:note, note: "+1 This is awesome")
|
||||||
|
issue.notes << create(:note, note: "+1 I want this")
|
||||||
|
issue.upvotes.should == 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#downvotes" do
|
||||||
|
it "with no notes has a 0/0 score" do
|
||||||
|
issue.downvotes.should == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should recognize non--1 notes" do
|
||||||
|
issue.notes << create(:note, note: "Almost got a -1")
|
||||||
|
issue.should have(1).note
|
||||||
|
issue.notes.first.downvote?.should be_false
|
||||||
|
issue.downvotes.should == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should recognize a single -1 note" do
|
||||||
|
issue.notes << create(:note, note: "-1 This is bad")
|
||||||
|
issue.downvotes.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should recognize multiple -1 notes" do
|
||||||
|
issue.notes << create(:note, note: "-1 This is bad")
|
||||||
|
issue.notes << create(:note, note: "-1 Away with this")
|
||||||
|
issue.downvotes.should == 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#votes_count" do
|
||||||
|
it "with no notes has a 0/0 score" do
|
||||||
|
issue.votes_count.should == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should recognize non notes" do
|
||||||
|
issue.notes << create(:note, note: "No +1 here")
|
||||||
|
issue.should have(1).note
|
||||||
|
issue.votes_count.should == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should recognize a single +1 note" do
|
||||||
|
issue.notes << create(:note, note: "+1 This is awesome")
|
||||||
|
issue.votes_count.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should recognize a single -1 note" do
|
||||||
|
issue.notes << create(:note, note: "-1 This is bad")
|
||||||
|
issue.votes_count.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should recognize multiple notes" do
|
||||||
|
issue.notes << create(:note, note: "+1 This is awesome")
|
||||||
|
issue.notes << create(:note, note: "-1 This is bad")
|
||||||
|
issue.notes << create(:note, note: "+1 I want this")
|
||||||
|
issue.votes_count.should == 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#upvotes_in_percent" do
|
||||||
|
it "with no notes has a 0% score" do
|
||||||
|
issue.upvotes_in_percent.should == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should count a single 1 note as 100%" do
|
||||||
|
issue.notes << create(:note, note: "+1 This is awesome")
|
||||||
|
issue.upvotes_in_percent.should == 100
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should count multiple +1 notes as 100%" do
|
||||||
|
issue.notes << create(:note, note: "+1 This is awesome")
|
||||||
|
issue.notes << create(:note, note: "+1 I want this")
|
||||||
|
issue.upvotes_in_percent.should == 100
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should count fractions for multiple +1 and -1 notes correctly" do
|
||||||
|
issue.notes << create(:note, note: "+1 This is awesome")
|
||||||
|
issue.notes << create(:note, note: "+1 I want this")
|
||||||
|
issue.notes << create(:note, note: "-1 This is bad")
|
||||||
|
issue.notes << create(:note, note: "+1 me too")
|
||||||
|
issue.upvotes_in_percent.should == 75
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#downvotes_in_percent" do
|
||||||
|
it "with no notes has a 0% score" do
|
||||||
|
issue.downvotes_in_percent.should == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should count a single -1 note as 100%" do
|
||||||
|
issue.notes << create(:note, note: "-1 This is bad")
|
||||||
|
issue.downvotes_in_percent.should == 100
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should count multiple -1 notes as 100%" do
|
||||||
|
issue.notes << create(:note, note: "-1 This is bad")
|
||||||
|
issue.notes << create(:note, note: "-1 Away with this")
|
||||||
|
issue.downvotes_in_percent.should == 100
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should count fractions for multiple +1 and -1 notes correctly" do
|
||||||
|
issue.notes << create(:note, note: "+1 This is awesome")
|
||||||
|
issue.notes << create(:note, note: "+1 I want this")
|
||||||
|
issue.notes << create(:note, note: "-1 This is bad")
|
||||||
|
issue.notes << create(:note, note: "+1 me too")
|
||||||
|
issue.downvotes_in_percent.should == 25
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue