aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoachim Filip Ignacy Bartosik <jbartosik@gmail.com>2011-07-20 20:45:16 +0200
committerPetteri Räty <petsku@petteriraty.eu>2011-08-03 12:51:57 +0300
commit33abbf833236f957ff49d46951ee0dad294c677e (patch)
tree27f53754b62c7de3045af7a917cf425f7143c5ee
parentMake some more actions available only to council members (diff)
downloadcouncil-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.rb9
-rw-r--r--site/app/models/agenda.rb19
-rw-r--r--site/app/models/agenda_item.rb2
-rw-r--r--site/app/models/vote.rb14
-rw-r--r--site/app/views/agenda_items/show.dryml43
-rw-r--r--site/config/routes.rb1
-rw-r--r--site/db/schema.rb18
-rw-r--r--site/doc/sample_configs/council_term.yml2
-rw-r--r--site/features/poll.feature14
-rw-r--r--site/features/step_definitions/poll_steps.rb22
-rw-r--r--site/features/step_definitions/voting_steps.rb2
-rw-r--r--site/features/support/env.rb1
-rw-r--r--site/features/support/paths.rb2
-rw-r--r--site/lib/fixed_conf.rb33
-rw-r--r--site/spec/models/agenda_spec.rb17
-rw-r--r--site/spec/models/vote_spec.rb11
-rw-r--r--site/spec/spec_helper.rb1
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="&current_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="&current_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)