Gentoo Archives: gentoo-commits

From: "Petteri Räty" <betelgeuse@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/council-webapp:master commit in: site/db/, site/features/support/, site/doc/sample_configs/, ...
Date: Wed, 03 Aug 2011 09:58:03
Message-Id: 33abbf833236f957ff49d46951ee0dad294c677e.betelgeuse@gentoo
1 commit: 33abbf833236f957ff49d46951ee0dad294c677e
2 Author: Joachim Filip Ignacy Bartosik <jbartosik <AT> gmail <DOT> com>
3 AuthorDate: Wed Jul 20 18:45:16 2011 +0000
4 Commit: Petteri Räty <betelgeuse <AT> gentoo <DOT> org>
5 CommitDate: Wed Aug 3 09:51:57 2011 +0000
6 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/council-webapp.git;a=commit;h=33abbf83
7
8 Custom doodle-like feature
9
10 ---
11 site/app/controllers/agenda_items_controller.rb | 9 ++++
12 site/app/models/agenda.rb | 19 ++++++++-
13 site/app/models/agenda_item.rb | 2 +
14 site/app/models/vote.rb | 14 ++++++
15 site/app/views/agenda_items/show.dryml | 43 ++++++++++++++++++++
16 site/config/routes.rb | 1 +
17 site/db/schema.rb | 18 +-------
18 site/doc/sample_configs/council_term.yml | 2 +
19 site/features/poll.feature | 14 ++++++
20 site/features/step_definitions/poll_steps.rb | 22 ++++++++++
21 site/features/step_definitions/voting_steps.rb | 2 +
22 site/features/support/env.rb | 1 +
23 site/features/support/paths.rb | 2 +-
24 .../fixed_conf.rb} | 28 ++++++++-----
25 site/spec/models/agenda_spec.rb | 17 ++++++--
26 site/spec/models/vote_spec.rb | 11 +++++
27 site/spec/spec_helper.rb | 1 +
28 17 files changed, 172 insertions(+), 34 deletions(-)
29
30 diff --git a/site/app/controllers/agenda_items_controller.rb b/site/app/controllers/agenda_items_controller.rb
31 index c2944dc..d202cd0 100644
32 --- a/site/app/controllers/agenda_items_controller.rb
33 +++ b/site/app/controllers/agenda_items_controller.rb
34 @@ -20,6 +20,15 @@ class AgendaItemsController < ApplicationController
35 auto_actions :all, :except => :index
36 before_filter :login, :except => :show
37
38 + def update_poll_answers
39 + new_choice = params[:choice].keys.collect { |txt| txt.to_i }
40 + item = AgendaItem.find(params[:agenda_item_id])
41 +
42 + Vote.update_user_poll_votes(new_choice, current_user, item)
43 +
44 + redirect_to agenda_item_path(item)
45 + end
46 +
47 protected
48 def login
49 redirect_to user_login_path unless current_user.signed_up?
50
51 diff --git a/site/app/models/agenda.rb b/site/app/models/agenda.rb
52 index bae34c3..b25b0b1 100644
53 --- a/site/app/models/agenda.rb
54 +++ b/site/app/models/agenda.rb
55 @@ -21,7 +21,7 @@ class Agenda < ActiveRecord::Base
56 meeting_time :datetime
57 email_reminder_sent :boolean
58 meeting_log :text
59 - summary :text
60 + summary :text, :null => true
61 timestamps
62 end
63
64 @@ -77,6 +77,21 @@ class Agenda < ActiveRecord::Base
65 agenda.meeting_time ||= Time.now
66 end
67
68 + after_create do |agenda|
69 + day_poll = AgendaItem.create! :poll => true, :title => 'Meeting day poll', :agenda => agenda
70 + hour_poll = AgendaItem.create! :poll => true, :title => 'Meeting hour poll', :agenda => agenda
71 + min_date = CustomConfig['CouncilTerm']['min_days_between_meetings'].days.from_now
72 +
73 + (0..CustomConfig['CouncilTerm']['days_for_meeting']).each do |days_from_min|
74 + description = (min_date + days_from_min.days).strftime '%Y.%m.%d %A'
75 + VotingOption.create! :agenda_item => day_poll, :description => description
76 + end
77 +
78 + (0..24).each do |hour|
79 + VotingOption.create! :agenda_item => hour_poll, :description => "#{hour}:00 - #{hour + 1}:00"
80 + end
81 + end
82 +
83 def self.current
84 result = Agenda.state_is_not(:old).first
85 result = Agenda.create! unless result
86 @@ -136,7 +151,7 @@ class Agenda < ActiveRecord::Base
87 end
88
89 def time_for_reminders(type)
90 - offset = CustomConfig['Reminders']["hours_before_meeting_to_send_#{type}_reminders"].hours
91 + offset = CustomConfig['Reminders']["hours_before_meeting_to_send_#{type.to_s}_reminders"].hours
92 meeting_time - offset
93 end
94
95
96 diff --git a/site/app/models/agenda_item.rb b/site/app/models/agenda_item.rb
97 index 1f30599..db5e0ae 100644
98 --- a/site/app/models/agenda_item.rb
99 +++ b/site/app/models/agenda_item.rb
100 @@ -22,6 +22,7 @@ class AgendaItem < ActiveRecord::Base
101 discussion :string
102 body :markdown
103 rejected :boolean, :default => false
104 + poll :boolean, :default => false
105 timelimits :text
106 discussion_time :string
107 timestamps
108 @@ -33,6 +34,7 @@ class AgendaItem < ActiveRecord::Base
109
110 validate :timelimits_entered_properly
111
112 + attr_readonly :poll
113 # --- Permissions --- #
114 def create_permitted?
115 return false if acting_user.guest?
116
117 diff --git a/site/app/models/vote.rb b/site/app/models/vote.rb
118 index 7aea160..a8b63f0 100644
119 --- a/site/app/models/vote.rb
120 +++ b/site/app/models/vote.rb
121 @@ -68,11 +68,25 @@ class Vote < ActiveRecord::Base
122 end
123 end
124
125 + def self.update_user_poll_votes(new_choice, user, item)
126 + old_choice = Vote.user_is(user).for_item(item).*.voting_option_id
127 + (old_choice - new_choice).each do |choice_id|
128 + vote = Vote.user_is(user).voting_option_is(choice_id).first
129 + next if vote.nil?
130 + vote.destroy
131 + end
132 +
133 + (new_choice - old_choice).each do |choice_id|
134 + next unless VotingOption.find(choice_id).agenda_item_is?(item)
135 + Vote.create! :user => user, :voting_option_id => choice_id
136 + end
137 + end
138 protected
139 def user_voted_only_once
140 return if user.nil?
141 return if voting_option.nil?
142 return if voting_option.agenda_item.nil?
143 + return if voting_option.agenda_item.poll
144 other_votes = Vote.for_item(voting_option.agenda_item_id).user_id_is(user_id)
145 other_votes = other_votes.id_is_not(id) unless new_record?
146 if other_votes.count > 0
147
148 diff --git a/site/app/views/agenda_items/show.dryml b/site/app/views/agenda_items/show.dryml
149 index b0b425d..3f0507f 100644
150 --- a/site/app/views/agenda_items/show.dryml
151 +++ b/site/app/views/agenda_items/show.dryml
152 @@ -16,6 +16,49 @@
153 </form>
154 </span>
155 </div>
156 +
157 + <table if="&current_user.signed_up?">
158 + <tr>
159 + <repeat:voting_options>
160 + <th>
161 + <name/>
162 + </th>
163 + </repeat>
164 + </tr>
165 + <repeat with="&User.council_member_is(true)">
166 + <tr>
167 + <% user = this %>
168 + <repeat with="&@this.voting_options">
169 + <td if="& not(user.id == current_user.id)">
170 + <unless with="&Vote.user_is(user).voting_option_is(this).count.zero?">
171 + +
172 + </unless>
173 + </td>
174 + </repeat>
175 + </tr>
176 + </repeat>
177 + <form if="&current_user.council_member?" action="&update_poll_answers_path">
178 + <tr>
179 + <repeat with="&@this.voting_options">
180 + <td>
181 + <% name = "choice[#{this.id}]"
182 + id = this.id %>
183 + <if with="&Vote.user_is(current_user).voting_option_is(this).count.zero?">
184 + <input type="checkbox" name="&name" value="&id"/>
185 + </if>
186 + <else>
187 + <input type="checkbox" name="&name" value="&this.id" checked/>
188 + </else>
189 + </td>
190 + </repeat>
191 + <td>
192 + <after-submit stay-here/>
193 + <input type="hidden" name="agenda_item_id" value="&this.id"/>
194 + <submit label="Update choice"/>
195 + </td>
196 + </tr>
197 + </form>
198 + </table>
199 </append-content-body:>
200
201 <after-collection:>
202
203 diff --git a/site/config/routes.rb b/site/config/routes.rb
204 index 19fcaba..2a66e46 100644
205 --- a/site/config/routes.rb
206 +++ b/site/config/routes.rb
207 @@ -20,6 +20,7 @@ Council::Application.routes.draw do
208
209 match 'users/voters' => 'users#voters', :as => 'voters'
210 match 'users/current_council_slacking' => 'users#current_council_slacking', :as => 'current_council_slacking'
211 + match 'agenda_items/update_poll_answers' => 'agenda_items#update_poll_answers', :as => 'update_poll_answers'
212 match 'agendas/current_items' => 'agendas#current_items', :as => 'current_items'
213 match 'agendas/results' => 'agendas#results', :as => 'results'
214 match 'agendas/reminders' => 'agendas#reminders', :as => 'reminders'
215
216 diff --git a/site/db/schema.rb b/site/db/schema.rb
217 index 52620df..c160c10 100644
218 --- a/site/db/schema.rb
219 +++ b/site/db/schema.rb
220 @@ -1,18 +1,3 @@
221 -# Gentoo Council Web App - to help Gentoo Council do their job better
222 -# Copyright (C) 2011 Joachim Filip Bartosik
223 -#
224 -# This program is free software: you can redistribute it and/or modify
225 -# it under the terms of the GNU Affero General Public License as
226 -# published by the Free Software Foundation, version 3 of the License
227 -#
228 -# This program is distributed in the hope that it will be useful,
229 -# but WITHOUT ANY WARRANTY; without even the implied warranty of
230 -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
231 -# GNU Affero General Public License for more details.
232 -#
233 -# You should have received a copy of the GNU Affero General Public License
234 -# along with this program. If not, see <http://www.gnu.org/licenses/>.
235 -
236 # This file is auto-generated from the current state of the database. Instead
237 # of editing this file, please use the migrations feature of Active Record to
238 # incrementally modify your database, and then regenerate this schema definition.
239 @@ -25,7 +10,7 @@
240 #
241 # It's strongly recommended to check this file into your version control system.
242
243 -ActiveRecord::Schema.define(:version => 20110721195225) do
244 +ActiveRecord::Schema.define(:version => 20110802090951) do
245
246 create_table "agenda_items", :force => true do |t|
247 t.string "title", :default => "", :null => false
248 @@ -38,6 +23,7 @@ ActiveRecord::Schema.define(:version => 20110721195225) do
249 t.integer "agenda_id"
250 t.text "timelimits", :default => "", :null => false
251 t.string "discussion_time", :default => "", :null => false
252 + t.boolean "poll", :default => false
253 end
254
255 add_index "agenda_items", ["agenda_id"], :name => "index_agenda_items_on_agenda_id"
256
257 diff --git a/site/doc/sample_configs/council_term.yml b/site/doc/sample_configs/council_term.yml
258 index 3db917b..d889a1a 100644
259 --- a/site/doc/sample_configs/council_term.yml
260 +++ b/site/doc/sample_configs/council_term.yml
261 @@ -1 +1,3 @@
262 start_time: --- 2011-05-09T21:12:15+02:00
263 +min_days_between_meetings: 28
264 +days_for_meeting: 4
265
266 diff --git a/site/features/poll.feature b/site/features/poll.feature
267 new file mode 100644
268 index 0000000..f25fdb3
269 --- /dev/null
270 +++ b/site/features/poll.feature
271 @@ -0,0 +1,14 @@
272 +Feature: As council member
273 + I want to vote in automatically created polls
274 + To make planning meetings easier
275 +
276 + Scenario: View meeting time polls and vote
277 + Given I am logged in as a council member
278 + When I am on the current agenda page
279 + And I follow "Meeting day poll"
280 + Then I should see suggested meeting times
281 +
282 + When I check some boxes
283 + And press "Update choice"
284 +
285 + Then I should see my times marked
286
287 diff --git a/site/features/step_definitions/poll_steps.rb b/site/features/step_definitions/poll_steps.rb
288 new file mode 100644
289 index 0000000..86a80ca
290 --- /dev/null
291 +++ b/site/features/step_definitions/poll_steps.rb
292 @@ -0,0 +1,22 @@
293 +Then /^I should see suggested meeting times$/ do
294 + descriptions = Agenda.current.agenda_items.first.voting_options.*.description
295 + descriptions.each do |description|
296 + Then "I should see \"#{description}\""
297 + end
298 +end
299 +
300 +When /^I check some boxes$/ do
301 + options = Agenda.current.agenda_items.first.voting_options
302 + When "I check \"choice[#{options.first.id}]\""
303 + When "I check \"choice[#{options.last.id}]\""
304 +end
305 +
306 +Then /^I should see my times marked$/ do
307 + options = Agenda.current.agenda_items.first.voting_options
308 + "I should see checked checkbox with name \"#{options.first.id}\""
309 + "I should see checked checkbox with name \"#{options.last.id}\""
310 +end
311 +
312 +Then /^I should see checked checkbox with name "([^"]*)"$/ do |name|
313 + page.should have_xpath(:xpath, "//input[@type='checkbox'][@name='#{"choice[#{no}]"}'][@checked]")
314 +end
315
316 diff --git a/site/features/step_definitions/voting_steps.rb b/site/features/step_definitions/voting_steps.rb
317 index cfa1253..3740c0b 100644
318 --- a/site/features/step_definitions/voting_steps.rb
319 +++ b/site/features/step_definitions/voting_steps.rb
320 @@ -38,6 +38,8 @@ end
321
322 Given /^there is an item with some voting options for current agenda$/ do
323 agenda = Factory(:agenda)
324 + AgendaItem.destroy_all
325 + VotingOption.destroy_all
326 item = Factory(:agenda_item, :agenda => agenda)
327 voting_option1 = Factory(:voting_option, :agenda_item => item)
328 voting_option2 = Factory(:voting_option, :agenda_item => item, :description => 'Another choice')
329
330 diff --git a/site/features/support/env.rb b/site/features/support/env.rb
331 index eb307a5..4c9c00e 100644
332 --- a/site/features/support/env.rb
333 +++ b/site/features/support/env.rb
334 @@ -14,6 +14,7 @@
335 # along with this program. If not, see <http://www.gnu.org/licenses/>.
336
337 require 'cucumber/rails'
338 +require 'fixed_conf.rb'
339 Capybara.default_selector = :css
340 Capybara.default_driver = :selenium
341 ActionController::Base.allow_rescue = false
342
343 diff --git a/site/features/support/paths.rb b/site/features/support/paths.rb
344 index b0aa6e1..d3389bb 100644
345 --- a/site/features/support/paths.rb
346 +++ b/site/features/support/paths.rb
347 @@ -50,7 +50,7 @@ module NavigationHelpers
348 when /([1-9]*)th agenda page/
349 agenda_path(Agenda.find $1)
350
351 - when /agenda item number ([1-9]*) show page/
352 + when /agenda item number ([0-9]*) show page/
353 agenda_item_path($1)
354
355 when /newest agenda item page/
356
357 diff --git a/site/app/controllers/agenda_items_controller.rb b/site/lib/fixed_conf.rb
358 similarity index 54%
359 copy from site/app/controllers/agenda_items_controller.rb
360 copy to site/lib/fixed_conf.rb
361 index c2944dc..fd6d425 100644
362 --- a/site/app/controllers/agenda_items_controller.rb
363 +++ b/site/lib/fixed_conf.rb
364 @@ -13,15 +13,21 @@
365 # You should have received a copy of the GNU Affero General Public License
366 # along with this program. If not, see <http://www.gnu.org/licenses/>.
367
368 -class AgendaItemsController < ApplicationController
369 -
370 - hobo_model_controller
371 -
372 - auto_actions :all, :except => :index
373 - before_filter :login, :except => :show
374 -
375 - protected
376 - def login
377 - redirect_to user_login_path unless current_user.signed_up?
378 - end
379 +if Object.const_defined? :CustomConfig
380 + if CustomConfig.is_a? Hash
381 + CustomConfig.clear
382 + else
383 + puts "Warning constant CustomConfig is defined and is not a Hash. Something is wrong."
384 + CustomConfig = {}
385 + end
386 +else
387 + CustomConfig = {}
388 end
389 +
390 +CustomConfig['CouncilTerm'] = {}
391 +CustomConfig['CouncilTerm']['start_time'] = 1.year.ago
392 +CustomConfig['CouncilTerm']['min_days_between_meetings'] = 7
393 +CustomConfig['CouncilTerm']['days_for_meeting'] = 7
394 +CustomConfig['Reminders'] = {}
395 +CustomConfig['Reminders']['hours_before_meeting_to_send_irc_reminders'] = 2
396 +CustomConfig['Reminders']['hours_before_meeting_to_send_email_reminders'] = 2
397
398 diff --git a/site/spec/models/agenda_spec.rb b/site/spec/models/agenda_spec.rb
399 index aab4bb0..d718035 100644
400 --- a/site/spec/models/agenda_spec.rb
401 +++ b/site/spec/models/agenda_spec.rb
402 @@ -241,8 +241,6 @@ describe Agenda do
403 end
404
405 it 'should return proper irc_reminders hash' do
406 - CustomConfig['Reminders']["hours_befeore_meeting_to_send_irc_reminders"] = 2
407 -
408 a1 = Factory(:agenda)
409 users = users_factory([:council]*2 + [:user]*2)
410 Agenda.irc_reminders.keys.should include('remind_time')
411 @@ -301,6 +299,7 @@ describe Agenda do
412 it 'should return proper voting_array' do
413 old_agenda = Factory(:agenda, :state => 'old')
414 current_agenda = Factory(:agenda)
415 + AgendaItem.destroy_all
416 i1 = Factory(:agenda_item, :agenda => old_agenda, :timelimits => '0:0')
417 i2 = Factory(:agenda_item, :agenda => current_agenda, :timelimits => "10:0 Ten minutes passed")
418 i3 = Factory(:agenda_item, :agenda => current_agenda, :timelimits => "0:10 Ten seconds passed")
419 @@ -317,11 +316,12 @@ describe Agenda do
420 describe '.update_voting_options' do
421 it 'should remove unneeded voting options and keep existing needed options' do
422 current_agenda = Factory(:agenda)
423 + VotingOption.destroy_all
424 item = Factory(:agenda_item, :agenda => current_agenda)
425 unneeded_option = Factory(:voting_option, :agenda_item => item, :description => 'unneeded')
426 needed_option = Factory(:voting_option, :agenda_item => item, :description => 'needed')
427
428 - VotingOption.count.should be_equal(2)
429 + item.voting_options.count.should be_equal(2)
430
431 Agenda.update_voting_options [[item.title, [needed_option.description]]]
432
433 @@ -329,13 +329,15 @@ describe Agenda do
434 VotingOption.first.description.should == needed_option.description
435 VotingOption.first.id.should == needed_option.id
436 end
437 +
438 it 'should create requested new voting options' do
439 current_agenda = Factory(:agenda)
440 + VotingOption.destroy_all
441 item = Factory(:agenda_item, :agenda => current_agenda)
442 needed_option = Factory(:voting_option, :agenda_item => item, :description => 'needed')
443
444 Agenda.update_voting_options [[item.title, [needed_option.description, 'new option']]]
445 - VotingOption.count.should be_equal(2)
446 + item.voting_options.count.should be_equal(2)
447 VotingOption.last.description.should == 'new option'
448 end
449 end
450 @@ -347,4 +349,11 @@ describe Agenda do
451 agenda.save!
452 Approval.count.should be_zero
453 end
454 +
455 + it 'should create polls for choosing day and hour of meeting' do
456 + items = Agenda.current.agenda_items
457 + items.length.should be_equal(2)
458 + items.first.voting_options.length.should be_equal(CustomConfig['CouncilTerm']['days_for_meeting'] + 1)
459 + items.last.voting_options.length.should be_equal(25)
460 + end
461 end
462
463 diff --git a/site/spec/models/vote_spec.rb b/site/spec/models/vote_spec.rb
464 index 55f6d24..7a46243 100644
465 --- a/site/spec/models/vote_spec.rb
466 +++ b/site/spec/models/vote_spec.rb
467 @@ -53,6 +53,17 @@ describe Vote do
468 Vote.new(:user => v.user, :voting_option => o).should_not be_valid
469 end
470
471 + it 'should allow users to voting for multiple options in polls' do
472 + item = Factory(:agenda_item, :poll => true)
473 + option1 = Factory(:voting_option, :agenda_item => item, :description => 'option')
474 + option2 = Factory(:voting_option, :agenda_item => item, :description => 'other option')
475 + user = users_factory(:user)
476 +
477 + Factory(:vote, :user => user, :voting_option => option1)
478 + Vote.new(:user => user, :voting_option => option2).should be_valid
479 + Vote.new(:user => user, :voting_option => option1).should_not be_valid
480 + end
481 +
482 it 'should prevent users from setting council_vote to true' do
483 for u in users_factory(:registered)
484 v = Factory(:vote, :user => u, :council_vote => true)
485
486 diff --git a/site/spec/spec_helper.rb b/site/spec/spec_helper.rb
487 index 9f35af4..8e2059f 100644
488 --- a/site/spec/spec_helper.rb
489 +++ b/site/spec/spec_helper.rb
490 @@ -16,6 +16,7 @@
491 ENV["RAILS_ENV"] ||= 'test'
492 require File.expand_path("../../config/environment", __FILE__)
493 require 'rspec/rails'
494 +require 'fixed_conf.rb'
495
496 environment_path = File.expand_path(File.join(::Rails.root.to_s, 'config', 'environment'))
497 require(environment_path)