Implement vote results
This commit is contained in:
parent
0fb61f7a7f
commit
28071e1d7a
2
Gemfile
2
Gemfile
|
@ -54,7 +54,7 @@ gem 'jbuilder'
|
|||
|
||||
gem 'search_object'
|
||||
|
||||
gem 'her'
|
||||
gem 'faraday'
|
||||
|
||||
group :development do
|
||||
gem 'spring'
|
||||
|
|
|
@ -150,11 +150,6 @@ GEM
|
|||
guard (~> 2.1)
|
||||
guard-compat (~> 1.1)
|
||||
rspec (>= 2.99.0, < 4.0)
|
||||
her (0.8.2)
|
||||
activemodel (>= 3.0.0, <= 6.0.0)
|
||||
activesupport (>= 3.0.0, <= 6.0.0)
|
||||
faraday (>= 0.8, < 1.0)
|
||||
multi_json (~> 1.7)
|
||||
highline (1.7.8)
|
||||
hirb (0.7.3)
|
||||
http-cookie (1.0.3)
|
||||
|
@ -404,10 +399,10 @@ DEPENDENCIES
|
|||
devise-i18n
|
||||
factory_girl_rails
|
||||
faker
|
||||
faraday
|
||||
font-awesome-sass
|
||||
globalize
|
||||
guard-rspec
|
||||
her
|
||||
hirb
|
||||
i18n-tasks
|
||||
jbuilder
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
.huge {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.large {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,26 @@ module Management
|
|||
redirect_to management_root_path(current_conference: nil)
|
||||
end
|
||||
|
||||
def update_vote_data
|
||||
@conference = find_conference
|
||||
|
||||
begin
|
||||
if @conference.update_vote_data!
|
||||
flash[:notice] = t '.vote_data_successfully_updated'
|
||||
else
|
||||
flash[:alert] = t '.error_during_vote_data_save'
|
||||
end
|
||||
rescue StandardError => e
|
||||
flash[:alert] = t '.error_during_connection_with_voting_endpoint', error: e.message
|
||||
end
|
||||
|
||||
redirect_to :back
|
||||
end
|
||||
|
||||
def vote_results
|
||||
@conference = find_conference
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_conference
|
||||
|
@ -52,7 +72,8 @@ module Management
|
|||
|
||||
def conference_params
|
||||
params.require(:conference).permit(
|
||||
:title, :email, :start_date, :end_date, :description, :host_name, :planned_cfp_end_date,
|
||||
:title, :email, :start_date, :end_date, :description, :host_name,
|
||||
:planned_cfp_end_date, :vote_data_endpoint,
|
||||
event_types_attributes: [:id, :name, :description, :maximum_length,
|
||||
:minimum_length, :_destroy],
|
||||
tracks_attributes: [:id, :name, :color, :css_class, :description,
|
||||
|
|
|
@ -36,6 +36,14 @@ class Conference < ActiveRecord::Base
|
|||
submissions.group_by { |s| s.confirmed_at.to_date }
|
||||
end
|
||||
|
||||
def update_vote_data!
|
||||
ConferenceVoteSummary.new(conference: self).save
|
||||
end
|
||||
|
||||
def has_vote_results?
|
||||
vote_data_updated_at.present?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def planned_cfp_end_date_is_before_start_date
|
||||
|
@ -49,4 +57,50 @@ class Conference < ActiveRecord::Base
|
|||
errors.add(:end_date, :cannot_be_before_start_date)
|
||||
end
|
||||
end
|
||||
|
||||
class ConferenceVoteSummary
|
||||
include ActiveModel::Model
|
||||
|
||||
attr_accessor :conference
|
||||
|
||||
def number_of_ballots
|
||||
@number_of_ballots ||= remote_summary_data['number_of_ballots']
|
||||
end
|
||||
|
||||
def ranking
|
||||
@ranking ||= remote_summary_data['ranking'].map { |ranking_entry| EventRanking.new ranking_entry }
|
||||
end
|
||||
|
||||
def save
|
||||
conference.transaction do
|
||||
conference.update number_of_ballots_cast: number_of_ballots
|
||||
ranking.all?(&:save) or raise ActiveRecord::Rollback
|
||||
conference.touch :vote_data_updated_at
|
||||
conference.save or raise ActiveRecord::Rollback
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def conn
|
||||
@conn ||= Faraday.new(url: conference.vote_data_endpoint + '/summary.json',
|
||||
headers: {'Content-Type' => 'application/json'})
|
||||
end
|
||||
|
||||
def remote_summary_data
|
||||
@remote_summary_data ||= JSON.parse(conn.get do |request|
|
||||
request.body = {summary: {talk_ids: Conference.last.events.pluck(:id)}}.to_json
|
||||
end.body)
|
||||
end
|
||||
|
||||
class EventRanking
|
||||
include ActiveModel::Model
|
||||
|
||||
attr_accessor :talk_id, :votes, :place
|
||||
|
||||
def save
|
||||
Event.find(talk_id).update(number_of_votes: votes, rank: place)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -60,6 +60,14 @@ class Event < ActiveRecord::Base
|
|||
}
|
||||
end
|
||||
|
||||
def per_cent_of_votes
|
||||
if conference.has_vote_results? and conference.number_of_ballots_cast > 0
|
||||
Rational(number_of_votes * 100, conference.number_of_ballots_cast)
|
||||
else
|
||||
Float::NAN
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def track_belongs_to_the_selected_conference
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
= f.input :start_date, as: :date
|
||||
= f.input :end_date, as: :date
|
||||
= f.input :description
|
||||
= f.input :vote_data_endpoint
|
||||
hr
|
||||
.row
|
||||
.col-lg-12
|
||||
|
|
|
@ -85,3 +85,50 @@
|
|||
.row
|
||||
.col-lg-12
|
||||
.submissions-chart#submissions-chart data-submissions="#{submissions_chart_data(@conference).to_json}"
|
||||
- if @conference.vote_data_endpoint.present?
|
||||
hr
|
||||
.row
|
||||
.col-lg-12
|
||||
h2
|
||||
=> t '.voting_results'
|
||||
small
|
||||
- if @conference.vote_data_updated_at.present?
|
||||
= t '.vote_data_updated_at', updated_at: l(@conference.vote_data_updated_at, format: :long)
|
||||
.panel.panel-default
|
||||
table.table.table-striped.table-hover.record-table
|
||||
- if @conference.vote_data_updated_at.present?
|
||||
thead
|
||||
tr
|
||||
th.text-right = t('.rank')
|
||||
th.text-right = t('.percent')
|
||||
th = Event.model_name.human.mb_chars.capitalize
|
||||
th
|
||||
tbody
|
||||
- if @conference.vote_data_updated_at.present?
|
||||
- current_conference.events.order(rank: :asc).group_by(&:rank).to_a[0..9].each do |rank, events|
|
||||
- events.each.with_index do |event, index|
|
||||
tr
|
||||
- if index == 0
|
||||
td.text-right rowspan="#{events.count}"
|
||||
.large
|
||||
span.label.label-info = event.rank
|
||||
td.text-right rowspan="#{events.count}"
|
||||
span title="#{t('.vote_ratio', votes: event.number_of_votes, total_votes: @conference.number_of_ballots_cast)}"
|
||||
= number_to_percentage(event.per_cent_of_votes, strip_insignificant_zeros: true, precision: 2)
|
||||
td = event.title
|
||||
td.actions = action_buttons(@conference, event, [:show])
|
||||
- else
|
||||
tr
|
||||
td.text-center colspan="20"
|
||||
p.large
|
||||
= t '.vote_data_never_updated'
|
||||
p
|
||||
=< link_to update_vote_data_management_conference_path, method: :patch, class: ['btn', 'btn-primary'] do
|
||||
= icon :refresh, t('.fetch_vote_results')
|
||||
- if @conference.vote_data_updated_at.present?
|
||||
.panel-footer.text-right
|
||||
.btn-group
|
||||
= link_to vote_results_management_conference_path, class: ['btn', 'btn-info'] do
|
||||
= icon 'list-ol', t('.full_vote_results')
|
||||
= link_to update_vote_data_management_conference_path, method: :patch, class: ['btn', 'btn-primary'] do
|
||||
= icon :refresh, t('.fetch_vote_results')
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
- content_for :title
|
||||
= t '.vote_results'
|
||||
|
||||
.row
|
||||
.col-lg-12
|
||||
h1.page-header
|
||||
= t '.vote_results'
|
||||
- if @conference.vote_data_updated_at.present?
|
||||
small<
|
||||
= t '.vote_data_updated_at', updated_at: l(@conference.vote_data_updated_at, format: :long)
|
||||
- if @conference.vote_data_endpoint.present?
|
||||
.row
|
||||
.col-lg-12
|
||||
.panel.panel-default
|
||||
table.table.table-striped.table-hover.record-table
|
||||
- if @conference.vote_data_updated_at.present?
|
||||
thead
|
||||
tr
|
||||
th.text-right = t('.rank')
|
||||
th.text-right = t('.percent')
|
||||
th = Event.model_name.human.mb_chars.capitalize
|
||||
th
|
||||
tbody
|
||||
- if @conference.vote_data_updated_at.present?
|
||||
- current_conference.events.order(rank: :asc).group_by(&:rank).each do |rank, events|
|
||||
- events.each.with_index do |event, index|
|
||||
tr
|
||||
- if index == 0
|
||||
td.text-right rowspan="#{events.count}"
|
||||
.large
|
||||
span.label.label-info = event.rank
|
||||
td.text-right rowspan="#{events.count}"
|
||||
span title="#{t('.vote_ratio', votes: event.number_of_votes, total_votes: @conference.number_of_ballots_cast)}"
|
||||
= number_to_percentage(event.per_cent_of_votes, strip_insignificant_zeros: true, precision: 2)
|
||||
td = event.title
|
||||
td.actions = action_buttons(@conference, event, [:show])
|
||||
- else
|
||||
tr
|
||||
td.text-center colspan="20"
|
||||
p.large
|
||||
= t '.vote_data_never_updated'
|
||||
p
|
||||
=< link_to update_vote_data_management_conference_path, method: :patch, class: ['btn', 'btn-primary'] do
|
||||
= icon :refresh, t('.fetch_vote_results')
|
||||
- if @conference.vote_data_updated_at.present?
|
||||
.panel-footer.text-right
|
||||
.btn-group
|
||||
= link_to management_conference_path, class: ['btn', 'btn-info'] do
|
||||
= icon :users, t('.back_to', conference: @conference.title)
|
||||
= link_to update_vote_data_management_conference_path, method: :patch, class: ['btn', 'btn-primary'] do
|
||||
= icon :refresh, t('.fetch_vote_results')
|
|
@ -15,9 +15,31 @@ bg:
|
|||
contacts: "Данни за контакт"
|
||||
event_propositions: "Предложения за събития"
|
||||
conferences:
|
||||
update_vote_data:
|
||||
vote_data_successfully_updated: "Резултатите от гласуването бяха обновени успешно"
|
||||
error_during_vote_data_save: "Възникна грешка при запазването на резултатите от гласуването"
|
||||
error_during_connection_with_voting_endpoint: "Възникна грешка при опит за изтегляне на резултатите от гласуването: %{error}"
|
||||
vote_results:
|
||||
back_to: "Обратно към %{conference}"
|
||||
vote_results: "Резултати от гласуването"
|
||||
vote_data_never_updated: "Резулатите от гласуването не са изтеглени"
|
||||
voting_results: "Резултати от гласуването"
|
||||
vote_data_updated_at: "последно обновяване %{updated_at}"
|
||||
vote_ratio: "%{votes} от общо %{total_votes} гласа"
|
||||
fetch_vote_results: "Изтегли резултатите от гласуването"
|
||||
percent: "%"
|
||||
rank: "Позиция"
|
||||
show:
|
||||
full_vote_results: "Пълни резултати от гласуването"
|
||||
vote_data_never_updated: "Резулатите от гласуването не са изтеглени"
|
||||
voting_results: "Резултати от гласуването"
|
||||
vote_data_updated_at: "последно обновяване %{updated_at}"
|
||||
vote_ratio: "%{votes} от общо %{total_votes} гласа"
|
||||
summary: 'Обобщение'
|
||||
cfp_status: 'Състояние на CFP:'
|
||||
fetch_vote_results: "Изтегли резултатите от гласуването"
|
||||
percent: "%"
|
||||
rank: "Позиция"
|
||||
events:
|
||||
edit:
|
||||
edit: "Редакция на %{event_type} „%{event_title}“"
|
||||
|
|
|
@ -33,6 +33,11 @@ Rails.application.routes.draw do
|
|||
root to: 'conferences#index'
|
||||
|
||||
resources :conferences do
|
||||
member do
|
||||
patch :update_vote_data
|
||||
get :vote_results
|
||||
end
|
||||
|
||||
resources :events
|
||||
resources :volunteers
|
||||
resources :propositions
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
class AddVoteDataToConferences < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :conferences, :vote_data_endpoint, :string
|
||||
add_column :conferences, :number_of_ballots_cast, :integer
|
||||
add_column :conferences, :vote_data_updated_at, :datetime
|
||||
end
|
||||
end
|
|
@ -0,0 +1,6 @@
|
|||
class AddVoteDataToEvents < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :events, :number_of_votes, :integer
|
||||
add_column :events, :rank, :integer
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue