Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Citizen science #381

Merged
merged 11 commits into from
Mar 13, 2019
17 changes: 16 additions & 1 deletion app/controllers/errors_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class ErrorsController < ApplicationController

skip_authorization_check only: [:route_error, :uncaught_error, :test_exceptions, :show]
skip_authorization_check only: [:route_error, :uncaught_error, :test_exceptions, :show, :method_not_allowed_error]

# see application_controller.rb for error handling for specific exceptions.
# see routes.rb for the catch-all route for routing errors.
Expand Down Expand Up @@ -30,6 +30,9 @@ def show
when 404, '404', 'not_found'
status_symbol = :not_found
detail_message = 'Could not find the requested page.'
when 405, '405', 'method_not_allowed'
status_symbol = :method_not_allowed
detail_message = 'HTTP method not allowed for this resource.'
when 406, '406', 'not_acceptable'
status_symbol = :not_acceptable
detail_message = 'We cold not provide the format you asked for. Perhaps try a different file extension?'
Expand Down Expand Up @@ -71,6 +74,18 @@ def route_error

end

def method_not_allowed_error

render_error(
:method_not_allowed,
'HTTP method not allowed for this resource.',
nil,
'method_not_allowed_error',
{error_info: {original_route: params[:requested_route], original_http_method: request.method}}
)

end


def uncaught_error

Expand Down
104 changes: 104 additions & 0 deletions app/controllers/questions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
class QuestionsController < ApplicationController

include Api::ControllerHelper

#skip_authorization_check

# GET /questions
# GET /studies/:study_id/questions
def index
do_authorize_class

query = Question.all

if params[:study_id]
# todo:
# check if this can be done better. We shouln't need to join
# all the way to study, only to the join table.
query = query.belonging_to_study(params[:study_id])
end

@questions, opts = Settings.api_response.response_advanced(
api_filter_params,
query,
Question,
Question.filter_settings
)
respond_index(opts)
end

# GET /questions/:id
def show
do_load_resource
do_authorize_instance
respond_show
end

# GET /questions/filter
def filter
do_authorize_class

filter_response, opts = Settings.api_response.response_advanced(
api_filter_params,
Question.all,
Question,
Question.filter_settings
)
respond_filter(filter_response, opts)
end

# GET /questions/new
def new
do_new_resource
do_set_attributes
do_authorize_instance
respond_show
end

# POST /questions
def create
do_new_resource
do_set_attributes(question_params)
do_authorize_instance

if @question.save
respond_create_success(question_path(@question))
else
respond_change_fail
end
end

# PUT /questions/:id
def update
do_load_resource
do_authorize_instance

if @question.update_attributes(question_params)
respond_show
else
respond_change_fail
end
end

# DELETE /questions/:id
def destroy
do_load_resource
do_authorize_instance
@question.destroy
respond_destroy
end

private

def question_params
# empty array is replaced with nil by rails. Revert to empty array
# to avoid errors with strong parameters
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add in that link to the rails issue that describes this problem and the recommended work around?

# https://github.com/rails/rails/issues/13766
if params.has_key?(:question) and params[:question].has_key?(:study_ids) and params[:question][:study_ids].nil?
params[:question][:study_ids] = []
end
permitted = [{study_ids: []}, :text, :data]
params.require(:question).permit(permitted)
end

end
77 changes: 77 additions & 0 deletions app/controllers/responses_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
class ResponsesController < ApplicationController

include Api::ControllerHelper

# GET /responses
# GET /studies/:study_id/responses
def index
do_authorize_class

@responses, opts = Settings.api_response.response_advanced(
api_filter_params,
Access::ByPermission.responses(current_user, params[:study_id]),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if params[:study_id] is nil is that a problem?

You special-cased it in the Questions controller

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was only a problem in questions because it was an array (due to HABTM).

Response,
Response.filter_settings
)
respond_index(opts)
end

# GET /responses/:id
def show
do_load_resource
do_authorize_instance
respond_show
end

# GET /responses/filter
def filter
do_authorize_class

filter_response, opts = Settings.api_response.response_advanced(
api_filter_params,
Access::ByPermission.responses(current_user, nil),
Response,
Response.filter_settings
)
respond_filter(filter_response, opts)
end

# GET /responses/new
def new
do_new_resource
do_set_attributes
do_authorize_instance
respond_show
end

# POST /responses
# POST /study/:study_id/questions/:question_id/responses
def create
do_new_resource
do_set_attributes(response_params)
do_authorize_instance

if @response.save
respond_create_success(response_path(@response))
else
respond_change_fail
end
end

# DELETE /responses/:id
def destroy
do_load_resource
do_authorize_instance
@response.destroy
respond_destroy
end


private

def response_params
params.require(:response).permit(:study_id, :question_id, :dataset_item_id, :text, :data)
end


end
88 changes: 88 additions & 0 deletions app/controllers/studies_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
class StudiesController < ApplicationController

include Api::ControllerHelper

# GET /studies
def index
do_authorize_class

@studies, opts = Settings.api_response.response_advanced(
api_filter_params,
Study.all,
Study,
Study.filter_settings
)
respond_index(opts)
end

# GET /studies/:id
def show
do_load_resource
do_authorize_instance
respond_show
end

# GET /studies/filter
def filter
do_authorize_class

filter_response, opts = Settings.api_response.response_advanced(
api_filter_params,
Study.all,
Study,
Study.filter_settings
)
respond_filter(filter_response, opts)
end

def new
do_new_resource
do_set_attributes
do_authorize_instance

respond_show
end

# POST /studies/
def create
do_new_resource
do_set_attributes(study_params)
do_authorize_instance

if @study.save
respond_create_success(study_path(@study))
else
respond_change_fail
end
end

# PUT /studies/:id
def update
do_load_resource
do_authorize_instance

if @study.update_attributes(study_params)
respond_show
else
respond_change_fail
end
end

# DELETE /studies/:id
def destroy
do_load_resource
do_authorize_instance
@study.destroy
respond_destroy
end

private

def study_params
# params[:study] = params[:study] || {}
# params[:study][:dataset_id] = params[:dataset_id]
params.require(:study).permit(:dataset_id, :name)
end


end
47 changes: 47 additions & 0 deletions app/models/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ def initialize(user)
to_error(user, is_guest)
to_public(user, is_guest)

to_study(user, is_guest)
to_question(user, is_guest)
to_response(user, is_guest)

else
fail ArgumentError, "Permissions are not defined for user '#{user.id}': #{user.role_symbols}"

Expand Down Expand Up @@ -637,4 +641,47 @@ def to_public(user, is_guest)
], :public
end

def to_study(user, is_guest)

# only admin can create, update, delete

# all users including guest can access any get request
can [:new, :index, :filter, :show], Study

end

def to_question(user, is_guest)

can [:new], Question

# only admin create, update, delete

# only logged in users can view questions
can [:index, :filter, :show], Question unless is_guest

end

def to_response(user, is_guest)

can [:new], Response

# must have read permission on dataset item to create a response for it
can [:create], Response do |response|
check_model(response)
if response.dataset_item
Access::Core.can_any?(user, :reader, response.dataset_item.audio_recording.site.projects)
else
false
end
end

# users can only view their own responses
# therefore guest can not view any responses
can [:index, :filter, :show], Response, creator_id: user.id unless is_guest

# only admin can update or delete responses

end


end
2 changes: 2 additions & 0 deletions app/models/dataset.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class Dataset < ActiveRecord::Base
belongs_to :creator, class_name: 'User', foreign_key: :creator_id, inverse_of: :created_datasets
belongs_to :updater, class_name: 'User', foreign_key: :updater_id, inverse_of: :updated_datasets
has_many :dataset_items
has_many :study

# We have not enabled soft deletes yet since we do not support deleting datasets
# This may change in the future
Expand All @@ -23,6 +24,7 @@ class Dataset < ActiveRecord::Base
# This will potentially be hit very often, maybe multiple times per request
# and therefore is a possible avenue for future optimization if necessary
def self.default_dataset_id
# note: this may cause db:create and db:migrate to fail
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why? can we code around this edge case?

Dataset.where(name: 'default').first.id
end

Expand Down
4 changes: 1 addition & 3 deletions app/models/dataset_item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ class DatasetItem < ActiveRecord::Base
belongs_to :audio_recording, inverse_of: :dataset_items
belongs_to :creator, class_name: 'User', foreign_key: :creator_id, inverse_of: :created_dataset_items
has_many :progress_events, inverse_of: :dataset_item, dependent: :destroy

# We have not enabled soft deletes yet since we do not support deleting dataset items
# This may change in the future
has_many :responses, dependent: :destroy

# association validations
validates :dataset, existence: true
Expand Down
Loading