diff options
author | 2011-07-20 20:45:16 +0200 | |
---|---|---|
committer | 2011-08-03 12:51:57 +0300 | |
commit | 33abbf833236f957ff49d46951ee0dad294c677e (patch) | |
tree | 27f53754b62c7de3045af7a917cf425f7143c5ee | |
parent | Make some more actions available only to council members (diff) | |
download | council-webapp-33abbf833236f957ff49d46951ee0dad294c677e.tar.gz council-webapp-33abbf833236f957ff49d46951ee0dad294c677e.tar.bz2 council-webapp-33abbf833236f957ff49d46951ee0dad294c677e.zip |
Custom doodle-like feature
-rw-r--r-- | site/app/controllers/agenda_items_controller.rb | 9 | ||||
-rw-r--r-- | site/app/models/agenda.rb | 19 | ||||
-rw-r--r-- | site/app/models/agenda_item.rb | 2 | ||||
-rw-r--r-- | site/app/models/vote.rb | 14 | ||||
-rw-r--r-- | site/app/views/agenda_items/show.dryml | 43 | ||||
-rw-r--r-- | site/config/routes.rb | 1 | ||||
-rw-r--r-- | site/db/schema.rb | 18 | ||||
-rw-r--r-- | site/doc/sample_configs/council_term.yml | 2 | ||||
-rw-r--r-- | site/features/poll.feature | 14 | ||||
-rw-r--r-- | site/features/step_definitions/poll_steps.rb | 22 | ||||
-rw-r--r-- | site/features/step_definitions/voting_steps.rb | 2 | ||||
-rw-r--r-- | site/features/support/env.rb | 1 | ||||
-rw-r--r-- | site/features/support/paths.rb | 2 | ||||
-rw-r--r-- | site/lib/fixed_conf.rb | 33 | ||||
-rw-r--r-- | site/spec/models/agenda_spec.rb | 17 | ||||
-rw-r--r-- | site/spec/models/vote_spec.rb | 11 | ||||
-rw-r--r-- | site/spec/spec_helper.rb | 1 |
17 files changed, 188 insertions, 23 deletions
diff --git a/site/app/controllers/agenda_items_controller.rb b/site/app/controllers/agenda_items_controller.rb index c2944dc..d202cd0 100644 --- a/site/app/controllers/agenda_items_controller.rb +++ b/site/app/controllers/agenda_items_controller.rb @@ -20,6 +20,15 @@ class AgendaItemsController < ApplicationController auto_actions :all, :except => :index before_filter :login, :except => :show + def update_poll_answers + new_choice = params[:choice].keys.collect { |txt| txt.to_i } + item = AgendaItem.find(params[:agenda_item_id]) + + Vote.update_user_poll_votes(new_choice, current_user, item) + + redirect_to agenda_item_path(item) + end + protected def login redirect_to user_login_path unless current_user.signed_up? diff --git a/site/app/models/agenda.rb b/site/app/models/agenda.rb index bae34c3..b25b0b1 100644 --- a/site/app/models/agenda.rb +++ b/site/app/models/agenda.rb @@ -21,7 +21,7 @@ class Agenda < ActiveRecord::Base meeting_time :datetime email_reminder_sent :boolean meeting_log :text - summary :text + summary :text, :null => true timestamps end @@ -77,6 +77,21 @@ class Agenda < ActiveRecord::Base agenda.meeting_time ||= Time.now end + after_create do |agenda| + day_poll = AgendaItem.create! :poll => true, :title => 'Meeting day poll', :agenda => agenda + hour_poll = AgendaItem.create! :poll => true, :title => 'Meeting hour poll', :agenda => agenda + min_date = CustomConfig['CouncilTerm']['min_days_between_meetings'].days.from_now + + (0..CustomConfig['CouncilTerm']['days_for_meeting']).each do |days_from_min| + description = (min_date + days_from_min.days).strftime '%Y.%m.%d %A' + VotingOption.create! :agenda_item => day_poll, :description => description + end + + (0..24).each do |hour| + VotingOption.create! :agenda_item => hour_poll, :description => "#{hour}:00 - #{hour + 1}:00" + end + end + def self.current result = Agenda.state_is_not(:old).first result = Agenda.create! unless result @@ -136,7 +151,7 @@ class Agenda < ActiveRecord::Base end def time_for_reminders(type) - offset = CustomConfig['Reminders']["hours_before_meeting_to_send_#{type}_reminders"].hours + offset = CustomConfig['Reminders']["hours_before_meeting_to_send_#{type.to_s}_reminders"].hours meeting_time - offset end diff --git a/site/app/models/agenda_item.rb b/site/app/models/agenda_item.rb index 1f30599..db5e0ae 100644 --- a/site/app/models/agenda_item.rb +++ b/site/app/models/agenda_item.rb @@ -22,6 +22,7 @@ class AgendaItem < ActiveRecord::Base discussion :string body :markdown rejected :boolean, :default => false + poll :boolean, :default => false timelimits :text discussion_time :string timestamps @@ -33,6 +34,7 @@ class AgendaItem < ActiveRecord::Base validate :timelimits_entered_properly + attr_readonly :poll # --- Permissions --- # def create_permitted? return false if acting_user.guest? diff --git a/site/app/models/vote.rb b/site/app/models/vote.rb index 7aea160..a8b63f0 100644 --- a/site/app/models/vote.rb +++ b/site/app/models/vote.rb @@ -68,11 +68,25 @@ class Vote < ActiveRecord::Base end end + def self.update_user_poll_votes(new_choice, user, item) + old_choice = Vote.user_is(user).for_item(item).*.voting_option_id + (old_choice - new_choice).each do |choice_id| + vote = Vote.user_is(user).voting_option_is(choice_id).first + next if vote.nil? + vote.destroy + end + + (new_choice - old_choice).each do |choice_id| + next unless VotingOption.find(choice_id).agenda_item_is?(item) + Vote.create! :user => user, :voting_option_id => choice_id + end + end protected def user_voted_only_once return if user.nil? return if voting_option.nil? return if voting_option.agenda_item.nil? + return if voting_option.agenda_item.poll other_votes = Vote.for_item(voting_option.agenda_item_id).user_id_is(user_id) other_votes = other_votes.id_is_not(id) unless new_record? if other_votes.count > 0 diff --git a/site/app/views/agenda_items/show.dryml b/site/app/views/agenda_items/show.dryml index b0b425d..3f0507f 100644 --- a/site/app/views/agenda_items/show.dryml +++ b/site/app/views/agenda_items/show.dryml @@ -16,6 +16,49 @@ </form> </span> </div> + + <table if="¤t_user.signed_up?"> + <tr> + <repeat:voting_options> + <th> + <name/> + </th> + </repeat> + </tr> + <repeat with="&User.council_member_is(true)"> + <tr> + <% user = this %> + <repeat with="&@this.voting_options"> + <td if="& not(user.id == current_user.id)"> + <unless with="&Vote.user_is(user).voting_option_is(this).count.zero?"> + + + </unless> + </td> + </repeat> + </tr> + </repeat> + <form if="¤t_user.council_member?" action="&update_poll_answers_path"> + <tr> + <repeat with="&@this.voting_options"> + <td> + <% name = "choice[#{this.id}]" + id = this.id %> + <if with="&Vote.user_is(current_user).voting_option_is(this).count.zero?"> + <input type="checkbox" name="&name" value="&id"/> + </if> + <else> + <input type="checkbox" name="&name" value="&this.id" checked/> + </else> + </td> + </repeat> + <td> + <after-submit stay-here/> + <input type="hidden" name="agenda_item_id" value="&this.id"/> + <submit label="Update choice"/> + </td> + </tr> + </form> + </table> </append-content-body:> <after-collection:> diff --git a/site/config/routes.rb b/site/config/routes.rb index 19fcaba..2a66e46 100644 --- a/site/config/routes.rb +++ b/site/config/routes.rb @@ -20,6 +20,7 @@ Council::Application.routes.draw do match 'users/voters' => 'users#voters', :as => 'voters' match 'users/current_council_slacking' => 'users#current_council_slacking', :as => 'current_council_slacking' + match 'agenda_items/update_poll_answers' => 'agenda_items#update_poll_answers', :as => 'update_poll_answers' match 'agendas/current_items' => 'agendas#current_items', :as => 'current_items' match 'agendas/results' => 'agendas#results', :as => 'results' match 'agendas/reminders' => 'agendas#reminders', :as => 'reminders' diff --git a/site/db/schema.rb b/site/db/schema.rb index 52620df..c160c10 100644 --- a/site/db/schema.rb +++ b/site/db/schema.rb @@ -1,18 +1,3 @@ -# Gentoo Council Web App - to help Gentoo Council do their job better -# Copyright (C) 2011 Joachim Filip Bartosik -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, version 3 of the License -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. @@ -25,7 +10,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20110721195225) do +ActiveRecord::Schema.define(:version => 20110802090951) do create_table "agenda_items", :force => true do |t| t.string "title", :default => "", :null => false @@ -38,6 +23,7 @@ ActiveRecord::Schema.define(:version => 20110721195225) do t.integer "agenda_id" t.text "timelimits", :default => "", :null => false t.string "discussion_time", :default => "", :null => false + t.boolean "poll", :default => false end add_index "agenda_items", ["agenda_id"], :name => "index_agenda_items_on_agenda_id" diff --git a/site/doc/sample_configs/council_term.yml b/site/doc/sample_configs/council_term.yml index 3db917b..d889a1a 100644 --- a/site/doc/sample_configs/council_term.yml +++ b/site/doc/sample_configs/council_term.yml @@ -1 +1,3 @@ start_time: --- 2011-05-09T21:12:15+02:00 +min_days_between_meetings: 28 +days_for_meeting: 4 diff --git a/site/features/poll.feature b/site/features/poll.feature new file mode 100644 index 0000000..f25fdb3 --- /dev/null +++ b/site/features/poll.feature @@ -0,0 +1,14 @@ +Feature: As council member + I want to vote in automatically created polls + To make planning meetings easier + + Scenario: View meeting time polls and vote + Given I am logged in as a council member + When I am on the current agenda page + And I follow "Meeting day poll" + Then I should see suggested meeting times + + When I check some boxes + And press "Update choice" + + Then I should see my times marked diff --git a/site/features/step_definitions/poll_steps.rb b/site/features/step_definitions/poll_steps.rb new file mode 100644 index 0000000..86a80ca --- /dev/null +++ b/site/features/step_definitions/poll_steps.rb @@ -0,0 +1,22 @@ +Then /^I should see suggested meeting times$/ do + descriptions = Agenda.current.agenda_items.first.voting_options.*.description + descriptions.each do |description| + Then "I should see \"#{description}\"" + end +end + +When /^I check some boxes$/ do + options = Agenda.current.agenda_items.first.voting_options + When "I check \"choice[#{options.first.id}]\"" + When "I check \"choice[#{options.last.id}]\"" +end + +Then /^I should see my times marked$/ do + options = Agenda.current.agenda_items.first.voting_options + "I should see checked checkbox with name \"#{options.first.id}\"" + "I should see checked checkbox with name \"#{options.last.id}\"" +end + +Then /^I should see checked checkbox with name "([^"]*)"$/ do |name| + page.should have_xpath(:xpath, "//input[@type='checkbox'][@name='#{"choice[#{no}]"}'][@checked]") +end diff --git a/site/features/step_definitions/voting_steps.rb b/site/features/step_definitions/voting_steps.rb index cfa1253..3740c0b 100644 --- a/site/features/step_definitions/voting_steps.rb +++ b/site/features/step_definitions/voting_steps.rb @@ -38,6 +38,8 @@ end Given /^there is an item with some voting options for current agenda$/ do agenda = Factory(:agenda) + AgendaItem.destroy_all + VotingOption.destroy_all item = Factory(:agenda_item, :agenda => agenda) voting_option1 = Factory(:voting_option, :agenda_item => item) voting_option2 = Factory(:voting_option, :agenda_item => item, :description => 'Another choice') diff --git a/site/features/support/env.rb b/site/features/support/env.rb index eb307a5..4c9c00e 100644 --- a/site/features/support/env.rb +++ b/site/features/support/env.rb @@ -14,6 +14,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. require 'cucumber/rails' +require 'fixed_conf.rb' Capybara.default_selector = :css Capybara.default_driver = :selenium ActionController::Base.allow_rescue = false diff --git a/site/features/support/paths.rb b/site/features/support/paths.rb index b0aa6e1..d3389bb 100644 --- a/site/features/support/paths.rb +++ b/site/features/support/paths.rb @@ -50,7 +50,7 @@ module NavigationHelpers when /([1-9]*)th agenda page/ agenda_path(Agenda.find $1) - when /agenda item number ([1-9]*) show page/ + when /agenda item number ([0-9]*) show page/ agenda_item_path($1) when /newest agenda item page/ diff --git a/site/lib/fixed_conf.rb b/site/lib/fixed_conf.rb new file mode 100644 index 0000000..fd6d425 --- /dev/null +++ b/site/lib/fixed_conf.rb @@ -0,0 +1,33 @@ +# Gentoo Council Web App - to help Gentoo Council do their job better +# Copyright (C) 2011 Joachim Filip Bartosik +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, version 3 of the License +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +if Object.const_defined? :CustomConfig + if CustomConfig.is_a? Hash + CustomConfig.clear + else + puts "Warning constant CustomConfig is defined and is not a Hash. Something is wrong." + CustomConfig = {} + end +else + CustomConfig = {} +end + +CustomConfig['CouncilTerm'] = {} +CustomConfig['CouncilTerm']['start_time'] = 1.year.ago +CustomConfig['CouncilTerm']['min_days_between_meetings'] = 7 +CustomConfig['CouncilTerm']['days_for_meeting'] = 7 +CustomConfig['Reminders'] = {} +CustomConfig['Reminders']['hours_before_meeting_to_send_irc_reminders'] = 2 +CustomConfig['Reminders']['hours_before_meeting_to_send_email_reminders'] = 2 diff --git a/site/spec/models/agenda_spec.rb b/site/spec/models/agenda_spec.rb index aab4bb0..d718035 100644 --- a/site/spec/models/agenda_spec.rb +++ b/site/spec/models/agenda_spec.rb @@ -241,8 +241,6 @@ describe Agenda do end it 'should return proper irc_reminders hash' do - CustomConfig['Reminders']["hours_befeore_meeting_to_send_irc_reminders"] = 2 - a1 = Factory(:agenda) users = users_factory([:council]*2 + [:user]*2) Agenda.irc_reminders.keys.should include('remind_time') @@ -301,6 +299,7 @@ describe Agenda do it 'should return proper voting_array' do old_agenda = Factory(:agenda, :state => 'old') current_agenda = Factory(:agenda) + AgendaItem.destroy_all i1 = Factory(:agenda_item, :agenda => old_agenda, :timelimits => '0:0') i2 = Factory(:agenda_item, :agenda => current_agenda, :timelimits => "10:0 Ten minutes passed") i3 = Factory(:agenda_item, :agenda => current_agenda, :timelimits => "0:10 Ten seconds passed") @@ -317,11 +316,12 @@ describe Agenda do describe '.update_voting_options' do it 'should remove unneeded voting options and keep existing needed options' do current_agenda = Factory(:agenda) + VotingOption.destroy_all item = Factory(:agenda_item, :agenda => current_agenda) unneeded_option = Factory(:voting_option, :agenda_item => item, :description => 'unneeded') needed_option = Factory(:voting_option, :agenda_item => item, :description => 'needed') - VotingOption.count.should be_equal(2) + item.voting_options.count.should be_equal(2) Agenda.update_voting_options [[item.title, [needed_option.description]]] @@ -329,13 +329,15 @@ describe Agenda do VotingOption.first.description.should == needed_option.description VotingOption.first.id.should == needed_option.id end + it 'should create requested new voting options' do current_agenda = Factory(:agenda) + VotingOption.destroy_all item = Factory(:agenda_item, :agenda => current_agenda) needed_option = Factory(:voting_option, :agenda_item => item, :description => 'needed') Agenda.update_voting_options [[item.title, [needed_option.description, 'new option']]] - VotingOption.count.should be_equal(2) + item.voting_options.count.should be_equal(2) VotingOption.last.description.should == 'new option' end end @@ -347,4 +349,11 @@ describe Agenda do agenda.save! Approval.count.should be_zero end + + it 'should create polls for choosing day and hour of meeting' do + items = Agenda.current.agenda_items + items.length.should be_equal(2) + items.first.voting_options.length.should be_equal(CustomConfig['CouncilTerm']['days_for_meeting'] + 1) + items.last.voting_options.length.should be_equal(25) + end end diff --git a/site/spec/models/vote_spec.rb b/site/spec/models/vote_spec.rb index 55f6d24..7a46243 100644 --- a/site/spec/models/vote_spec.rb +++ b/site/spec/models/vote_spec.rb @@ -53,6 +53,17 @@ describe Vote do Vote.new(:user => v.user, :voting_option => o).should_not be_valid end + it 'should allow users to voting for multiple options in polls' do + item = Factory(:agenda_item, :poll => true) + option1 = Factory(:voting_option, :agenda_item => item, :description => 'option') + option2 = Factory(:voting_option, :agenda_item => item, :description => 'other option') + user = users_factory(:user) + + Factory(:vote, :user => user, :voting_option => option1) + Vote.new(:user => user, :voting_option => option2).should be_valid + Vote.new(:user => user, :voting_option => option1).should_not be_valid + end + it 'should prevent users from setting council_vote to true' do for u in users_factory(:registered) v = Factory(:vote, :user => u, :council_vote => true) diff --git a/site/spec/spec_helper.rb b/site/spec/spec_helper.rb index 9f35af4..8e2059f 100644 --- a/site/spec/spec_helper.rb +++ b/site/spec/spec_helper.rb @@ -16,6 +16,7 @@ ENV["RAILS_ENV"] ||= 'test' require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' +require 'fixed_conf.rb' environment_path = File.expand_path(File.join(::Rails.root.to_s, 'config', 'environment')) require(environment_path) |