add voting portal #7
|
@ -1,5 +1,5 @@
|
|||
class MainController < ApplicationController
|
||||
before_action :set_user_room, only: [:join, :users, :stream]
|
||||
before_action :set_user_room, only: [:join, :users, :stream, :stemmen]
|
||||
|
||||
def index
|
||||
end
|
||||
|
@ -32,10 +32,19 @@ class MainController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def stemmen
|
||||
@votes = Vote.where(room_id: @room.id, user_id: @user.id).order(created_at: :desc)
|
||||
render :votes
|
||||
end
|
||||
|
||||
private
|
||||
def set_user_room
|
||||
@user = User.find_by token: params[:token]
|
||||
@room = Room.find(@user.room_id)
|
||||
if @user.nil?
|
||||
redirect_to 'https://bij1.org/', status: :unauthorized
|
||||
else
|
||||
@room = Room.find(@user.room_id)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -130,6 +130,9 @@ class UsersController < ApplicationController
|
|||
# DELETE /users/1
|
||||
# DELETE /users/1.json
|
||||
def destroy
|
||||
Vote.where(user_id: @user.id).each do |vote|
|
||||
vote.destroy
|
||||
end
|
||||
@user.destroy
|
||||
respond_to do |format|
|
||||
format.html { redirect_to room_users_url(@user.room_id), notice: 'User was successfully destroyed.' }
|
||||
|
@ -141,6 +144,9 @@ class UsersController < ApplicationController
|
|||
# DELETE /rooms/1/users.json
|
||||
def destroy_all
|
||||
@users.each do |user|
|
||||
Vote.where(user_id: user.id).each do |vote|
|
||||
vote.destroy
|
||||
end
|
||||
user.destroy
|
||||
end
|
||||
respond_to do |format|
|
||||
|
@ -162,11 +168,12 @@ class UsersController < ApplicationController
|
|||
|
||||
require 'csv'
|
||||
|
||||
CSV.parse(users_csv, :headers => true) do |row|
|
||||
users = CSV.parse(users_csv, :headers => true).map { |row|
|
||||
fields = row.to_hash
|
||||
fields[:room_id] = room_id
|
||||
User.create!(fields)
|
||||
end
|
||||
fields
|
||||
}
|
||||
User.upsert_all(users, unique_by: [:room_id, :email])
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to room_users_url(room_id), notice: 'Users were successfully created.' }
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
class VotesController < ApplicationController
|
||||
http_basic_authenticate_with name: Rails.application.config.admin_name,
|
||||
|
||||
password: Rails.application.config.admin_password
|
||||
|
||||
before_action :set_room, only: [:index, :bulk, :destroy_for_room]
|
||||
before_action :set_votes, only: [:index, :bulk, :destroy_for_room]
|
||||
|
||||
# GET /rooms/:room_id/votes
|
||||
# GET /rooms/:room_id/votes.json
|
||||
def index
|
||||
respond_to do |format|
|
||||
format.html { render :index }
|
||||
# let's protect voter credentials
|
||||
format.json { render json: [], status: :unauthorized }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /rooms/:room_id/votes/bulk
|
||||
def bulk
|
||||
@sample = "info@bij1.org,mijn-stemming,1,abcdABCD1234"
|
||||
end
|
||||
|
||||
# POST /rooms/:room_id/votes/bulk
|
||||
# POST /rooms/:room_id/votes/bulk.json
|
||||
def create_bulk
|
||||
room_id = params[:room_id]
|
||||
votes_csv = params[:votes_csv]
|
||||
|
||||
require 'csv'
|
||||
|
||||
headers = %i[
|
||||
voter_email
|
||||
short_name
|
||||
voter_login_id
|
||||
voter_password
|
||||
]
|
||||
votes = CSV.parse(votes_csv, headers: headers).map { |row|
|
||||
csv_fields = row.to_hash
|
||||
kiara marked this conversation as resolved
joep
commented
this code probably fails if this code probably fails if `:user_id` and `:election_slug` combination already exists. catch duplicate exception.
kiara
commented
thanks! looks like your ruby already got better than mine ;), i indeed had yet to test upserts, and it does indeed turn out my db uniqueness constraint won't do here. i'll try and push an update to address this. got the code now, but seems to use upsert i should still migrate it from sqlite to postgres as well. thanks! looks like your ruby already got better than mine ;), i indeed had yet to test upserts, and it does indeed turn out my db uniqueness constraint won't do here.
i'll try and push an update to address this. got the code now, but seems to use upsert i should still migrate it from sqlite to postgres as well.
kiara
commented
i pushed some commits to address this, manually tested to verify this works now. i pushed some commits to address this, manually tested to verify this works now.
|
||||
email = csv_fields[:voter_email]
|
||||
user = User.find_by(room_id: room_id, email: email)
|
||||
{
|
||||
:room_id => room_id,
|
||||
:user_id => user.id,
|
||||
:election_slug => csv_fields[:short_name],
|
||||
:voter_login_id => csv_fields[:voter_login_id],
|
||||
:voter_password => csv_fields[:voter_password],
|
||||
}
|
||||
}
|
||||
Vote.upsert_all(votes, unique_by: [:user_id, :election_slug])
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to room_users_url(room_id), notice: 'Votes were successfully created.' }
|
||||
format.json { render :show, status: :created, location: @room }
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /rooms/:room_id/votes
|
||||
# DELETE /rooms/:room_id/votes.json
|
||||
def destroy_for_room
|
||||
@votes.each do |vote|
|
||||
vote.destroy
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to room_users_url(@room.id), notice: 'Votes were successfully destroyed.' }
|
||||
format.json { render :show, status: :created, location: @room }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
|
||||
def set_room
|
||||
@room = Room.find(params[:room_id])
|
||||
end
|
||||
|
||||
def set_votes
|
||||
@votes = Vote.where(room_id: @room.id)
|
||||
end
|
||||
|
||||
end
|
|
@ -6,4 +6,7 @@ class User < ApplicationRecord
|
|||
attribute :invited, :boolean, default: false
|
||||
attribute :vote, :boolean, default: true
|
||||
attribute :proxy, :boolean, default: false
|
||||
|
||||
validates :email, uniqueness: { scope: :room_id }
|
||||
validates :room_id, uniqueness: { scope: :email }
|
||||
end
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
class Vote < ApplicationRecord
|
||||
belongs_to :user
|
||||
|
||||
validates :election_slug, uniqueness: { scope: :user_id }
|
||||
validates :user_id, uniqueness: { scope: :election_slug }
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
<p id="notice"><%= notice %></p>
|
||||
|
||||
<h1>Stemmingen</h1>
|
||||
|
||||
<ul>
|
||||
<% @votes.each do |vote| %>
|
||||
<li>
|
||||
<a href="https://stemmen.bij1.org/helios/e/<%= vote.election_slug %>/vote#id_voter_id=<%= vote.voter_login_id %>&id_password=<%= vote.voter_password %>">
|
||||
<%= vote.election_slug %>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
|
||||
<%= link_to 'Stream', user_stream_path %> |
|
||||
<%= link_to 'Inbellen', join_room_path %> via BigBlueButton (voor toelichtingen)
|
|
@ -32,5 +32,7 @@
|
|||
|
||||
<%= link_to 'Show Users', room_users_path(@room) %> |
|
||||
<%= link_to 'Edit', edit_room_path(@room) %> |
|
||||
<%= link_to 'Import Users', bulk_new_room_users_path(@room), method: :get %> |
|
||||
<%= link_to 'Import Votes', bulk_new_room_votes_path(@room), method: :get %> |
|
||||
<%= link_to 'Present Users', room_present_users_path(@room) %> |
|
||||
<%= link_to 'Back', rooms_path %>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<h1>Bulk Users: <%= @room.name %></h1>
|
||||
|
||||
<p>
|
||||
csv formaat voor bulk import: email,name,moderator,vote,proxy,invited,presence. kolom volgorde maakt niet uit. komma-separated met header.
|
||||
csv formaat voor bulk import: <code>email,name,moderator,vote,proxy,invited,presence</code>. kolom volgorde maakt niet uit. komma-separated met header.
|
||||
<% if false %>
|
||||
download een sample <%= link_to "hier", "/bbb.csv" %>.
|
||||
<% end %>
|
||||
|
|
|
@ -50,4 +50,6 @@
|
|||
<%= link_to 'Present Users', room_present_users_path(id: params[:room_id], v: 1) %> |
|
||||
<%= link_to 'Absent Users', room_present_users_path(id: params[:room_id], v: 0) %> |
|
||||
<%= link_to 'Destroy all', destroy_room_users_path(params[:room_id]), method: :delete %> |
|
||||
<%= link_to 'Import Votes', bulk_new_room_votes_path(params[:room_id]), method: :get %> |
|
||||
<%= link_to 'Destroy Votes', room_votes_path(params[:room_id]), method: :delete %> |
|
||||
<%= link_to 'Back', room_path(params[:room_id]) %>
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<h1>Bulk Votes: <%= @room.name %></h1>
|
||||
|
||||
<p>
|
||||
csv formaat voor bulk import, in die volgorde, komma-separated en zonder header: <code>voter_email,short_name,voter_login_id,voter_password</code>
|
||||
<% if false %>
|
||||
download een sample <%= link_to "hier", "/votes.csv" %>.
|
||||
<% end %>
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li><code>voter_email</code> is het email adres van de stemmer, om de credentials terug te koppelen aan het juiste lid.</li>
|
||||
<li><code>short_name</code> is de naam van de stemming</li>
|
||||
<li><code>voter_login_id</code> is de login id van de stemmer in het stem systeem</li>
|
||||
<li><code>voter_password</code> is het wachtwoord van de stemmer in het stem systeem</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
De gegevens hiervoor zijn te verkrijgen via commando, gegeven SSH toegang tot de database server:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>ssh db.bij1.net "sudo psql -U postgres helios -c \"SELECT voter_email, short_name, voter_login_id, voter_password FROM helios_voter JOIN helios_election ON election_id = helios_election.id WHERE helios_election.uuid IN ('UUID1', 'UUID2', ...);\" -tAF,"</code>
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
... waar <code>'UUID1', 'UUID2', ...</code> vervangen dient te worden door de UUIDs van de stemmingen van deze kamer, in de URLs te vinden in <a href="https://stemmen.bij1.org/helios/elections/administered">Helios</a>.
|
||||
</p>
|
||||
<p>
|
||||
Gebruikers vinden vervolgens hun stem links op de volgende URL, waar <code>TOKEN</code> dient te worden vervangen door hun Ingang token:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>https://vergadering.bij1.org/ingang/TOKEN/stemmen</code>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<%= form_with(local: true) do |form| %>
|
||||
<div class="field">
|
||||
<%= form.text_area :votes_csv, value: @sample %>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<%= form.submit %>
|
||||
</div>
|
||||
<%= link_to 'Back', room_users_path %>
|
||||
<% end %>
|
|
@ -0,0 +1,29 @@
|
|||
<p id="notice"><%= notice %></p>
|
||||
|
||||
<h1>Votes: <%= @room.name %></h1>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>User ID</th>
|
||||
<th>Election Slug</th>
|
||||
<th>Login ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<% @votes.each do |vote| %>
|
||||
<tr>
|
||||
<td><%= vote.id %></td>
|
||||
<td><%= vote.user_id %></td>
|
||||
<td><%= vote.election_slug %></td>
|
||||
<td><%= vote.voter_login_id %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<br>
|
||||
|
||||
<%= link_to 'Back', room_path(@room) %>
|
|
@ -1,4 +1,8 @@
|
|||
RAILS_LOG_TO_STDOUT: 'any value will do, it just cares that this env var is present'
|
||||
SMTP_USERNAME: ""
|
||||
SMTP_PASSWORD: ""
|
||||
INGANG_ADMIN_PASSWORD: ""
|
||||
BIGBLUEBUTTON_SECRET: ""
|
||||
DATABASE_PASSWORD_PRODUCTION: ""
|
||||
DATABASE_PASSWORD_TEST: ""
|
||||
DATABASE_PASSWORD_DEV: ""
|
||||
|
|
|
@ -1,35 +1,29 @@
|
|||
# SQLite. Versions 3.8.0 and up are supported.
|
||||
# gem install sqlite3
|
||||
#
|
||||
# Ensure the SQLite 3 gem is defined in your Gemfile
|
||||
# gem 'sqlite3'
|
||||
#
|
||||
default: &default
|
||||
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
||||
timeout: 5000
|
||||
# upsert does not work with sqlite
|
||||
adapter: postgresql
|
||||
encoding: unicode
|
||||
host: db.internal.bij1.net
|
||||
port: 5432
|
||||
|
||||
development:
|
||||
<<: *default
|
||||
adapter: sqlite3
|
||||
database: db/development.sqlite3
|
||||
database: ingang_dev
|
||||
username: ingang_dev
|
||||
password: <%= ENV.fetch("DATABASE_PASSWORD_DEV") { } %>
|
||||
|
||||
# Warning: The database defined as "test" will be erased and
|
||||
# re-generated from your development database when you run "rake".
|
||||
# Do not set this db to the same as development or production.
|
||||
test:
|
||||
<<: *default
|
||||
adapter: sqlite3
|
||||
database: db/test.sqlite3
|
||||
database: ingang_test
|
||||
username: ingang_test
|
||||
password: <%= ENV.fetch("DATABASE_PASSWORD_TEST") { } %>
|
||||
|
||||
production:
|
||||
<<: *default
|
||||
adapter: sqlite3
|
||||
database: db/production.sqlite3
|
||||
# adapter: postgresql
|
||||
# encoding: unicode
|
||||
# database: ingang
|
||||
# username: ingang
|
||||
# password: <%= ENV.fetch("DATABASE_PASSWORD") { } %>
|
||||
# host: db.internal.bij1.net
|
||||
# port: 5432
|
||||
|
||||
database: ingang
|
||||
username: ingang
|
||||
password: <%= ENV.fetch("DATABASE_PASSWORD_PRODUCTION") { } %>
|
||||
|
|
|
@ -1,18 +1,24 @@
|
|||
Rails.application.routes.draw do
|
||||
resources :rooms do
|
||||
resources :users, shallow: true
|
||||
resources :rooms, shallow: true do
|
||||
resources :users do
|
||||
# resources :votes
|
||||
end
|
||||
end
|
||||
get 'rooms/:room_id/users/bulk', to: 'users#bulk', as: 'bulk_new_room_users'
|
||||
post 'rooms/:room_id/users/bulk', to: 'users#create_bulk', as: 'bulk_create_room_users'
|
||||
post 'rooms/:room_id/users/:id/test_invite', to: 'users#test_invite', as: 'test_invite_user'
|
||||
post 'rooms/:room_id/users/invite', to: 'users#invite', as: 'invite_room_users'
|
||||
delete 'rooms/:room_id/users', to: 'users#destroy_all', as: 'destroy_room_users'
|
||||
delete 'rooms/:room_id/votes', to: 'votes#destroy_for_room', as: 'room_votes'
|
||||
delete 'rooms/:room_id/users/invite', to: 'users#uninvite', as: 'uninvite_room_users'
|
||||
post 'rooms/:room_id/users/mark_invited', to: 'users#mark_invited', as: 'mark_invited_room_users'
|
||||
post 'rooms/:room_id/users/mark_presence', to: 'users#mark_presence', as: 'mark_presence_room_users'
|
||||
get 'rooms/:room_id/votes/bulk', to: 'votes#bulk', as: 'bulk_new_room_votes'
|
||||
post 'rooms/:room_id/votes/bulk', to: 'votes#create_bulk', as: 'bulk_create_room_votes'
|
||||
get 'rooms/:id/voters.csv', to: 'rooms#voters', as: 'room_export_voters'
|
||||
get 'rooms/:id/aanwezig.csv', to: 'rooms#present', as: 'room_present_users'
|
||||
get ':token/stream', to: 'main#stream'
|
||||
get ':token/stemmen', to: 'main#stemmen', as: 'user_elections'
|
||||
get ':token/stream', to: 'main#stream', as: 'user_stream'
|
||||
get ':token', to: 'main#join', as: 'join_room'
|
||||
root 'main#index'
|
||||
end
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
class CreateVotes < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
create_table :votes do |t|
|
||||
t.references :room, null: false, foreign_key: true
|
||||
t.references :user, null: false, foreign_key: true
|
||||
t.string :election_slug
|
||||
t.string :voter_login_id
|
||||
t.string :voter_password
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
change_column_default :votes, :created_at, from: nil, to: ->{ 'now()' }
|
||||
change_column_default :votes, :updated_at, from: nil, to: ->{ 'now()' }
|
||||
add_index :votes, [:user_id, :election_slug], unique: true
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
class UpsertableUsers < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
change_column_default :users, :created_at, from: nil, to: ->{ 'now()' }
|
||||
change_column_default :users, :updated_at, from: nil, to: ->{ 'now()' }
|
||||
add_index :users, [:room_id, :email], unique: true
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
info@bij1.org,mijn-stemming,1,abcdABCD1234
|
|
|
@ -0,0 +1,17 @@
|
|||
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
|
||||
|
||||
one:
|
||||
id:
|
||||
room_id: 1
|
||||
user_id: 1
|
||||
election_slug: MyString
|
||||
voter_login_id: MyString
|
||||
voter_password: MyString
|
||||
|
||||
two:
|
||||
id:
|
||||
room_id: 1
|
||||
user_id: 1
|
||||
election_slug: MyString
|
||||
voter_login_id: MyString
|
||||
voter_password: MyString
|
|
@ -0,0 +1,7 @@
|
|||
require 'test_helper'
|
||||
|
||||
class VoteTest < ActiveSupport::TestCase
|
||||
# test "the truth" do
|
||||
# assert true
|
||||
# end
|
||||
end
|
Loading…
Reference in New Issue
opmerking voor later: niet heel handig dat alle admins met hetzelfde account inloggen. hierdoor kan je niet bijhouden wie welke handeling heeft verricht..
eens, is wat het resultaat geweest van meer projecten dan mensen.
de data in dit project is van zeer tijdelijke aard tho, en verwachting tot nu toe is geweest dat deze ivm AVG na de dag van de vergadering weer verwijderd wordt.