diff --git a/app/controllers/admin/poll/booth_assignments_controller.rb b/app/controllers/admin/poll/booth_assignments_controller.rb index 7998f29a2..5b9ea9d7b 100644 --- a/app/controllers/admin/poll/booth_assignments_controller.rb +++ b/app/controllers/admin/poll/booth_assignments_controller.rb @@ -23,18 +23,24 @@ class Admin::Poll::BoothAssignmentsController < Admin::Poll::BaseController end def create - @booth_assignment = ::Poll::BoothAssignment.new(poll_id: booth_assignment_params[:poll_id], - booth_id: booth_assignment_params[:booth_id]) + @poll = Poll.find(booth_assignment_params[:poll_id]) + @booth = Poll::Booth.find(booth_assignment_params[:booth_id]) + @booth_assignment = ::Poll::BoothAssignment.new(poll: @poll, + booth: @booth) if @booth_assignment.save notice = t("admin.poll_booth_assignments.flash.create") else notice = t("admin.poll_booth_assignments.flash.error_create") end - redirect_to admin_poll_booth_assignments_path(@booth_assignment.poll_id), notice: notice + respond_to do |format| + format.js { render layout: false } + end end def destroy + @poll = Poll.find(booth_assignment_params[:poll_id]) + @booth = Poll::Booth.find(booth_assignment_params[:booth_id]) @booth_assignment = ::Poll::BoothAssignment.find(params[:id]) if @booth_assignment.destroy @@ -42,7 +48,14 @@ class Admin::Poll::BoothAssignmentsController < Admin::Poll::BaseController else notice = t("admin.poll_booth_assignments.flash.error_destroy") end - redirect_to admin_poll_booth_assignments_path(@booth_assignment.poll_id), notice: notice + respond_to do |format| + format.js { render layout: false } + end + end + + def manage + @booths = ::Poll::Booth.all + @poll = Poll.find(params[:poll_id]) end private diff --git a/app/controllers/admin/poll/polls_controller.rb b/app/controllers/admin/poll/polls_controller.rb index 5103275fb..90a31192c 100644 --- a/app/controllers/admin/poll/polls_controller.rb +++ b/app/controllers/admin/poll/polls_controller.rb @@ -47,6 +47,10 @@ class Admin::Poll::PollsController < Admin::Poll::BaseController redirect_to admin_poll_path(@poll), notice: notice end + def booth_assignments + @polls = Poll.current_or_incoming + end + private def load_geozones @@ -54,9 +58,10 @@ class Admin::Poll::PollsController < Admin::Poll::BaseController end def poll_params - params.require(:poll).permit(:name, :starts_at, :ends_at, :geozone_restricted, :summary, :description, - geozone_ids: [], - image_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy]) + params.require(:poll).permit(:name, :starts_at, :ends_at, :geozone_restricted, + :summary, :description, :results_enabled, :stats_enabled, + geozone_ids: [], + image_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy]) end def search_params diff --git a/app/controllers/officing/base_controller.rb b/app/controllers/officing/base_controller.rb index 97ef23d30..07cf4cfa5 100644 --- a/app/controllers/officing/base_controller.rb +++ b/app/controllers/officing/base_controller.rb @@ -7,6 +7,6 @@ class Officing::BaseController < ApplicationController skip_authorization_check def verify_officer - raise CanCan::AccessDenied unless current_user.try(:poll_officer?) || current_user.try(:administrator?) + raise CanCan::AccessDenied unless current_user.try(:poll_officer?) end end \ No newline at end of file diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 111a80267..da340bb46 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -52,12 +52,8 @@ module UsersHelper current_user && current_user.manager? end - def current_poll_officer? - current_user && current_user.poll_officer? - end - def show_admin_menu? - current_administrator? || current_moderator? || current_valuator? || current_manager? || current_poll_officer? + current_administrator? || current_moderator? || current_valuator? || current_manager? end def interests_title_text(user) diff --git a/app/models/abilities/administrator.rb b/app/models/abilities/administrator.rb index 1bc8d5e0c..704f9be24 100644 --- a/app/models/abilities/administrator.rb +++ b/app/models/abilities/administrator.rb @@ -56,10 +56,10 @@ module Abilities can [:index, :create, :edit, :update, :destroy], Geozone - can [:read, :create, :update, :destroy, :add_question, :search_booths, :search_officers], Poll + can [:read, :create, :update, :destroy, :add_question, :search_booths, :search_officers, :booth_assignments], Poll can [:read, :create, :update, :destroy, :available], Poll::Booth can [:search, :create, :index, :destroy], ::Poll::Officer - can [:create, :destroy], ::Poll::BoothAssignment + can [:create, :destroy, :manage], ::Poll::BoothAssignment can [:create, :destroy], ::Poll::OfficerAssignment can [:read, :create, :update], Poll::Question can :destroy, Poll::Question # , comments_count: 0, votes_up: 0 diff --git a/app/models/poll/booth.rb b/app/models/poll/booth.rb index 04f9d3dea..07e7d2456 100644 --- a/app/models/poll/booth.rb +++ b/app/models/poll/booth.rb @@ -15,5 +15,8 @@ class Poll where(polls: { id: Poll.current_or_incoming }).includes(:polls) end + def assignment_on_poll(poll) + booth_assignments.where(poll: poll).first + end end -end \ No newline at end of file +end diff --git a/app/views/admin/_menu.html.erb b/app/views/admin/_menu.html.erb index f2af2c116..f3abf04aa 100644 --- a/app/views/admin/_menu.html.erb +++ b/app/views/admin/_menu.html.erb @@ -60,7 +60,8 @@ <%= t("admin.menu.title_polls") %> <% end %> +<% if current_user && current_user.poll_officer? %> +
  • + <%= link_to t("layouts.header.officing"), officing_root_path %> +
  • +<% end %> diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index 38abe6f56..2e5e38dc2 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -409,6 +409,7 @@ en: poll_officers: Poll officers polls: Polls poll_booths: Booths location + poll_booth_assignments: Booths Assignments poll_shifts: Manage shifts officials: Officials organizations: Organisations @@ -524,6 +525,17 @@ en: date_missing: "A date must be selected" vote_collection: Collect Votes recount_scrutiny: Recount & Scrutiny + booth_assignments: + manage_assignments: Manage assignments + manage: + assignments_list: "Assignments for poll '%{poll}'" + status: + assign_status: Assignment + assigned: Assigned + unassigned: Unassigned + actions: + assign: Assign booth + unassign: Unassign booth poll_booth_assignments: flash: destroy: "Booth not assigned anymore" @@ -547,9 +559,6 @@ en: no_booths: "There are no booths assigned to this poll." table_name: "Name" table_location: "Location" - table_assignment: "Assignment" - remove_booth: "Remove booth from poll" - add_booth: "Assign booth" polls: index: title: "List of polls" @@ -560,6 +569,10 @@ en: geozone_restricted: "Restricted to districts" new: title: "New poll" + show_results_and_stats: "Show results and stats" + show_results: "Show results" + show_stats: "Show stats" + results_and_stats_reminder: "Marking these checkboxes the results and/or stats of this poll will be publicly available and every user will see them." submit_button: "Create poll" edit: title: "Edit poll" @@ -666,6 +679,7 @@ en: add_booth: "Add booth" name: "Name" location: "Location" + no_location: "No Location" new: title: "New booth" name: "Name" diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 3336cdc5b..06ffa6c52 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -420,6 +420,7 @@ es: poll_officers: Presidentes de mesa polls: Votaciones poll_booths: Ubicación de urnas + poll_booth_assignments: Asignación de urnas poll_shifts: Asignar turnos officials: Cargos públicos organizations: Organizaciones @@ -524,6 +525,17 @@ es: date_missing: "Debe seleccionarse una fecha" vote_collection: Recoger Votos recount_scrutiny: Recuento & Escrutinio + booth_assignments: + manage_assignments: Gestionar asignaciones + manage: + assignments_list: "Asignaciones para la votación '%{poll}'" + status: + assign_status: Asignación + assigned: Asignada + unassigned: No asignada + actions: + assign: Assign booth + unassign: Unassign booth poll_booth_assignments: flash: destroy: "Urna desasignada" @@ -547,9 +559,6 @@ es: no_booths: "No hay urnas asignadas a esta votación." table_name: "Nombre" table_location: "Ubicación" - table_assignment: "Asignación" - remove_booth: "Desasignar urna" - add_booth: "Asignar urna" polls: index: title: "Listado de votaciones" @@ -560,6 +569,10 @@ es: geozone_restricted: "Restringida a los distritos" new: title: "Nueva votación" + show_results_and_stats: "Mostrar resultados y estadísticas" + show_results: "Mostrar resultados" + show_stats: "Mostrar estadísticas" + results_and_stats_reminder: "Si marcas estas casillas los resultados y/o estadísticas de esta votación serán públicos y podrán verlos todos los usuarios." submit_button: "Crear votación" edit: title: "Editar votación" @@ -668,6 +681,7 @@ es: add_booth: "Añadir urna" name: "Nombre" location: "Ubicación" + no_location: "Sin Ubicación" new: title: "Nueva urna" name: "Nombre" diff --git a/config/routes.rb b/config/routes.rb index 38858b813..fe38eb9b4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -275,10 +275,12 @@ Rails.application.routes.draw do scope module: :poll do resources :polls do + get :booth_assignments, on: :collection patch :add_question, on: :member resources :booth_assignments, only: [:index, :show, :create, :destroy] do get :search_booths, on: :collection + get :manage, on: :collection end resources :officer_assignments, only: [:index, :create, :destroy] do diff --git a/db/migrate/20171020163240_add_results_and_stats_to_polls.rb b/db/migrate/20171020163240_add_results_and_stats_to_polls.rb new file mode 100644 index 000000000..af6f82aaa --- /dev/null +++ b/db/migrate/20171020163240_add_results_and_stats_to_polls.rb @@ -0,0 +1,6 @@ +class AddResultsAndStatsToPolls < ActiveRecord::Migration + def change + add_column :polls, :results_enabled, :boolean, default: false + add_column :polls, :stats_enabled, :boolean, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index b2c99b9be..7f3177760 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20171019095042) do +ActiveRecord::Schema.define(version: 20171020163240) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -486,10 +486,6 @@ ActiveRecord::Schema.define(version: 20171019095042) do t.boolean "draft_publication_enabled", default: false t.boolean "result_publication_enabled", default: false t.boolean "published", default: true - t.date "proposals_phase_start_date" - t.date "proposals_phase_end_date" - t.boolean "proposals_phase_enabled" - t.text "proposals_description" end add_index "legislation_processes", ["allegations_end_date"], name: "index_legislation_processes_on_allegations_end_date", using: :btree @@ -502,36 +498,6 @@ ActiveRecord::Schema.define(version: 20171019095042) do add_index "legislation_processes", ["result_publication_date"], name: "index_legislation_processes_on_result_publication_date", using: :btree add_index "legislation_processes", ["start_date"], name: "index_legislation_processes_on_start_date", using: :btree - create_table "legislation_proposals", force: :cascade do |t| - t.integer "legislation_process_id" - t.string "title", limit: 80 - t.text "description" - t.string "question" - t.string "external_url" - t.integer "author_id" - t.datetime "hidden_at" - t.integer "flags_count", default: 0 - t.datetime "ignored_flag_at" - t.integer "cached_votes_up", default: 0 - t.integer "comments_count", default: 0 - t.datetime "confirmed_hide_at" - t.integer "hot_score", limit: 8, default: 0 - t.integer "confidence_score", default: 0 - t.string "responsible_name", limit: 60 - t.text "summary" - t.string "video_url" - t.tsvector "tsv" - t.integer "geozone_id" - t.datetime "retired_at" - t.string "retired_reason" - t.text "retired_explanation" - t.integer "community_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - end - - add_index "legislation_proposals", ["legislation_process_id"], name: "index_legislation_proposals_on_legislation_process_id", using: :btree - create_table "legislation_question_options", force: :cascade do |t| t.integer "legislation_question_id" t.string "value" @@ -794,6 +760,8 @@ ActiveRecord::Schema.define(version: 20171019095042) do t.integer "comments_count", default: 0 t.integer "author_id" t.datetime "hidden_at" + t.boolean "results_enabled", default: false + t.boolean "stats_enabled", default: false end add_index "polls", ["starts_at", "ends_at"], name: "index_polls_on_starts_at_and_ends_at", using: :btree @@ -953,20 +921,16 @@ ActiveRecord::Schema.define(version: 20171019095042) do add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree create_table "tags", force: :cascade do |t| - t.string "name", limit: 40 - t.integer "taggings_count", default: 0 - t.integer "debates_count", default: 0 - t.integer "proposals_count", default: 0 - t.integer "spending_proposals_count", default: 0 + t.string "name", limit: 40 + t.integer "taggings_count", default: 0 + t.integer "debates_count", default: 0 + t.integer "proposals_count", default: 0 + t.integer "spending_proposals_count", default: 0 t.string "kind" - t.integer "budget/investments_count", default: 0 - t.integer "legislation/proposals_count", default: 0 - t.integer "legislation/processes_count", default: 0 + t.integer "budget/investments_count", default: 0 end add_index "tags", ["debates_count"], name: "index_tags_on_debates_count", using: :btree - add_index "tags", ["legislation/processes_count"], name: "index_tags_on_legislation/processes_count", using: :btree - add_index "tags", ["legislation/proposals_count"], name: "index_tags_on_legislation/proposals_count", using: :btree add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree add_index "tags", ["proposals_count"], name: "index_tags_on_proposals_count", using: :btree add_index "tags", ["spending_proposals_count"], name: "index_tags_on_spending_proposals_count", using: :btree @@ -1144,7 +1108,6 @@ ActiveRecord::Schema.define(version: 20171019095042) do add_foreign_key "identities", "users" add_foreign_key "images", "users" add_foreign_key "legislation_draft_versions", "legislation_processes" - add_foreign_key "legislation_proposals", "legislation_processes" add_foreign_key "locks", "users" add_foreign_key "managers", "users" add_foreign_key "moderators", "users" diff --git a/spec/features/admin/poll/booth_assigments_spec.rb b/spec/features/admin/poll/booth_assigments_spec.rb index 5813e2dac..cceec1a3e 100644 --- a/spec/features/admin/poll/booth_assigments_spec.rb +++ b/spec/features/admin/poll/booth_assigments_spec.rb @@ -7,66 +7,109 @@ feature 'Admin booths assignments' do login_as(admin.user) end - scenario 'Assign booth to poll', :js do - poll = create(:poll) - booth = create(:poll_booth) + feature 'Admin Booth Assignment management' do - visit admin_poll_path(poll) - within('#poll-resources') do - click_link 'Booths (0)' + let!(:poll) { create(:poll) } + let!(:booth) { create(:poll_booth) } + + scenario 'List Polls and Booths to manage', :js do + second_poll = create(:poll) + second_booth = create(:poll_booth) + + visit booth_assignments_admin_polls_path + + expect(page).to have_content(poll.name) + expect(page).to have_content(second_poll.name) + + within("#poll_#{second_poll.id}") do + click_link 'Manage assignments' + end + + expect(page).to have_content "Assignments for poll '#{second_poll.name}'" + + expect(page).to have_content(booth.name) + expect(page).to have_content(second_booth.name) end - expect(page).to have_content 'There are no booths assigned to this poll.' + scenario 'Assign booth to poll', :js do + visit admin_poll_path(poll) + within('#poll-resources') do + click_link 'Booths (0)' + end - fill_in 'search-booths', with: booth.name - click_button 'Search' - expect(page).to have_content(booth.name) + expect(page).to have_content 'There are no booths assigned to this poll.' + expect(page).to_not have_content booth.name - within('#search-booths-results') do - click_link 'Assign booth' + fill_in 'search-booths', with: booth.name + click_button 'Search' + expect(page).to have_content(booth.name) + + visit manage_admin_poll_booth_assignments_path(poll) + + expect(page).to have_content "Assignments for poll '#{poll.name}'" + + within("#poll_booth_#{booth.id}") do + expect(page).to have_content(booth.name) + expect(page).to have_content "Unassigned" + + click_link 'Assign booth' + + expect(page).not_to have_content "Unassigned" + expect(page).to have_content "Assigned" + expect(page).to have_link "Unassign booth" + end + + visit admin_poll_path(poll) + within('#poll-resources') do + click_link 'Booths (1)' + end + + expect(page).to_not have_content 'There are no booths assigned to this poll.' + expect(page).to have_content booth.name end - expect(page).to have_content 'Booth assigned' + scenario 'Unassign booth from poll', :js do + assignment = create(:poll_booth_assignment, poll: poll, booth: booth) - visit admin_poll_path(poll) - within('#poll-resources') do - click_link 'Booths (1)' + visit admin_poll_path(poll) + within('#poll-resources') do + click_link 'Booths (1)' + end + + expect(page).not_to have_content 'There are no booths assigned to this poll.' + expect(page).to have_content booth.name + + fill_in 'search-booths', with: booth.name + click_button 'Search' + expect(page).to have_content(booth.name) + + visit manage_admin_poll_booth_assignments_path(poll) + + expect(page).to have_content "Assignments for poll '#{poll.name}'" + + within("#poll_booth_#{booth.id}") do + expect(page).to have_content(booth.name) + expect(page).to have_content "Assigned" + + click_link 'Unassign booth' + + expect(page).to have_content "Unassigned" + expect(page).not_to have_content "Assigned" + expect(page).to have_link "Assign booth" + end + + visit admin_poll_path(poll) + within('#poll-resources') do + click_link 'Booths (0)' + end + + expect(page).to have_content 'There are no booths assigned to this poll.' + expect(page).not_to have_content booth.name end - - expect(page).to_not have_content 'There are no booths assigned to this poll.' - expect(page).to have_content booth.name - end - - scenario 'Remove booth from poll', :js do - poll = create(:poll) - booth = create(:poll_booth) - assignment = create(:poll_booth_assignment, poll: poll, booth: booth) - - visit admin_poll_path(poll) - within('#poll-resources') do - click_link 'Booths (1)' - end - - expect(page).to_not have_content 'There are no booths assigned to this poll.' - expect(page).to have_content booth.name - - within("#poll_booth_assignment_#{assignment.id}") do - click_link 'Remove booth from poll' - end - - expect(page).to have_content 'Booth not assigned anymore' - - visit admin_poll_path(poll) - within('#poll-resources') do - click_link 'Booths (0)' - end - - expect(page).to have_content 'There are no booths assigned to this poll.' - expect(page).to_not have_content booth.name end feature 'Show' do - scenario 'Lists all assigned poll oficers' do + scenario 'Lists all assigned poll officers' do poll = create(:poll) booth = create(:poll_booth) booth_assignment = create(:poll_booth_assignment, poll: poll, booth: booth) diff --git a/spec/features/admin/poll/polls_spec.rb b/spec/features/admin/poll/polls_spec.rb index 2a2d26ea5..210b2ff6a 100644 --- a/spec/features/admin/poll/polls_spec.rb +++ b/spec/features/admin/poll/polls_spec.rb @@ -60,6 +60,10 @@ feature 'Admin polls' do fill_in 'poll_ends_at', with: end_date.strftime("%d/%m/%Y") fill_in 'poll_summary', with: "Upcoming poll's summary. This poll..." fill_in 'poll_description', with: "Upcomming poll's description. This poll..." + + expect(page).to_not have_css("#poll_results_enabled") + expect(page).to_not have_css("#poll_stats_enabled") + click_button "Create poll" expect(page).to have_content "Poll created successfully" @@ -79,14 +83,25 @@ feature 'Admin polls' do expect(page).to have_css("img[alt='#{poll.image.title}']") + expect(page).to have_css("#poll_results_enabled") + expect(page).to have_css("#poll_stats_enabled") + fill_in "poll_name", with: "Next Poll" fill_in 'poll_ends_at', with: end_date.strftime("%d/%m/%Y") + check 'poll_results_enabled' + check 'poll_stats_enabled' click_button "Update poll" expect(page).to have_content "Poll updated successfully" expect(page).to have_content "Next Poll" expect(page).to have_content I18n.l(end_date.to_date) + + click_link "Edit poll" + + expect(page).to have_field('poll_results_enabled', checked: true) + expect(page).to have_field('poll_stats_enabled', checked: true) + end scenario 'Edit from index' do diff --git a/spec/features/officing_spec.rb b/spec/features/officing_spec.rb index 338b416ea..b208b735c 100644 --- a/spec/features/officing_spec.rb +++ b/spec/features/officing_spec.rb @@ -55,7 +55,22 @@ feature 'Poll Officing' do expect(page).to have_content "You do not have permission to access this page" end - scenario 'Access as an poll officer is authorized' do + scenario 'Access as an administrator is not authorized' do + create(:administrator, user: user) + create(:poll) + login_as(user) + visit root_path + + expect(page).to_not have_link("Polling officers") + visit officing_root_path + + expect(current_path).not_to eq(officing_root_path) + expect(current_path).to eq(root_path) + expect(page).to have_content "You do not have permission to access this page" + end + + scenario 'Access as an administrator with poll officer role is authorized' do + create(:administrator, user: user) create(:poll_officer, user: user) create(:poll) login_as(user) @@ -68,8 +83,8 @@ feature 'Poll Officing' do expect(page).to_not have_content "You do not have permission to access this page" end - scenario 'Access as an administrator is authorized' do - create(:administrator, user: user) + scenario 'Access as an poll officer is authorized' do + create(:poll_officer, user: user) create(:poll) login_as(user) visit root_path