public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-commits] proj/council-webapp:master commit in: site/db/, site/features/support/, site/app/views/agenda_items/, site/features/, ...
@ 2011-06-05 20:37 Petteri Räty
  0 siblings, 0 replies; only message in thread
From: Petteri Räty @ 2011-06-05 20:37 UTC (permalink / raw
  To: gentoo-commits

commit:     4b874a907e16dcfb61cc82a69f9c3891bff1bfa8
Author:     Joachim Filip Ignacy Bartosik <jbartosik <AT> gmail <DOT> com>
AuthorDate: Wed May 18 15:19:38 2011 +0000
Commit:     Petteri Räty <betelgeuse <AT> gentoo <DOT> org>
CommitDate: Wed Jun  1 13:21:14 2011 +0000
URL:        http://git.overlays.gentoo.org/gitweb/?p=proj/council-webapp.git;a=commit;h=4b874a90

Application provides data for IRC bot

---
 site/app/controllers/agendas_controller.rb        |    4 ++
 site/app/controllers/users_controller.rb          |    3 +
 site/app/controllers/voting_options_controller.rb |    6 ++
 site/app/models/agenda.rb                         |    6 ++
 site/app/models/agenda_item.rb                    |    2 +-
 site/app/models/user.rb                           |    1 +
 site/app/models/vote.rb                           |   54 +++++++++++++++++++++
 site/app/models/voting_option.rb                  |   41 ++++++++++++++++
 site/app/viewhints/agenda_item_hints.rb           |    5 ++
 site/app/views/agenda_items/show.dryml            |    9 ++++
 site/config/hobo_routes.rb                        |   10 ++++
 site/config/routes.rb                             |    3 +
 site/db/schema.rb                                 |   23 ++++++++-
 site/features/step_definitions/voting_steps.rb    |   22 ++++++++
 site/features/step_definitions/within_steps.rb    |    4 +-
 site/features/support/paths.rb                    |    7 +++
 site/features/voting.feature                      |   23 +++++++++
 site/spec/factories.rb                            |   13 +++++-
 site/spec/models/vote_spec.rb                     |   37 ++++++++++++++
 site/spec/models/voting_option_spec.rb            |   41 ++++++++++++++++
 20 files changed, 309 insertions(+), 5 deletions(-)

diff --git a/site/app/controllers/agendas_controller.rb b/site/app/controllers/agendas_controller.rb
index bfc32a4..0ee7ae0 100644
--- a/site/app/controllers/agendas_controller.rb
+++ b/site/app/controllers/agendas_controller.rb
@@ -7,4 +7,8 @@ class AgendasController < ApplicationController
   def index
     hobo_index Agenda.state_is(:old)
   end
+
+  def current_items
+    render :json => Agenda.current.voting_array
+  end
 end

diff --git a/site/app/controllers/users_controller.rb b/site/app/controllers/users_controller.rb
index 651de7d..eccc510 100644
--- a/site/app/controllers/users_controller.rb
+++ b/site/app/controllers/users_controller.rb
@@ -14,4 +14,7 @@ class UsersController < ApplicationController
     end
   end
 
+  def voters
+    render :json => ::User.council_member_is(true).*.irc_nick
+  end
 end

diff --git a/site/app/controllers/voting_options_controller.rb b/site/app/controllers/voting_options_controller.rb
new file mode 100644
index 0000000..951ddbb
--- /dev/null
+++ b/site/app/controllers/voting_options_controller.rb
@@ -0,0 +1,6 @@
+class VotingOptionsController < ApplicationController
+
+  hobo_model_controller
+
+  auto_actions :all
+end

diff --git a/site/app/models/agenda.rb b/site/app/models/agenda.rb
index f8f90df..ed8e385 100644
--- a/site/app/models/agenda.rb
+++ b/site/app/models/agenda.rb
@@ -81,6 +81,12 @@ class Agenda < ActiveRecord::Base
     ['open', 'submissions_closed'].include?(state.to_s)
   end
 
+  def voting_array
+    agenda_items.collect do |item|
+      [item.title, item.voting_options.*.description]
+    end
+  end
+
   protected
     def there_is_only_one_non_archival_agenda
       return if(state.to_s == 'old')

diff --git a/site/app/models/agenda_item.rb b/site/app/models/agenda_item.rb
index f5108bd..f590bb1 100644
--- a/site/app/models/agenda_item.rb
+++ b/site/app/models/agenda_item.rb
@@ -12,6 +12,7 @@ class AgendaItem < ActiveRecord::Base
 
   belongs_to :user, :creator => true
   belongs_to :agenda
+  has_many   :voting_options
 
   # --- Permissions --- #
   def create_permitted?
@@ -49,5 +50,4 @@ class AgendaItem < ActiveRecord::Base
     return false unless agenda.nil?
     return acting_user == user if [nil, :title, :discussion, :body].include?(field)
   end
-
 end

diff --git a/site/app/models/user.rb b/site/app/models/user.rb
index e49fa77..738165e 100644
--- a/site/app/models/user.rb
+++ b/site/app/models/user.rb
@@ -11,6 +11,7 @@ class User < ActiveRecord::Base
     timestamps
   end
 
+  has_many  :votes
   # --- Signup lifecycle --- #
 
   lifecycle do

diff --git a/site/app/models/vote.rb b/site/app/models/vote.rb
new file mode 100644
index 0000000..00c64a7
--- /dev/null
+++ b/site/app/models/vote.rb
@@ -0,0 +1,54 @@
+class Vote < ActiveRecord::Base
+
+  hobo_model # Don't put anything above this
+
+  fields do
+    timestamps
+  end
+
+  belongs_to :voting_option, :null => false
+  belongs_to :user, :null => false
+
+  index [:voting_option_id, :user_id], :unique => true
+
+  validates_presence_of :voting_option
+  validates_presence_of :user
+  validates_uniqueness_of :voting_option_id, :scope => :user_id
+  validate :user_voted_only_once
+  validate :user_is_council_member
+  # --- Permissions --- #
+
+  def create_permitted?
+    false
+  end
+
+  def update_permitted?
+    false
+  end
+
+  def destroy_permitted?
+    false
+  end
+
+  def view_permitted?(field)
+    true
+  end
+
+  protected
+    def user_voted_only_once
+      return if user.nil?
+      return if voting_option.nil?
+      return if voting_option.agenda_item.nil?
+      other_votes = Vote.joins(:voting_option).where(['voting_options.agenda_item_id = ? AND votes.user_id = ?',
+                                                        voting_option.agenda_item_id, user_id])
+      other_votes = other_votes.id_is_not(id) unless new_record?
+      if other_votes.count > 0
+        errors.add(:user, 'User can vote only once per agenda item.')
+      end
+    end
+
+    def user_is_council_member
+      return if user.nil?
+      errors.add(:user, 'Only council members can vote.') unless user.council_member?
+    end
+end

diff --git a/site/app/models/voting_option.rb b/site/app/models/voting_option.rb
new file mode 100644
index 0000000..b9c2226
--- /dev/null
+++ b/site/app/models/voting_option.rb
@@ -0,0 +1,41 @@
+class VotingOption < ActiveRecord::Base
+
+  hobo_model # Don't put anything above this
+
+  fields do
+    description :string
+    timestamps
+  end
+
+  belongs_to  :agenda_item, :null => false
+  has_many    :votes
+
+  validates_presence_of :agenda_item
+  validates_uniqueness_of :description, :scope => :agenda_item_id
+
+  def name
+    description
+  end
+
+  # --- Permissions --- #
+
+  def create_permitted?
+    acting_user.council_member?
+  end
+
+  def update_permitted?
+    return false unless acting_user.council_member?
+    return true if agenda_item.nil?
+    return true if agenda_item.agenda.nil?
+    return true if agenda_item.agenda.state == 'open'
+    false
+  end
+
+  def destroy_permitted?
+    updatable_by?(acting_user)
+  end
+
+  def view_permitted?(field)
+    true
+  end
+end

diff --git a/site/app/viewhints/agenda_item_hints.rb b/site/app/viewhints/agenda_item_hints.rb
new file mode 100644
index 0000000..3fe2071
--- /dev/null
+++ b/site/app/viewhints/agenda_item_hints.rb
@@ -0,0 +1,5 @@
+class AgendaItemHints < Hobo::ViewHints
+
+  children :voting_options
+
+end

diff --git a/site/app/views/agenda_items/show.dryml b/site/app/views/agenda_items/show.dryml
index 626298d..ba27673 100644
--- a/site/app/views/agenda_items/show.dryml
+++ b/site/app/views/agenda_items/show.dryml
@@ -11,4 +11,13 @@
       <submit label="Un-reject" if="&this.rejected"/>
     </form>
   </append-content:>
+
+  <after-collection:>
+    <form if="&VotingOption.new.creatable_by?(current_user)" action="&create_voting_option_path">
+      <input id="voting_option_description" name="voting_option[description]" type="text"/>
+      <input id="voting_option_agenda_item_id" name="voting_option[agenda_item_id]" type="hidden" value="&this.id"/>
+      <after-submit stay-here/>
+      <submit label="Add a voting option"/>
+    </form>
+  </after-collection:>
 </show-page>

diff --git a/site/config/hobo_routes.rb b/site/config/hobo_routes.rb
index 11fb9b0..ad63a15 100644
--- a/site/config/hobo_routes.rb
+++ b/site/config/hobo_routes.rb
@@ -53,4 +53,14 @@ Council::Application.routes.draw do
   put 'agenda_items/:id(.:format)' => 'agenda_items#update', :as => 'update_agenda_item', :constraints => { :id => %r([^/.?]+) }
   delete 'agenda_items/:id(.:format)' => 'agenda_items#destroy', :as => 'destroy_agenda_item', :constraints => { :id => %r([^/.?]+) }
 
+
+  # Resource routes for controller "voting_options"
+  get 'voting_options(.:format)' => 'voting_options#index', :as => 'voting_options'
+  get 'voting_options/new(.:format)', :as => 'new_voting_option'
+  get 'voting_options/:id/edit(.:format)' => 'voting_options#edit', :as => 'edit_voting_option'
+  get 'voting_options/:id(.:format)' => 'voting_options#show', :as => 'voting_option', :constraints => { :id => %r([^/.?]+) }
+  post 'voting_options(.:format)' => 'voting_options#create', :as => 'create_voting_option'
+  put 'voting_options/:id(.:format)' => 'voting_options#update', :as => 'update_voting_option', :constraints => { :id => %r([^/.?]+) }
+  delete 'voting_options/:id(.:format)' => 'voting_options#destroy', :as => 'destroy_voting_option', :constraints => { :id => %r([^/.?]+) }
+
 end

diff --git a/site/config/routes.rb b/site/config/routes.rb
index e36d680..e3337bb 100644
--- a/site/config/routes.rb
+++ b/site/config/routes.rb
@@ -3,6 +3,9 @@ Council::Application.routes.draw do
 
   match 'search' => 'front#search', :as => 'site_search'
 
+  match 'users/voters' => 'users#voters', :as => 'voters'
+  match 'agendas/current_items' => 'agendas#current_items', :as => 'current_items'
+
   # The priority is based upon order of creation:
   # first created -> highest priority.
 

diff --git a/site/db/schema.rb b/site/db/schema.rb
index cf1a791..ea99919 100644
--- a/site/db/schema.rb
+++ b/site/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended to check this file into your version control system.
 
-ActiveRecord::Schema.define(:version => 20110523180453) do
+ActiveRecord::Schema.define(:version => 20110601094807) do
 
   create_table "agenda_items", :force => true do |t|
     t.string   "title"
@@ -37,7 +37,6 @@ ActiveRecord::Schema.define(:version => 20110523180453) do
   add_index "agendas", ["state"], :name => "index_agendas_on_state"
 
   create_table "participations", :force => true do |t|
-    t.string   "name"
     t.string   "irc_nick"
     t.datetime "created_at"
     t.datetime "updated_at"
@@ -66,4 +65,24 @@ ActiveRecord::Schema.define(:version => 20110523180453) do
 
   add_index "users", ["state"], :name => "index_users_on_state"
 
+  create_table "votes", :force => true do |t|
+    t.datetime "created_at"
+    t.datetime "updated_at"
+    t.integer  "voting_option_id", :null => false
+    t.integer  "user_id",          :null => false
+  end
+
+  add_index "votes", ["user_id"], :name => "index_votes_on_user_id"
+  add_index "votes", ["voting_option_id", "user_id"], :name => "index_votes_on_voting_option_id_and_user_id", :unique => true
+  add_index "votes", ["voting_option_id"], :name => "index_votes_on_voting_option_id"
+
+  create_table "voting_options", :force => true do |t|
+    t.string   "description"
+    t.datetime "created_at"
+    t.datetime "updated_at"
+    t.integer  "agenda_item_id", :null => false
+  end
+
+  add_index "voting_options", ["agenda_item_id"], :name => "index_voting_options_on_agenda_item_id"
+
 end

diff --git a/site/features/step_definitions/voting_steps.rb b/site/features/step_definitions/voting_steps.rb
new file mode 100644
index 0000000..0519397
--- /dev/null
+++ b/site/features/step_definitions/voting_steps.rb
@@ -0,0 +1,22 @@
+When /^I follow first agenda item link$/ do
+  When "I follow \"#{AgendaItem.first.title}\""
+end
+
+When /^I add example voting option$/ do
+  When 'I fill in "voting_option_description" with "example"'
+  When 'I press "Add a voting option"'
+end
+
+Given /^example voting option$/ do
+  Given 'example agenda item'
+  VotingOption.new(:description => 'example', :agenda_item => AgendaItem.last).save!
+end
+
+When /^I go from the homepage to edit last voting option page$/ do
+  When 'I am on the homepage'
+  When 'I follow "Agendas"'
+  When 'I follow link to current agenda'
+  When "I follow \"#{AgendaItem.last.title}\""
+  When "I follow \"#{VotingOption.last.description}\""
+  When 'I follow "Edit Voting option"'
+end

diff --git a/site/features/step_definitions/within_steps.rb b/site/features/step_definitions/within_steps.rb
index 7d56fc4..57bf030 100644
--- a/site/features/step_definitions/within_steps.rb
+++ b/site/features/step_definitions/within_steps.rb
@@ -9,7 +9,9 @@
   'in the agendas collection' => '.collection.agendas',
   'as empty collection message' => '.empty-collection-message',
   'as meeting time' => '.meeting-time-view',
-  'as the user nick' => '.user-irc-nick'
+  'as the user nick' => '.user-irc-nick',
+  'as voting option' => '.collection.voting-options',
+  'as voting option description' => '.voting-option-description'
 }.
 each do |within, selector|
   Then /^I should( not)? see "([^"]*)" #{within}$/ do |negation, text|

diff --git a/site/features/support/paths.rb b/site/features/support/paths.rb
index ab24b9e..6f8dad8 100644
--- a/site/features/support/paths.rb
+++ b/site/features/support/paths.rb
@@ -23,6 +23,13 @@ module NavigationHelpers
     when /the first suggested agenda page/
       agenda_item_path(AgendaItem.first(:conditions => {:agenda_id => nil}))
 
+    when /the voters page/
+      voters_path
+
+    when /the current items page/
+      current_items_path
+
+    # Add more mappings here.
     # Add more mappings here.
     # Here is an example that pulls values out of the Regexp:
     #

diff --git a/site/features/voting.feature b/site/features/voting.feature
new file mode 100644
index 0000000..2e25318
--- /dev/null
+++ b/site/features/voting.feature
@@ -0,0 +1,23 @@
+Feature:  Application side of voting
+  In order to handle voting with IRC bot
+  I want application to help with that
+
+  Scenario: Add voting option
+    Given example agenda item
+    And an agenda
+    And some council members
+    And I am logged in as council member
+    When I am on the current agenda page
+    And I follow first agenda item link
+    And I add example voting option
+    Then I should see "example" as voting option
+
+  Scenario: Edit voting option
+    Given example voting option
+    Given an agenda
+    And some council members
+    And I am logged in as council member
+    When I go from the homepage to edit last voting option page
+    And I fill in "voting_option_description" with "some description"
+    And I press "Save Voting option"
+    Then I should see "some description" as voting option description

diff --git a/site/spec/factories.rb b/site/spec/factories.rb
index 7405ee9..bc6fe8e 100644
--- a/site/spec/factories.rb
+++ b/site/spec/factories.rb
@@ -10,6 +10,17 @@ end
 
 Factory.define :agenda do |a|; end
 
-Factory.define :agenda_item do |a|; end
+Factory.define :agenda_item do |a|
+end
 
 Factory.define :participation do |p|; end
+
+Factory.define :vote do |v|;
+  v.association :voting_option
+  v.user        { users_factory(:council) }
+end
+
+Factory.define :voting_option  do |v|;
+  v.agenda_item { AgendaItem.create! }
+  v.description { "example" }
+end

diff --git a/site/spec/models/vote_spec.rb b/site/spec/models/vote_spec.rb
new file mode 100644
index 0000000..9936829
--- /dev/null
+++ b/site/spec/models/vote_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+
+describe Vote do
+  it 'should not allow anyone to create update or destroy to anyone' do
+    vote = Factory(:vote)
+    for u in users_factory(AllRoles) do
+      vote.should_not be_creatable_by(u)
+      vote.should_not be_updatable_by(u)
+      vote.should_not be_destroyable_by(u)
+    end
+  end
+
+  it 'should anyone to view' do
+    vote = Factory(:vote)
+    for u in users_factory(AllRoles) do
+      vote.should be_viewable_by(u)
+    end
+  end
+
+  it 'should allow council members to vote' do
+    for u in users_factory(:council, :council_admin) do
+      Vote.new(:user => u, :voting_option => Factory(:voting_option)).should be_valid
+    end
+  end
+
+  it 'should prevent non-council members from voting' do
+    for u in users_factory(:user, :admin) do
+      Vote.new(:user => u, :voting_option => Factory(:voting_option)).should_not be_valid
+    end
+  end
+
+  it 'should prevent users from voting multiple times' do
+    v = Factory(:vote)
+    o = Factory(:voting_option, :agenda_item => v.voting_option.agenda_item, :description => 'other option')
+    Vote.new(:user => v.user, :voting_option => o).should_not be_valid
+  end
+end

diff --git a/site/spec/models/voting_option_spec.rb b/site/spec/models/voting_option_spec.rb
new file mode 100644
index 0000000..4a41067
--- /dev/null
+++ b/site/spec/models/voting_option_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe VotingOption do
+  it 'should allow only council members to create' do
+    v = Factory(:voting_option)
+    for u in users_factory(:guest, :user, :admin)
+      v.should_not be_creatable_by(u)
+    end
+
+    for u in users_factory(:council, :council_admin)
+      v.should be_creatable_by(u)
+    end
+  end
+
+  it 'should allow only council members to update and destroy if it belongs to open agenda' do
+    v = Factory(:voting_option)
+    for u in users_factory(:guest, :user, :admin)
+      v.should_not be_updatable_by(u)
+      v.should_not be_destroyable_by(u)
+    end
+    for u in users_factory(:council, :council_admin)
+      v.should be_updatable_by(u)
+      v.should be_destroyable_by(u)
+    end
+  end
+
+  it 'should allow no one to update and destroy if it belongs to closed or archived agenda' do
+    a1 = Factory(:agenda, :state => 'closed')
+    i1 = Factory(:agenda_item, :agenda => a1)
+    v1 = Factory(:voting_option, :agenda_item => i1)
+    a2 = Factory(:agenda, :state => 'old')
+    i2 = Factory(:agenda_item, :agenda => a2)
+    v2 = Factory(:voting_option, :agenda_item => i2)
+    for u in users_factory(AllRoles)
+      v1.should_not be_updatable_by(u)
+      v1.should_not be_destroyable_by(u)
+      v2.should_not be_updatable_by(u)
+      v2.should_not be_destroyable_by(u)
+    end
+  end
+end



^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2011-06-05 20:38 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-06-05 20:37 [gentoo-commits] proj/council-webapp:master commit in: site/db/, site/features/support/, site/app/views/agenda_items/, site/features/, Petteri Räty

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox