Fixes #voodoorai2000 comments

Fixes some comments from #voodoorai2000 for the PR to consul
This commit is contained in:
Juan Salvador Pérez García
2018-07-27 16:30:03 +02:00
parent a41ce7a0bd
commit 50f73abb0c
33 changed files with 541 additions and 404 deletions

View File

@@ -5,7 +5,7 @@
(function(){ (function(){
'use strict'; 'use strict';
var ProposalGraph = function(url) { let ProposalGraph = function(url) {
this.url = url; this.url = url;
this.successfulProposalDataUrl = null; this.successfulProposalDataUrl = null;
this.proposalAchievementsUrl = null; this.proposalAchievementsUrl = null;
@@ -15,12 +15,11 @@
this.progressLabel = 'Progress'; this.progressLabel = 'Progress';
this.supportsLabel = 'Supports'; this.supportsLabel = 'Supports';
this.successLabel = 'Success'; this.successLabel = 'Success';
this.chart = null;
this.goals = null; this.goals = null;
this.achievements = null; this.achievements = null;
this.xColumnValues = null; this.xColumnValues = null;
this.succesfulColumnValues = null;
this.progressColumnValues = null; this.progressColumnValues = null;
this.resourcesUrl = null;
}; };
ProposalGraph.prototype.refresh = function() { ProposalGraph.prototype.refresh = function() {
@@ -33,7 +32,7 @@
ProposalGraph.prototype.refreshGoals = function () { ProposalGraph.prototype.refreshGoals = function () {
return $.ajax({ return $.ajax({
url: '/dashboard/resources.json', url: this.resourcesUrl,
cache: false, cache: false,
success: function(data) { success: function(data) {
this.parseGoals(data); this.parseGoals(data);
@@ -42,7 +41,7 @@
}; };
ProposalGraph.prototype.parseGoals = function(data) { ProposalGraph.prototype.parseGoals = function(data) {
var i, l; let i, l;
this.goals = []; this.goals = [];
for (i = 0, l = data.length; i < l; i += 1) { for (i = 0, l = data.length; i < l; i += 1) {
@@ -67,7 +66,7 @@
}; };
ProposalGraph.prototype.parseData = function(data) { ProposalGraph.prototype.parseData = function(data) {
var key; let key;
this.xColumnValues = [ ]; this.xColumnValues = [ ];
this.progressColumnValues = [ this.progressLabel ]; this.progressColumnValues = [ this.progressLabel ];
@@ -94,7 +93,7 @@
}; };
ProposalGraph.prototype.parseSuccessfulProposalData = function(data) { ProposalGraph.prototype.parseSuccessfulProposalData = function(data) {
var key; let key;
this.successfulColumnValues = [ this.successLabel ]; this.successfulColumnValues = [ this.successLabel ];
@@ -120,7 +119,7 @@
}; };
ProposalGraph.prototype.parseAchievements = function(data) { ProposalGraph.prototype.parseAchievements = function(data) {
var group; let group;
this.achievements = []; this.achievements = [];
for (group in data) { for (group in data) {
@@ -128,7 +127,7 @@
this.addXColumnValue(group); this.addXColumnValue(group);
this.achievements.push({ this.achievements.push({
value: group, value: group,
text: data[group][data[group].length - 1].title text: data[group].title
}); });
} }
} }
@@ -138,12 +137,12 @@
if (this.xColumnValues.indexOf(value) === -1) { if (this.xColumnValues.indexOf(value) === -1) {
this.xColumnValues.push(value); this.xColumnValues.push(value);
} }
} };
ProposalGraph.prototype.draw = function(data) { ProposalGraph.prototype.draw = function() {
this.formatXColumnValues(); this.formatXColumnValues();
this.chart = c3.generate({ c3.generate({
bindto: '#' + this.targetId, bindto: '#' + this.targetId,
data: { data: {
x: 'x', x: 'x',
@@ -176,9 +175,6 @@
y: { y: {
lines: this.goals lines: this.goals
} }
//x: {
// lines: this.achievements
//}
}, },
legend: { legend: {
position: 'right' position: 'right'
@@ -187,7 +183,7 @@
}; };
ProposalGraph.prototype.formatXColumnValues = function () { ProposalGraph.prototype.formatXColumnValues = function () {
var i, l, parts; let i, l, parts;
this.xColumnValues = this.xColumnValues.sort(); this.xColumnValues = this.xColumnValues.sort();
@@ -199,7 +195,7 @@
} }
this.xColumnValues.unshift('x'); this.xColumnValues.unshift('x');
} };
ProposalGraph.prototype.isDailyGrouped = function() { ProposalGraph.prototype.isDailyGrouped = function() {
return this.groupBy === undefined || this.groupBy === '' || this.groupBy === null return this.groupBy === undefined || this.groupBy === '' || this.groupBy === null
@@ -207,7 +203,7 @@
$(document).ready(function () { $(document).ready(function () {
$('[data-proposal-graph-url]').each(function () { $('[data-proposal-graph-url]').each(function () {
var graph = new ProposalGraph($(this).data('proposal-graph-url')); let graph = new ProposalGraph($(this).data('proposal-graph-url'));
graph.successfulProposalDataUrl = $(this).data('successful-proposal-graph-url'); graph.successfulProposalDataUrl = $(this).data('successful-proposal-graph-url');
graph.proposalAchievementsUrl = $(this).data('proposal-achievements-url'); graph.proposalAchievementsUrl = $(this).data('proposal-achievements-url');
graph.targetId = $(this).attr('id'); graph.targetId = $(this).attr('id');
@@ -216,6 +212,7 @@
graph.supportsLabel = $(this).data('proposal-graph-supports-label'); graph.supportsLabel = $(this).data('proposal-graph-supports-label');
graph.successLabel = $(this).data('proposal-graph-success-label'); graph.successLabel = $(this).data('proposal-graph-success-label');
graph.proposalSuccess = parseInt($(this).data('proposal-success'), 10); graph.proposalSuccess = parseInt($(this).data('proposal-success'), 10);
graph.resourcesUrl = $(this).data('proposal-resources-url');
graph.refresh(); graph.refresh();
}); });

View File

@@ -0,0 +1,15 @@
module Dashboard::ExpectsDateRange
extend ActiveSupport::Concern
include Dashboard::HasProposal
def start_date
return Date.parse(params[:start_date]) unless params[:start_date].blank?
proposal.created_at.to_date
end
def end_date
return Date.parse(params[:end_date]) unless params[:end_date].blank?
Date.today
end
end

View File

@@ -0,0 +1,7 @@
module Dashboard::HasProposal
extend ActiveSupport::Concern
def proposal
@proposal ||= Proposal.includes(:community).find(params[:proposal_id])
end
end

View File

@@ -0,0 +1,44 @@
class Dashboard::AchievementsController < Dashboard::BaseController
include Dashboard::ExpectsDateRange
def index
authorize! :dashboard, proposal
render json: processed_groups
end
private
def processed_groups
grouped_results = groups
grouped_results.each do |key, results|
grouped_results[key] = {
executed_at: results.last.executed_at,
title: results.last.action.title
}
end
grouped_results
end
def groups
if params[:group_by] == 'week'
return executed_proposed_actions.group_by { |v| "#{v.executed_at.to_date.cweek}/#{v.executed_at.to_date.year}"}
end
if params[:group_by] == 'month'
return executed_proposed_actions.group_by { |v| "#{v.executed_at.to_date.year}-#{v.executed_at.to_date.month}"}
end
executed_proposed_actions.group_by { |a| a.executed_at.to_date }
end
def executed_proposed_actions
@executed_proposed_actions ||= Dashboard::ExecutedAction
.joins(:action)
.includes(:action)
.where(proposal: proposal)
.where(executed_at: start_date.beginning_of_day..end_date.end_of_day)
.where(dashboard_actions: { action_type: 0 })
.order(executed_at: :asc)
end
end

View File

@@ -1,6 +1,8 @@
class Dashboard::BaseController < ApplicationController class Dashboard::BaseController < ApplicationController
before_action :authenticate_user! before_action :authenticate_user!
include Dashboard::HasProposal
helper_method :proposal, :proposed_actions, :resource, :resources, :next_goal, :next_goal_supports, :next_goal_progress, :community_members_count helper_method :proposal, :proposed_actions, :resource, :resources, :next_goal, :next_goal_supports, :next_goal_progress, :community_members_count
respond_to :html respond_to :html
@@ -8,10 +10,6 @@ class Dashboard::BaseController < ApplicationController
private private
def proposal
@proposal ||= Proposal.includes(:community).find(params[:proposal_id])
end
def proposed_actions def proposed_actions
@proposed_actions ||= Dashboard::Action.proposed_actions.active_for(proposal).order(order: :asc) @proposed_actions ||= Dashboard::Action.proposed_actions.active_for(proposal).order(order: :asc)
end end

View File

@@ -0,0 +1,58 @@
class Dashboard::SuccessfulSupportsController < Dashboard::BaseController
include Dashboard::ExpectsDateRange
def index
authorize! :dashboard, proposal
render json: accumulated_grouped_supports
end
private
def accumulated_grouped_supports
grouped_votes = grouped_supports
grouped_votes.each do |group, votes|
grouped_votes[group] = votes.inject(0) { |sum, vote| sum + vote.vote_weight }
end
accumulated = 0
grouped_votes.each do |k, v|
accumulated += v
grouped_votes[k] = accumulated
end
grouped_votes
end
def grouped_supports
if params[:group_by] == 'week'
return supports.group_by { |v| "#{v.voted_at.to_date.cweek}/#{v.voted_at.to_date.year}" }
end
if params[:group_by] == 'month'
return supports.group_by { |v| "#{v.voted_at.to_date.year}-#{v.voted_at.to_date.month}" }
end
supports.group_by { |v| v.voted_at.to_date }
end
def supports
return [] if successful_proposal.nil?
Vote
.select("created_at + interval '#{days_diff} day' voted_at, *")
.where(votable: successful_proposal)
.where("created_at + interval '#{days_diff} day' between ? and ?", start_date.beginning_of_day, end_date.end_of_day)
.order(created_at: :asc)
end
def successful_proposal
@successful_proposal ||= Proposal.find_by(id: Setting['proposals.successful_proposal_id'])
end
def days_diff
return 0 if successful_proposal.nil?
return 0 if proposal.published_at.nil?
(proposal.published_at.to_date - successful_proposal.published_at.to_date).to_i
end
end

View File

@@ -0,0 +1,43 @@
class Dashboard::SupportsController < Dashboard::BaseController
include Dashboard::ExpectsDateRange
def index
authorize! :dashboard, proposal
render json: accumulated_supports
end
private
def accumulated_supports
grouped_votes = grouped_supports
grouped_votes.each do |group, votes|
grouped_votes[group] = votes.inject(0) { |sum, vote| sum + vote.vote_weight }
end
accumulated = 0
grouped_votes.each do |k, v|
accumulated += v
grouped_votes[k] = accumulated
end
grouped_votes
end
def grouped_supports
if params[:group_by] == 'week'
return supports.group_by { |v| "#{v.created_at.to_date.cweek}/#{v.created_at.to_date.year}" }
end
if params[:group_by] == 'month'
return supports.group_by { |v| "#{v.created_at.to_date.year}-#{v.created_at.to_date.month}" }
end
supports.group_by { |v| v.created_at.to_date }
end
def supports
@supports ||= Vote
.where(votable: proposal, created_at: start_date.beginning_of_day..end_date.end_of_day)
.order(created_at: :asc)
end
end

View File

@@ -37,7 +37,7 @@ class DashboardController < Dashboard::BaseController
if @dashboard_executed_action.save if @dashboard_executed_action.save
Dashboard::AdministratorTask.create(source: @dashboard_executed_action) Dashboard::AdministratorTask.create(source: @dashboard_executed_action)
redirect_to progress_proposal_dashboard_index_path(proposal.to_param), { flash: { info: t('.success') } } redirect_to progress_proposal_dashboard_index_path(proposal.to_param), { flash: { info: t('dashboard.create_request.success') } }
else else
flash.now[:alert] = @dashboard_executed_action.errors.full_messages.join('<br>') flash.now[:alert] = @dashboard_executed_action.errors.full_messages.join('<br>')
render :new_request render :new_request
@@ -52,21 +52,6 @@ class DashboardController < Dashboard::BaseController
authorize! :dashboard, proposal authorize! :dashboard, proposal
end end
def supports
authorize! :dashboard, proposal
render json: ProposalSupportsQuery.for(params)
end
def successful_supports
authorize! :dashboard, proposal
render json: SuccessfulProposalSupportsQuery.for(params)
end
def achievements
authorize! :dashboard, proposal
render json: ProposalAchievementsQuery.for(params)
end
private private
def dashboard_action def dashboard_action

View File

@@ -5,7 +5,7 @@ class Poll::Question < ActiveRecord::Base
acts_as_paranoid column: :hidden_at acts_as_paranoid column: :hidden_at
include ActsAsParanoidAliases include ActsAsParanoidAliases
belongs_to :poll, inverse_of: :questions belongs_to :poll
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id' belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
has_many :comments, as: :commentable has_many :comments, as: :commentable

View File

@@ -1,61 +0,0 @@
class ProposalAchievementsQuery
attr_reader :params
def self.for(params)
query = ProposalAchievementsQuery.new params
query.results
end
def initialize(params)
@params = params
end
def results
grouped_results = groups
grouped_results.each do |key, achievements|
grouped_results[key] = []
achievements.each do |achievement|
grouped_results[key] << {
executed_at: achievements.last.executed_at,
title: achievements.last.action.title
}
end
end
grouped_results
end
private
def groups
return achievements.group_by { |v| v.executed_at.to_date.year } if params[:group_by] == 'year'
return achievements.group_by { |v| "#{v.executed_at.to_date.cweek}/#{v.executed_at.to_date.year}" } if params[:group_by] == 'week'
return achievements.group_by { |v| "#{v.executed_at.to_date.year}-#{v.executed_at.to_date.month}" } if params[:group_by] == 'month'
achievements.group_by { |a| a.executed_at.to_date }
end
def achievements
Dashboard::ExecutedAction
.joins(:action)
.includes(:action)
.where(proposal: proposal, executed_at: start_date.beginning_of_day..end_date.end_of_day)
.where(dashboard_actions: { action_type: 0 })
.order(executed_at: :asc)
end
def proposal
@proposal ||= Proposal.find(params[:proposal_id])
end
def start_date
return Date.parse(params[:start_date]) unless params[:start_date].blank?
proposal.created_at.to_date
end
def end_date
return Date.parse(params[:end_date]) unless params[:end_date].blank?
Date.today
end
end

View File

@@ -1,54 +0,0 @@
class ProposalSupportsQuery
attr_reader :params
def self.for(params)
query = ProposalSupportsQuery.new params
query.results
end
def initialize(params)
@params = params
end
def results
grouped_votes = groups
grouped_votes.each do |group, votes|
grouped_votes[group] = votes.inject(0) { |sum, vote| sum + vote.vote_weight }
end
accumulated = 0
grouped_votes.each do |k, v|
accumulated += v
grouped_votes[k] = accumulated
end
grouped_votes
end
private
def groups
return votes.group_by { |v| v.created_at.to_date.year } if params[:group_by] == 'year'
return votes.group_by { |v| "#{v.created_at.to_date.cweek}/#{v.created_at.to_date.year}" } if params[:group_by] == 'week'
return votes.group_by { |v| "#{v.created_at.to_date.year}-#{v.created_at.to_date.month}" } if params[:group_by] == 'month'
votes.group_by { |v| v.created_at.to_date }
end
def votes
Vote.where(votable: proposal, created_at: start_date.beginning_of_day..end_date.end_of_day).order(created_at: :asc)
end
def proposal
@proposal ||= Proposal.find(params[:proposal_id])
end
def start_date
return Date.parse(params[:start_date]) unless params[:start_date].blank?
proposal.created_at.to_date
end
def end_date
return Date.parse(params[:end_date]) unless params[:end_date].blank?
Date.today
end
end

View File

@@ -1,72 +0,0 @@
class SuccessfulProposalSupportsQuery
attr_reader :params
def self.for(params)
query = SuccessfulProposalSupportsQuery.new params
query.results
end
def initialize(params)
@params = params
end
def results
grouped_votes = groups
grouped_votes.each do |group, votes|
grouped_votes[group] = votes.inject(0) { |sum, vote| sum + vote.vote_weight }
end
accumulated = 0
grouped_votes.each do |k, v|
accumulated += v
grouped_votes[k] = accumulated
end
grouped_votes
end
private
def groups
return votes.group_by { |v| v.voted_at.to_date.year } if params[:group_by] == 'year'
return votes.group_by { |v| "#{v.voted_at.to_date.cweek}/#{v.voted_at.to_date.year}" } if params[:group_by] == 'week'
return votes.group_by { |v| "#{v.voted_at.to_date.year}-#{v.voted_at.to_date.month}" } if params[:group_by] == 'month'
votes.group_by { |v| v.voted_at.to_date }
end
def votes
return [] if successful_proposal.nil?
Vote
.select("created_at + interval '#{days_diff} day' voted_at, *")
.where(votable: successful_proposal)
.where("created_at + interval '#{days_diff} day' between ? and ?", start_date.beginning_of_day, end_date.end_of_day)
.order(created_at: :asc)
end
def proposal
@proposal ||= Proposal.find(params[:proposal_id])
end
def successful_proposal
@successful_proposal ||= Proposal.find_by(id: Setting['proposals.successful_proposal_id'])
end
def days_diff
return 0 if successful_proposal.nil?
return 0 if proposal.published_at.nil?
(proposal.published_at.to_date - successful_proposal.published_at.to_date).to_i
end
def start_date
return Date.parse(params[:start_date]) unless params[:start_date].blank?
proposal.created_at.to_date
end
def end_date
return Date.parse(params[:end_date]) unless params[:end_date].blank?
Date.today
end
end

View File

@@ -251,4 +251,5 @@
</li> </li>
</ul> </ul>
</li> </li>
</ul>
</div> </div>

View File

@@ -18,14 +18,15 @@
<div class="small-12 column"> <div class="small-12 column">
<div id="proposal-graph" <div id="proposal-graph"
data-proposal-graph-url="<%= supports_proposal_dashboard_index_path(proposal, format: :json) %>" data-proposal-graph-url="<%= proposal_dashboard_supports_path(proposal, format: :json) %>"
data-successful-proposal-graph-url="<%= successful_supports_proposal_dashboard_index_path(proposal, format: :json) %>" data-successful-proposal-graph-url="<%= proposal_dashboard_successful_supports_path(proposal, format: :json) %>"
data-proposal-achievements-url="<%= achievements_proposal_dashboard_index_path(proposal, format: :json) %>" data-proposal-achievements-url="<%= proposal_dashboard_achievements_path(proposal, format: :json) %>"
data-proposal-graph-group-by="<%= params[:group_by] %>" data-proposal-graph-group-by="<%= params[:group_by] %>"
data-proposal-graph-progress-label="<%= t("dashboard.progress.progress") %>" data-proposal-graph-progress-label="<%= t("dashboard.progress.progress") %>"
data-proposal-graph-supports-label="<%= t("dashboard.progress.supports") %>" data-proposal-graph-supports-label="<%= t("dashboard.progress.supports") %>"
data-proposal-graph-success-label="<%= t("dashboard.progress.success") %>" data-proposal-graph-success-label="<%= t("dashboard.progress.success") %>"
data-proposal-success="<%= Setting["votes_for_proposal_success"] %>" data-proposal-success="<%= Setting["votes_for_proposal_success"] %>"
data-proposal-resources-url="<%= proposal_dashboard_resources_path(proposal, format: :json) %>"
class="c3 proposal-graph" class="c3 proposal-graph"
style="max-height: 320px; position: relative;"></div> style="max-height: 320px; position: relative;"></div>
</div> </div>

View File

@@ -2,7 +2,6 @@
<div class="row margin-top"> <div class="row margin-top">
<div class="small-12 column"> <div class="small-12 column">
<ul class="menu simple clear"> <ul class="menu simple clear">
<%# if current_user && current_user.administrator? || @poll.results_enabled? %>
<% if can?(:results, @poll) %> <% if can?(:results, @poll) %>
<% if controller_name == "polls" && action_name == "results" %> <% if controller_name == "polls" && action_name == "results" %>
<li class="is-active"> <li class="is-active">

View File

@@ -1,16 +1,9 @@
namespace :dashboard do
resources :resources, only: [:index]
end
resources :proposals do resources :proposals do
resources :dashboard, only: [:index] do resources :dashboard, only: [:index] do
collection do collection do
patch :publish patch :publish
get :supports
get :successful_supports
get :progress get :progress
get :community get :community
get :achievements
end end
member do member do
@@ -21,6 +14,10 @@ resources :proposals do
end end
namespace :dashboard do namespace :dashboard do
resources :resources, only: [:index]
resources :achievements, only: [:index]
resources :successful_supports, only: [:index]
resources :supports, only: [:index]
resources :polls, except: [:show, :destroy] resources :polls, except: [:show, :destroy]
resources :mailing, only: [:index, :new, :create] resources :mailing, only: [:index, :new, :create]
end end

View File

@@ -69,4 +69,11 @@ section "Creating Settings" do
Setting['feature.homepage.widgets.feeds.proposals'] = true Setting['feature.homepage.widgets.feeds.proposals'] = true
Setting['feature.homepage.widgets.feeds.debates'] = true Setting['feature.homepage.widgets.feeds.debates'] = true
Setting['feature.homepage.widgets.feeds.processes'] = true Setting['feature.homepage.widgets.feeds.processes'] = true
Setting['proposals.successful_proposal_id'] = nil
Setting['proposals.poll_short_title'] = nil
Setting['proposals.poll_description'] = nil
Setting['proposals.poll_link'] = nil
Setting['proposals.email_short_title'] = nil
Setting['proposals.email_description'] = nil
end end

View File

@@ -1,9 +1,5 @@
class AddPublishedAtToProposal < ActiveRecord::Migration class AddPublishedAtToProposal < ActiveRecord::Migration
def change def change
add_column :proposals, :published_at, :datetime, null: true add_column :proposals, :published_at, :datetime, null: true
Proposal.draft.find_each do |proposal|
proposal.update(published_at: proposal.created_at)
end
end end
end end

View File

@@ -0,0 +1,6 @@
class AddTimestampsToDashboardActions < ActiveRecord::Migration
def change
add_column :dashboard_actions, :created_at, :datetime
add_column :dashboard_actions, :updated_at, :datetime
end
end

View File

@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20180723112930) do ActiveRecord::Schema.define(version: 20180726055120) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@@ -318,6 +318,8 @@ ActiveRecord::Schema.define(version: 20180723112930) do
t.datetime "hidden_at" t.datetime "hidden_at"
t.integer "action_type", default: 0, null: false t.integer "action_type", default: 0, null: false
t.string "short_description" t.string "short_description"
t.datetime "created_at"
t.datetime "updated_at"
end end
create_table "dashboard_administrator_tasks", force: :cascade do |t| create_table "dashboard_administrator_tasks", force: :cascade do |t|

View File

@@ -27,6 +27,13 @@ namespace :proposal_actions do
end end
end end
desc 'Publish all proposals'
task publish_all: :environment do
Proposal.draft.find_each do |proposal|
proposal.update(published_at: proposal.created_at)
end
end
desc 'Simulate successful proposal' desc 'Simulate successful proposal'
task create_successful_proposal: :environment do task create_successful_proposal: :environment do
expected_supports = [ expected_supports = [

View File

@@ -1038,7 +1038,7 @@ LOREM_IPSUM
end end
factory :dashboard_action, class: 'Dashboard::Action' do factory :dashboard_action, class: 'Dashboard::Action' do
title { Faker::Lorem.sentence } title { Faker::Lorem.sentence[0..80] }
description { Faker::Lorem.sentence } description { Faker::Lorem.sentence }
link nil link nil
request_to_administrators true request_to_administrators true

View File

@@ -26,6 +26,14 @@ feature 'Polls' do
end end
end end
scenario "Proposal polls won't be listed" do
proposal = create(:proposal)
_poll = create(:poll, related: proposal)
visit polls_path
expect(page).to have_content('There are no open votings')
end
scenario 'Filtering polls' do scenario 'Filtering polls' do
create(:poll, name: "Current poll") create(:poll, name: "Current poll")
create(:poll, :incoming, name: "Incoming poll") create(:poll, :incoming, name: "Incoming poll")

View File

@@ -1029,12 +1029,12 @@ describe Budget::Investment do
investment.heading = heading2 investment.heading = heading2
investment.store_reclassified_votes("heading_changed") investment.store_reclassified_votes("heading_changed")
reclassified_vote = Budget::ReclassifiedVote.first
expect(Budget::ReclassifiedVote.count).to eq(3) expect(Budget::ReclassifiedVote.count).to eq(3)
Budget::ReclassifiedVote.find_each do |reclassified_vote| expect(reclassified_vote.investment_id).to eq(investment.id)
expect(reclassified_vote.investment_id).to eq(investment.id) expect(reclassified_vote.user_id).to eq(Budget::Ballot.first.user.id)
expect(reclassified_vote.reason).to eq("heading_changed") expect(reclassified_vote.reason).to eq("heading_changed")
expect(Budget::Ballot.where(user_id: reclassified_vote.user_id)).not_to be_empty
end
end end
end end

View File

@@ -18,118 +18,135 @@ describe Dashboard::Action do
let(:request_to_administrators) { true } let(:request_to_administrators) { true }
let(:action_type) { 'resource' } let(:action_type) { 'resource' }
it { should be_valid } it 'is invalid when title is blank' do
action = build(:dashboard_action, title: '')
context 'when validating title' do expect(action).not_to be_valid
context 'and title is blank' do
let(:title) { nil }
it { should_not be_valid }
end
context 'and title is very short' do
let(:title) { 'abc' }
it { should_not be_valid }
end
context 'and title is very long' do
let(:title) { 'a' * 81 }
it { should_not be_valid }
end
end end
context 'when validating day_offset' do it 'is invalid when title is too short' do
context 'and day_offset is nil' do action = build(:dashboard_action, title: 'abc')
let(:day_offset) { nil } expect(action).not_to be_valid
it { should_not be_valid }
end
context 'and day_offset is negative' do
let(:day_offset) { -1 }
it { should_not be_valid }
end
context 'and day_offset is not an integer' do
let(:day_offset) { 1.23 }
it { should_not be_valid }
end
end end
context 'when validating required_supports' do it 'is invalid when title is too long' do
context 'and required_supports is nil' do action = build(:dashboard_action, title: 'a' * 81)
let(:required_supports) { nil } expect(action).not_to be_valid
it { should_not be_valid }
end
context 'and required_supports is negative' do
let(:required_supports) { -1 }
it { should_not be_valid }
end
context 'and required_supports is not an integer' do
let(:required_supports) { 1.23 }
it { should_not be_valid }
end
end end
context 'when action type is nil' do it 'is invalid when day_offset is not defined' do
let(:action_type) { nil } action = build(:dashboard_action, day_offset: nil)
expect(action).not_to be_valid
it { should_not be_valid } end
it 'is invalid when day_offset is negative' do
action = build(:dashboard_action, day_offset: -1)
expect(action).not_to be_valid
end
it 'is invalid when day_offset not an integer' do
action = build(:dashboard_action, day_offset: 1.23)
expect(action).not_to be_valid
end
it 'is invalid when required_supports is nil' do
action = build(:dashboard_action, required_supports: nil)
expect(action).not_to be_valid
end
it 'is invalid when required_supports is negative' do
action = build(:dashboard_action, required_supports: -1)
expect(action).not_to be_valid
end
it 'is invalid when required_supports is not an integer' do
action = build(:dashboard_action, required_supports: 1.23)
expect(action).not_to be_valid
end
it 'is invalid when action_type is nil' do
action = build(:dashboard_action, action_type: nil)
expect(action).not_to be_valid
end end
context 'active_for?' do context 'active_for?' do
let(:proposal) { create(:proposal, published_at: published_at, cached_votes_up: cached_votes_up) } it 'is active when required supports is 0 and day_offset is 0' do
let(:published_at) { Time.current } action = build(:dashboard_action, required_supports: 0, day_offset: 0)
let(:cached_votes_up) { Proposal.votes_needed_for_success + 100 } proposal = build(:proposal)
it { should be_active_for(proposal) }
context 'and not enough supports' do
let(:required_supports) { cached_votes_up + 100 }
it { should_not be_active_for(proposal) } expect(action).to be_active_for(proposal)
end end
context 'and not passed enough time since publication' do it 'is active when published after day_offset' do
let(:day_offset) { 10 } action = build(:dashboard_action, required_supports: 0, day_offset: 10)
proposal = build(:proposal, published_at: Time.current - 10.days)
expect(action).to be_active_for(proposal)
end
it { should_not be_active_for(proposal) } it 'is active when have enough supports' do
action = build(:dashboard_action, required_supports: 10, day_offset: 0)
proposal = build(:proposal, cached_votes_up: 10)
expect(action).to be_active_for(proposal)
end
it 'is not active when not enough time published' do
action = build(:dashboard_action, required_supports: 0, day_offset: 10)
proposal = build(:proposal, published_at: Time.current - 9.days)
expect(action).not_to be_active_for(proposal)
end
it 'is not active when not enough supports' do
action = build(:dashboard_action, required_supports: 10, day_offset: 0)
proposal = build(:proposal, cached_votes_up: 9)
expect(action).not_to be_active_for(proposal)
end end
end end
context 'executed_for? and requested_for?' do context 'requested_for?' do
let(:proposal) { create(:proposal) } it 'is not requested when no administrator task' do
subject { create(:dashboard_action, :active, :admin_request, :resource) } proposal = create(:proposal)
action = create(:dashboard_action, :active, :admin_request, :resource)
it { should_not be_requested_for(proposal) } expect(action).not_to be_requested_for(proposal)
it { should_not be_executed_for(proposal) } end
context 'and executed action' do it 'is requested when administrator task' do
let(:executed_action) { create(:dashboard_executed_action, proposal: proposal, action: subject) } proposal = create(:proposal)
action = create(:dashboard_action, :active, :admin_request, :resource)
executed_action = create(:dashboard_executed_action, proposal: proposal, action: action)
_task = create(:dashboard_administrator_task, :pending, source: executed_action)
context 'and pending administrator task' do expect(action).to be_requested_for(proposal)
let!(:task) { create(:dashboard_administrator_task, :pending, source: executed_action) } end
end
it { should be_requested_for(proposal) } context 'executed_for?' do
it { should_not be_executed_for(proposal) } it 'is not executed when no administrator task' do
end proposal = create(:proposal)
action = create(:dashboard_action, :active, :admin_request, :resource)
context 'and solved administrator task' do expect(action).not_to be_executed_for(proposal)
let!(:task) { create(:dashboard_administrator_task, :done, source: executed_action) } end
it { should be_requested_for(proposal) } it 'is not executed when pending administrator task' do
it { should be_executed_for(proposal) } proposal = create(:proposal)
end action = create(:dashboard_action, :active, :admin_request, :resource)
executed_action = create(:dashboard_executed_action, proposal: proposal, action: action)
_task = create(:dashboard_administrator_task, :pending, source: executed_action)
expect(action).not_to be_executed_for(proposal)
end
it 'is executed when done administrator task' do
proposal = create(:proposal)
action = create(:dashboard_action, :active, :admin_request, :resource)
executed_action = create(:dashboard_executed_action, proposal: proposal, action: action)
_task = create(:dashboard_administrator_task, :done, source: executed_action)
expect(action).to be_executed_for(proposal)
end end
end end
end end

View File

@@ -1,15 +1,8 @@
require 'rails_helper' require 'rails_helper'
describe Dashboard::AdministratorTask do describe Dashboard::AdministratorTask do
subject { build :dashboard_administrator_task, source: executed_action } it 'is invalid when source is nil' do
let(:executed_action) { build :dashboard_executed_action } task = build(:dashboard_administrator_task, source: nil)
expect(task).not_to be_valid
it { should be_valid }
context 'when source is nil' do
let(:executed_action) { nil }
it { should_not be_valid }
end end
end end

View File

@@ -1,49 +1,29 @@
require 'rails_helper' require 'rails_helper'
describe Dashboard::ExecutedAction do describe Dashboard::ExecutedAction do
subject do
build :dashboard_executed_action,
proposal: proposal,
action: action,
executed_at: executed_at
end
let(:proposal) { create :proposal } let(:proposal) { create :proposal }
let(:action) do let(:action) do
create :dashboard_action, request_to_administrators: request_to_administrators, link: Faker::Internet.url create :dashboard_action, request_to_administrators: true, link: Faker::Internet.url
end
let(:request_to_administrators) { false }
let(:executed_at) { Time.current }
it { should be_valid }
context 'when proposal is nil' do
let(:proposal) { nil }
it { should_not be_valid }
end end
context 'when action is nil' do it 'is invalid when proposal is nil' do
let(:action) { nil } action = build(:dashboard_executed_action, proposal: nil)
expect(action).not_to be_valid
it { should_not be_valid }
end end
context 'when executed_at is nil' do it 'is invalid when action is nil' do
let(:executed_at) { nil } action = build(:dashboard_executed_action, action: nil)
expect(action).not_to be_valid
it { should_not be_valid }
end end
context 'when the action sends a request to the administrators' do it 'is invalid when executed_at is nil' do
let(:request_to_administrators) { true } action = build(:dashboard_executed_action, executed_at: nil)
expect(action).not_to be_valid
it { should be_valid }
end end
context 'when it has been already executed' do it 'when action has been already executed it is invalid' do
let!(:executed) { create(:dashboard_executed_action, proposal: proposal, action: action) } _executed = create(:dashboard_executed_action, proposal: proposal, action: action)
action = build(:dashboard_executed_action, proposal: proposal, action: action)
it { should_not be_valid } expect(action).not_to be_valid
end end
end end

View File

@@ -1,29 +1,15 @@
require 'rails_helper' require 'rails_helper'
describe Link do describe Link do
subject do let(:action) { build :dashboard_action }
build :link,
linkable: dashboard_action, it 'is invalid when label is blank' do
label: label, link = build(:link, linkable: action, label: '')
url: url, expect(link).not_to be_valid
open_in_new_tab: true
end end
let(:dashboard_action) { build :dashboard_action } it 'is invalid when url is blank' do
let(:label) { Faker::Lorem.sentence } link = build(:link, linkable: action, url: '')
let(:url) { Faker::Internet.url } expect(link).not_to be_valid
it { should be_valid }
context 'when label is blank' do
let(:label) { '' }
it { should_not be_valid }
end
context 'when url is blank' do
let(:url) { '' }
it { should_not be_valid }
end end
end end

View File

@@ -0,0 +1,51 @@
require 'rails_helper'
describe "Retrieves achievements for a proposal" do
let(:created_at) { DateTime.parse("2018-01-01 12:00:00") }
let(:proposal) { create(:proposal, created_at: created_at) }
let(:executed_actions) { create_list(:dashboard_action, 8, :active, :proposed_action) }
let!(:non_executed_actions) { create_list(:dashboard_action, 8, :active, :proposed_action) }
before do
sign_in(proposal.author)
executed_actions.each_with_index do |action, index|
create(:dashboard_executed_action, proposal: proposal, action: action, executed_at: proposal.created_at + index.days)
end
end
it "returns a list of most recent executed achievements grouped by day" do
get proposal_dashboard_achievements_path(proposal, format: :json)
json = JSON.parse(response.body, symbolize_names: true)
expect(response).to have_http_status(200)
expect(json.length).to eq(8)
executed_actions.each do |action|
expect(json.values.select { |a| a[:title] == action.title }).not_to be_empty
end
non_executed_actions.each do |action|
expect(json.values.select { |a| a[:title] == action.title }).to be_empty
end
end
it "returns a list of most recent executed achievements grouped by week" do
get proposal_dashboard_achievements_path(proposal, group_by: 'week', format: :json)
json = JSON.parse(response.body, symbolize_names: true)
expect(response).to have_http_status(200)
expect(json.length).to eq(2)
end
it "returns a list of most recent executed achievements grouped by month" do
get proposal_dashboard_achievements_path(proposal, group_by: 'month', format: :json)
json = JSON.parse(response.body, symbolize_names: true)
expect(response).to have_http_status(200)
expect(json.length).to eq(1)
end
end

View File

@@ -0,0 +1,58 @@
require 'rails_helper'
describe "Retrieves number of supports for the successful proposal" do
let(:created_at) { DateTime.parse("2018-01-01 12:00:00") }
let(:proposal) { create(:proposal, created_at: created_at) }
before do
@successful_proposal_id = Setting['proposals.successful_proposal_id']
Setting['proposals.successful_proposal_id'] = proposal.id
8.times do |i|
user = create(:user, :verified)
Vote.create!(
votable: proposal,
voter: user,
vote_weight: 1,
created_at: proposal.created_at + i.days,
updated_at: proposal.created_at + i.days
)
end
sign_in(proposal.author)
end
after do
Setting['proposals.successful_proposal_id'] = @successful_proposal_id
end
it "returns the number of supports grouped by day" do
get proposal_dashboard_successful_supports_path(proposal, format: :json)
json = JSON.parse(response.body, symbolize_names: true)
expect(response).to have_http_status(200)
expect(json.length).to eq(8)
expect(json.values.last).to eq(8)
end
it "returns the number of supports grouped by week" do
get proposal_dashboard_successful_supports_path(proposal, group_by: 'week', format: :json)
json = JSON.parse(response.body, symbolize_names: true)
expect(response).to have_http_status(200)
expect(json.length).to eq(2)
expect(json.values.last).to eq(8)
end
it "returns the number of supports grouped by month" do
get proposal_dashboard_successful_supports_path(proposal, group_by: 'month', format: :json)
json = JSON.parse(response.body, symbolize_names: true)
expect(response).to have_http_status(200)
expect(json.length).to eq(1)
expect(json.values.last).to eq(8)
end
end

View File

@@ -0,0 +1,45 @@
require 'rails_helper'
describe "Retrieves number of supports for a proposal" do
let(:created_at) { DateTime.parse("2018-01-01 12:00:00") }
let(:proposal) { create(:proposal, created_at: created_at) }
before do
8.times do |i|
user = create(:user, :verified)
Vote.create!(votable: proposal, voter: user, vote_weight: 1, created_at: proposal.created_at + i.days, updated_at: proposal.created_at + i.days)
end
sign_in(proposal.author)
end
it "returns the number of supports grouped by day" do
get proposal_dashboard_supports_path(proposal, format: :json)
json = JSON.parse(response.body, symbolize_names: true)
expect(response).to have_http_status(200)
expect(json.length).to eq(8)
expect(json.values.last).to eq(8)
end
it "returns the number of supports grouped by week" do
get proposal_dashboard_supports_path(proposal, group_by: 'week', format: :json)
json = JSON.parse(response.body, symbolize_names: true)
expect(response).to have_http_status(200)
expect(json.length).to eq(2)
expect(json.values.last).to eq(8)
end
it "returns the number of supports grouped by month" do
get proposal_dashboard_supports_path(proposal, group_by: 'month', format: :json)
json = JSON.parse(response.body, symbolize_names: true)
expect(response).to have_http_status(200)
expect(json.length).to eq(1)
expect(json.values.last).to eq(8)
end
end

View File

@@ -0,0 +1,22 @@
module RequestSpecHelper
include Warden::Test::Helpers
def self.included(base)
base.before(:each) { Warden.test_mode! }
base.after(:each) { Warden.test_reset! }
end
def sign_in(resource)
login_as(resource, scope: warden_scope(resource))
end
def sign_out(resource)
logout(warden_scope(resource))
end
private
def warden_scope(resource)
resource.class.name.underscore.to_sym
end
end

View File

@@ -14,6 +14,7 @@ RSpec.configure do |config|
config.filter_run :focus config.filter_run :focus
config.run_all_when_everything_filtered = true config.run_all_when_everything_filtered = true
config.include Devise::TestHelpers, type: :controller config.include Devise::TestHelpers, type: :controller
config.include RequestSpecHelper, type: :request
config.include FactoryBot::Syntax::Methods config.include FactoryBot::Syntax::Methods
config.include(EmailSpec::Helpers) config.include(EmailSpec::Helpers)
config.include(EmailSpec::Matchers) config.include(EmailSpec::Matchers)