diff --git a/Gemfile b/Gemfile index faf2a363..25b56592 100644 --- a/Gemfile +++ b/Gemfile @@ -55,7 +55,6 @@ group :deploy do gem 'capistrano-bundler', require: false gem 'capistrano-rails' gem 'capistrano-rvm', require: false - gem 'whenever' gem 'httparty' end diff --git a/Gemfile.lock b/Gemfile.lock index 8ecb7da5..1bd24db6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -16,7 +16,7 @@ GIT GEM remote: http://rubygems.org/ specs: - RedCloth (4.3.2) + RedCloth (4.3.3) actioncable (6.1.7.6) actionpack (= 6.1.7.6) activesupport (= 6.1.7.6) @@ -81,12 +81,12 @@ GEM minitest (>= 5.1) tzinfo (~> 2.0) zeitwerk (~> 2.3) - addressable (2.8.5) + addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) airbrussh (1.5.0) sshkit (>= 1.6.1, != 1.7.0) awesome_print (1.9.2) - bcrypt (3.1.19) + bcrypt (3.1.20) bcrypt_pbkdf (1.1.0) better_errors (2.10.1) erubi (>= 1.0.0) @@ -96,11 +96,11 @@ GEM debug_inspector (>= 0.0.1) bluecloth (2.2.0) builder (3.2.4) - bullet (7.1.2) + bullet (7.1.4) activesupport (>= 3.0.0) uniform_notifier (~> 1.11) byebug (11.1.3) - capistrano (3.17.3) + capistrano (3.18.0) airbrussh (>= 1.0.0) i18n rake (>= 10.0.0) @@ -144,7 +144,7 @@ GEM activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) - date (3.3.3) + date (3.3.4) debug_inspector (1.1.0) devise (4.9.3) bcrypt (~> 3.0) @@ -165,7 +165,7 @@ GEM actionmailer (>= 5.2, < 8) activesupport (>= 5.2, < 8) execjs (2.7.0) - factory_bot (6.3.0) + factory_bot (6.4.2) activesupport (>= 5.0.0) ffaker (2.23.0) ffi (1.16.3) @@ -188,7 +188,7 @@ GEM rspec (>= 2.99.0, < 4.0) gutentag (2.6.2) activerecord (>= 3.2.0) - haml (6.2.3) + haml (6.3.0) temple (>= 0.8.2) thor tilt @@ -230,10 +230,10 @@ GEM listen (3.8.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - loofah (2.21.4) + loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) - lumberjack (1.2.9) + lumberjack (1.2.10) mail (2.8.1) mini_mime (>= 0.1.1) net-imap @@ -243,31 +243,31 @@ GEM matrix (0.4.2) method_source (1.0.0) mini_mime (1.1.5) - mini_portile2 (2.8.4) + mini_portile2 (2.8.5) mini_racer (0.8.0) libv8-node (~> 18.16.0.0) minitest (5.20.0) multi_xml (0.6.0) nenv (0.3.0) - net-imap (0.4.1) + net-imap (0.4.7) date net-protocol net-pop (0.1.2) net-protocol - net-protocol (0.2.1) + net-protocol (0.2.2) timeout net-scp (4.0.0) net-ssh (>= 2.6.5, < 8.0.0) net-smtp (0.4.0) net-protocol net-ssh (7.2.0) - nio4r (2.5.9) - nokogiri (1.15.4) + nio4r (2.7.0) + nokogiri (1.15.5) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.15.4-x86_64-darwin) + nokogiri (1.15.5-x86_64-darwin) racc (~> 1.4) - nokogiri (1.15.4-x86_64-linux) + nokogiri (1.15.5-x86_64-linux) racc (~> 1.4) notiffany (0.1.3) nenv (~> 0.1) @@ -281,10 +281,10 @@ GEM byebug (~> 11.0) pry (>= 0.13, < 0.15) psych (3.3.4) - public_suffix (5.0.3) + public_suffix (5.0.4) puma (6.4.0) nio4r (~> 2.0) - racc (1.7.1) + racc (1.7.3) rack (2.2.8) rack-cors (2.0.1) rack (>= 2.0.0) @@ -324,20 +324,20 @@ GEM method_source rake (>= 12.2) thor (~> 1.0) - rake (13.0.6) + rake (13.1.0) rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) - regexp_parser (2.8.2) + regexp_parser (2.8.3) responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) - rouge (4.1.3) + rouge (4.2.0) rspec (3.12.0) rspec-core (~> 3.12.0) rspec-expectations (~> 3.12.0) rspec-mocks (~> 3.12.0) - rspec-activemodel-mocks (1.1.0) + rspec-activemodel-mocks (1.2.0) activemodel (>= 3.0) activesupport (>= 3.0) rspec-mocks (>= 2.99, < 4.0) @@ -349,7 +349,7 @@ GEM rspec-mocks (3.12.6) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) - rspec-rails (6.0.3) + rspec-rails (6.1.0) actionpack (>= 6.1) activesupport (>= 6.1) railties (>= 6.1) @@ -368,10 +368,10 @@ GEM sprockets (> 3.0) sprockets-rails tilt - sentry-rails (5.12.0) + sentry-rails (5.15.0) railties (>= 5.0) - sentry-ruby (~> 5.12.0) - sentry-ruby (5.12.0) + sentry-ruby (~> 5.15.0) + sentry-ruby (5.15.0) concurrent-ruby (~> 1.0, >= 1.0.2) shellany (0.0.1) shoulda-matchers (5.3.0) @@ -389,7 +389,7 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) - sshkit (1.21.5) + sshkit (1.21.6) net-scp (>= 1.1.2) net-ssh (>= 2.8.0) temple (0.10.3) @@ -397,10 +397,10 @@ GEM daemons (~> 1.0, >= 1.0.9) eventmachine (~> 1.0, >= 1.0.4) rack (>= 1, < 3) - thor (1.2.2) + thor (1.3.0) tilt (2.3.0) timecop (0.9.8) - timeout (0.4.0) + timeout (0.4.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) uglifier (4.2.0) @@ -412,8 +412,6 @@ GEM websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - whenever (1.0.0) - chronic (>= 0.6.3) wirble (0.1.3) xpath (3.2.0) nokogiri (~> 1.8) @@ -485,7 +483,6 @@ DEPENDENCIES timecop uglifier uuidtools - whenever wirble zeitwerk (< 2.6.4) diff --git a/app/assets/javascripts/credence.js b/app/assets/javascripts/credence.js deleted file mode 100644 index e848c2b8..00000000 --- a/app/assets/javascripts/credence.js +++ /dev/null @@ -1,7 +0,0 @@ -$(document).ready(function () { - $('#credence-graph-container a').click(function (event) { - $('#credence-graph').show(); - $('#credence-graph-container a').hide(); - return false; - }); -}); diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 74e3a871..372b83e8 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -8,7 +8,6 @@ *= require predictions *= require footer *= require facebox - *= require credence */ /* General Page Structure diff --git a/app/assets/stylesheets/credence.css b/app/assets/stylesheets/credence.css deleted file mode 100644 index 9678c111..00000000 --- a/app/assets/stylesheets/credence.css +++ /dev/null @@ -1,20 +0,0 @@ -.credence-result strong { font-size: 1.5em; padding-right: 3px; } - -#credence-scores { margin-bottom: 10px; } - -#credence-scores td { - text-align: center; - font-size: 2em; - font-weight: bold; -} - -#credence-scores th { - padding: 1px 5px; - vertical-align: top; - font-weight: normal; - line-height: 120%; -} - -#credence-graph:not(.show) { - display: none; -} diff --git a/app/controllers/api/prediction_group_by_description_controller.rb b/app/controllers/api/prediction_group_by_description_controller.rb deleted file mode 100644 index b1f629eb..00000000 --- a/app/controllers/api/prediction_group_by_description_controller.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -module Api - class PredictionGroupByDescriptionController < PredictionGroupsController - protected - - def find_prediction_group - scope = PredictionGroup.includes(predictions: %i[responses versions judgements]) - scope.find_or_initialize_by(description: params[:id]) - end - end -end diff --git a/app/controllers/api/prediction_groups_controller.rb b/app/controllers/api/prediction_groups_controller.rb index 14bdea3c..1c491521 100644 --- a/app/controllers/api/prediction_groups_controller.rb +++ b/app/controllers/api/prediction_groups_controller.rb @@ -5,20 +5,8 @@ class PredictionGroupsController < AuthorisedController MAXIMUM_PREDICTION_GROUPS_LIMIT = 1000 DEFAULT_PREDICTION_GROUPS_LIMIT = 100 - before_action :find_prediction_group, only: %i[show update] + before_action :find_prediction_group, only: %i[show] before_action :authorize_to_see_prediction_group, only: [:show] - before_action :authorize_to_update_prediction_group, only: [:update] - - def create - prediction_group = UpdatedPredictionGroup.new(PredictionGroup.new, - @user, - prediction_group_params).prediction_group - if prediction_group.save - render json: prediction_group - else - render json: prediction_group.errors, status: :unprocessable_entity - end - end def index render json: build_prediction_groups @@ -28,17 +16,6 @@ def show render json: find_prediction_group end - def update - prediction_group = UpdatedPredictionGroup.new(find_prediction_group, - @user, - prediction_group_params).prediction_group - if prediction_group.save - render json: prediction_group - else - render json: prediction_group.errors, status: :unprocessable_entity - end - end - protected def find_prediction_group @@ -55,13 +32,6 @@ def authorize_to_see_prediction_group @user.authorized_for?(prediction)) end - def authorize_to_update_prediction_group - prediction_group = find_prediction_group - prediction = prediction_group.try(:predictions).try(:first) - return if prediction.nil? - raise UnauthorizedRequest unless @user.authorized_for?(prediction) - end - def build_prediction_groups limit = params[:limit].to_i out_of_range = (1..MAXIMUM_PREDICTION_GROUPS_LIMIT).cover?(limit) @@ -74,9 +44,5 @@ def build_prediction_groups .order(id: :desc) .limit(limit) end - - def prediction_group_params - params.require(:prediction_group).permit! - end end end diff --git a/app/controllers/api/prediction_judgements_controller.rb b/app/controllers/api/prediction_judgements_controller.rb deleted file mode 100644 index 3155eca2..00000000 --- a/app/controllers/api/prediction_judgements_controller.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -module Api - class PredictionJudgementsController < AuthorisedController - def create - @prediction = Prediction.find(params[:prediction_id]) - raise UnauthorizedRequest unless @user.authorized_for?(@prediction) - - @prediction.judge!(params[:outcome], @user) - render status: :created, json: @prediction.judgements.last - end - end -end diff --git a/app/controllers/api/predictions_controller.rb b/app/controllers/api/predictions_controller.rb index 20de142d..4dfee96e 100644 --- a/app/controllers/api/predictions_controller.rb +++ b/app/controllers/api/predictions_controller.rb @@ -6,18 +6,8 @@ class PredictionsController < AuthorisedController DEFAULT_PREDICTIONS_LIMIT = 100 before_action :build_predictions, only: [:index] - before_action :build_new_prediction, only: [:create] - before_action :find_prediction, only: %i[show update] + before_action :find_prediction, only: %i[show] before_action :authorize_to_see_prediction, only: [:show] - before_action :authorize_to_update_prediction, only: [:update] - - def create - if @prediction.save - render json: @prediction - else - render json: @prediction.errors, status: :unprocessable_entity - end - end def index render json: @predictions @@ -27,14 +17,6 @@ def show render json: @prediction end - def update - if @prediction.update(prediction_params) - render json: @prediction - else - render json: @prediction.errors, status: :unprocessable_entity - end - end - private def authorize_to_see_prediction @@ -42,16 +24,6 @@ def authorize_to_see_prediction @user.authorized_for?(@prediction) end - def authorize_to_update_prediction - raise UnauthorizedRequest unless @user.authorized_for?(@prediction) - end - - def build_new_prediction - permitted_params = prediction_params - permitted_params[:visibility] ||= @user.try(:visibility_default) - @prediction = Prediction.new(permitted_params.merge(creator: @user)) - end - def build_predictions limit = params[:limit].to_i limit = DEFAULT_PREDICTIONS_LIMIT unless (1..MAXIMUM_PREDICTIONS_LIMIT).cover?(limit) @@ -72,15 +44,5 @@ def build_predictions def find_prediction @prediction = Prediction.find(params[:id]) end - - def prediction_params - permitted_params = params.require(:prediction).permit! - # Handle previous version of the API that uses a private flag instead of visibility - if permitted_params[:private].present? - private_value = permitted_params.delete(:private) - permitted_params[:visibility] = 'visible_to_creator' if private_value - end - permitted_params - end end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0c2a2801..ec231f43 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -10,18 +10,13 @@ class UnauthorizedRequest < StandardError helper :all # include all helpers, all the time - before_action :set_timezone, :clear_return_to, :login_via_token + before_action :set_timezone, :clear_return_to before_action :configure_permitted_parameters, if: :devise_controller? - before_action :force_change_password protect_from_forgery with: :exception, prepend: true protected - def handle_unauthorised_request - render json: unauthorized_user_message, status: :unauthorized - end - def configure_permitted_parameters added_attrs = %i[login email password password_confirmation remember_me] devise_parameter_sanitizer.permit :sign_up, keys: added_attrs @@ -30,21 +25,6 @@ def configure_permitted_parameters private - def force_change_password - return unless current_user.present? && request.format.html? - - notice = 'PredictionBook has recently undergone a major upgrade. As part of the upgrade, our ' \ - 'authentication system has changed. We are currently transitioning users across to ' \ - 'use the new authentication system. You will need to change your password and ' \ - 'provide an email address if you have not already.' - force_pwd_change = !current_user.devise_password_specified? - redirecting = request.path == edit_user_registration_path - updating_password = request.path == '/users' && params['_method'] == 'put' - logging_out = request.path == destroy_user_session_path - redirect_to(edit_user_registration_path, notice: notice) if force_pwd_change && !redirecting && - !updating_password && !logging_out - end - def set_timezone user_timezone = current_user.try(:timezone) Time.zone = user_timezone.presence || 'UTC' @@ -54,19 +34,4 @@ def set_timezone def clear_return_to session[:return_to] = nil end - - def login_via_token - token = params[:token] - if token.present? - DeadlineNotification.use_token!(token) { |dn| @current_user = dn.user } - redirect_to # get rid of token in url so if it is copied and pasted it's not propagated - end - end - - def unauthorized_user_message - { - error: 'user is unauthorized to view/edit this private prediction', - status: :unauthorized - } - end end diff --git a/app/controllers/credence_game_responses_controller.rb b/app/controllers/credence_game_responses_controller.rb deleted file mode 100644 index 1c0252a6..00000000 --- a/app/controllers/credence_game_responses_controller.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -class CredenceGameResponsesController < ApplicationController - before_action :authenticate_user! - before_action :set_response - before_action :check_response_for_current_user - before_action :set_game - - def update - correct, score = @response.score_answer - if @game.current_response_id == @response.id - if @response.save - @game.add_score(score) - else - flash[:error] = @response.errors.full_messages.join(', ') - end - end - flash.merge!(correct: correct, score: score, message: @response.answer_message) - redirect_to credence_game_path('try') - end - - private - - def set_response - @response = CredenceGameResponse.find(params[:id]) - @response.assign_attributes(response_params) - end - - def check_response_for_current_user - head :forbidden if @response.credence_game.user_id != current_user.id - end - - def response_params - params - .require(:response) - .permit(:answer_credence, :given_answer) - .merge(answered_at: Time.zone.now) - end - - def set_game - @game = current_user.credence_game - end -end diff --git a/app/controllers/credence_games_controller.rb b/app/controllers/credence_games_controller.rb deleted file mode 100644 index 017fe478..00000000 --- a/app/controllers/credence_games_controller.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -class CredenceGamesController < ApplicationController - before_action :authenticate_user! - - def show - @title = 'Credence game' - - return unless CredenceQuestion.exists? - - @game = CredenceGame.find_or_create_by(user_id: current_user.id) - @response = @game.current_response - num_answered = @game.num_answered - @show_graph = num_answered > 10 && (num_answered % 10).zero? - end - - def destroy - CredenceGame.find(params[:id]).destroy - redirect_to credence_game_path('try') - end -end diff --git a/app/controllers/deadline_notifications_controller.rb b/app/controllers/deadline_notifications_controller.rb deleted file mode 100644 index 890aa6f4..00000000 --- a/app/controllers/deadline_notifications_controller.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -class DeadlineNotificationsController < NotificationsController - def index - id_param = UserLogin.new(params[:user_id]).to_s - @user = User.find_by(login: id_param) || User.find_by(id: id_param) - - @pending = @user.deadline_notifications.sendable.sort - @waiting = @user.deadline_notifications.unsent.enabled.unknown.sort - @known = @user.deadline_notifications.unsent.enabled.known.sort_by(&:judged_at) - @sent = @user.deadline_notifications.sent.sort_by(&:deadline) - end - - def notification_type - DeadlineNotification - end -end diff --git a/app/controllers/group_member_invitations_controller.rb b/app/controllers/group_member_invitations_controller.rb deleted file mode 100644 index bee3fe0f..00000000 --- a/app/controllers/group_member_invitations_controller.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -class GroupMemberInvitationsController < ApplicationController - def show - group_member = GroupMember.includes(:group).find_by(uuid: params[:id]) - if group_member.nil? || !group_member.invitee? - redirect_to(root_url) - return - end - group_member.update(role: 'contributor') - group = group_member.group - redirect_to(root_url, notice: "You are now a contributing member of the #{group.name} group") - end -end diff --git a/app/controllers/group_members_controller.rb b/app/controllers/group_members_controller.rb index f37927ab..ac0e92b6 100644 --- a/app/controllers/group_members_controller.rb +++ b/app/controllers/group_members_controller.rb @@ -4,9 +4,7 @@ class GroupMembersController < ApplicationController before_action :authenticate_user! before_action :assign_group before_action :assign_current_user_group_member - before_action :assign_group_member, only: %i[edit update destroy] before_action :must_be_member_of_group - before_action :must_be_admin_for_group, only: %i[new create update destroy] def index @group_members = Group @@ -16,54 +14,12 @@ def index .sort_by { |gm| gm.user.login || 'anonymous' } end - def new - @group_member = GroupMember.new - end - - def create - user = User.find_by(login: params[:login]) - - @group_member = GroupMember.new(group: @group, user: user, role: 'invitee') - if user.present? && @group_member.save - @group_member.send_invitation - redirect_to group_group_members_path(@group) - else - flash[:error] = if user.nil? - 'There is no PredictionBook user with that login' - else - @group_member.errors.full_messages.join(',') - end - render :new - end - end - - def update - role_param = params[:role] - if role_param.present? && GroupMember::ROLES.key?(role_param.to_sym) - @group_member.update(role: role_param) - else - @group_member.send_invitation - end - redirect_to group_group_members_path(@group) - end - - def destroy - @group_member.send_ejection - @group_member.destroy - redirect_to group_group_members_path(@group), - notice: "User '#{@group_member.user.login}' has been removed from the group." - end - private def assign_group @group = Group.find(params[:group_id]) end - def assign_group_member - @group_member = GroupMember.find(params[:id]) - end - def assign_current_user_group_member @current_user_group_member = @group.group_members.find { |gm| gm.user_id == current_user.id } end @@ -73,9 +29,4 @@ def must_be_member_of_group redirect_to(root_path, notice: notice) if @current_user_group_member.blank? || @current_user_group_member.invitee? end - - def must_be_admin_for_group - notice = 'You are not authorized to perform that action' - redirect_to(group_path(@group), notice: notice) unless @current_user_group_member.try(:admin?) - end end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 5385e38e..4686bbc9 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -2,8 +2,7 @@ class GroupsController < ApplicationController before_action :authenticate_user! - before_action :assign_group_member, only: %i[show edit update destroy] - before_action :must_be_admin_for_group, only: %i[edit update destroy] + before_action :assign_group_member, only: %i[show] def index group_members = current_user.group_members.includes(group: :group_members) @@ -20,68 +19,9 @@ def show Prediction.where(group: @group).page(params[:page]).includes(Prediction::DEFAULT_INCLUDES) end - def new - @group = Group.new - end - - def create - @group = Group.new(group_params) - add_group_members - if @group.save - @group.group_members.each(&:send_invitation) - redirect_to group_path(@group) - else - flash[:error] = @group.errors.full_messages.join(',') - render :new - end - end - - def edit - @group = @group_member.group - end - - def update - @group = @group_member.group - @group.assign_attributes(group_params) - if @group.save - redirect_to group_path(@group) - else - flash[:error] = @group.errors.full_messages.join(',') - render :edit - end - end - - def destroy - @group = @group_member.group - @group.destroy - redirect_to groups_path, notice: "Group '#{@group.name}' has been destroyed. All predictions " \ - 'visible to the group have been made private.' - end - private def assign_group_member @group_member = current_user.group_members.includes(:group).where(group_id: params[:id]).first end - - def must_be_admin_for_group - notice = 'You are not authorized to perform that action' - redirect_to(root_path, notice: notice) if @group_member.nil? || !@group_member.admin? - end - - def group_params - params.require(:group).permit(:name) - end - - def add_group_members - logins = (params[:invitees] || '').split("\n").take(20) - User.where.not(login: [nil, '', current_user.login]).where(login: logins).each do |user| - add_group_member(user, 'invitee') - end - add_group_member(current_user, 'admin') - end - - def add_group_member(user, role) - @group.group_members << GroupMember.new(group: @group, user: user, role: role) - end end diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb deleted file mode 100644 index 6c7f940b..00000000 --- a/app/controllers/notifications_controller.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -class NotificationsController < ApplicationController - before_action :authenticate_user! - - def create - notification = notification_collection.create!(notification_params) - redirect_or_render(notification) - end - - def update - notification = notification_collection.find(params[:id]) - notification.update!(notification_params) - redirect_or_render(notification) - end - - def underscored_notification_type - notification_type.to_s.underscore - end - - def notification_collection - current_user.send(underscored_notification_type.pluralize) - end - - private - - def redirect_or_render(notification) - if request.xhr? - render partial: notification - else - redirect_to prediction_path(notification.prediction) - end - end - - def notification_params - params.require(underscored_notification_type).permit(:prediction_id, :enabled) - end -end diff --git a/app/controllers/prediction_groups_controller.rb b/app/controllers/prediction_groups_controller.rb index d51cc05a..ae9dd650 100644 --- a/app/controllers/prediction_groups_controller.rb +++ b/app/controllers/prediction_groups_controller.rb @@ -2,52 +2,14 @@ class PredictionGroupsController < ApplicationController before_action :authenticate_user! - before_action :find_prediction_group, only: %i[show edit update] - before_action :must_be_authorized_for_prediction, only: %i[edit update show] + before_action :find_prediction_group, only: %i[show] + before_action :must_be_authorized_for_prediction, only: %i[show] def show @events = @prediction_group.predictions.flat_map(&:events).sort_by(&:created_at) @title = @prediction_group.description end - def new - @title = 'Make a Prediction Group' - @statistics = current_user.try(:statistics) - visibility = current_user.try(:visibility_default) || 0 - group_id = current_user.try(:group_default_id) - @prediction_group = PredictionGroup.new - @prediction_group.predictions.new(creator: current_user, visibility: visibility, - group_id: group_id) - end - - def create - @prediction_group = UpdatedPredictionGroup.new(PredictionGroup.new, - current_user, - prediction_group_params).prediction_group - if @prediction_group.save - redirect_to prediction_group_path(@prediction_group) - else - @prediction_group.predictions.new if @prediction_group.default_prediction.nil? - render action: 'new', status: :unprocessable_entity - end - end - - def edit - group_desc = @prediction_group.description - @title = "Editing: “!#{group_desc}”" - end - - def update - @prediction_group = UpdatedPredictionGroup.new(@prediction_group, - current_user, - prediction_group_params).prediction_group - if @prediction_group.save - redirect_to prediction_group_path(@prediction_group) - else - render action: 'edit', status: :unprocessable_entity - end - end - private def must_be_authorized_for_prediction @@ -63,8 +25,4 @@ def find_prediction_group @prediction_group = PredictionGroup.includes(predictions: %i[responses versions judgements]) .find(params[:id]) end - - def prediction_group_params - params.require(:prediction_group).permit! - end end diff --git a/app/controllers/predictions_controller.rb b/app/controllers/predictions_controller.rb index 9215a0b3..c9ea76b1 100644 --- a/app/controllers/predictions_controller.rb +++ b/app/controllers/predictions_controller.rb @@ -3,45 +3,11 @@ require 'csv' class PredictionsController < ApplicationController - before_action :authenticate_user!, only: %i[new create judge withdraw edit update mine] - before_action :find_prediction, only: %i[judge show withdraw edit update] - before_action :must_be_authorized_for_prediction, only: %i[withdraw edit update show] + before_action :authenticate_user!, only: %i[mine] + before_action :find_prediction, only: %i[show] + before_action :must_be_authorized_for_prediction, only: %i[show] before_action :ensure_statistics, only: [:index] - cache_sweeper :statistics_sweeper, only: :judge - - def new - @title = 'Make a Prediction' - @statistics = current_user.try(:statistics) - visibility = current_user.try(:visibility_default) || 0 - group_id = current_user.try(:group_default_id) - @prediction = Prediction.new(creator: current_user, visibility: visibility, group_id: group_id) - end - - def create - begin - @prediction = Prediction.create!(prediction_params) - rescue Prediction::DuplicateRecord => duplicate - @prediction = duplicate.record - end - redirect_to prediction_path(@prediction) - rescue ActiveRecord::RecordInvalid => invalid - @prediction = invalid.record - render action: 'new', status: :unprocessable_entity - end - - def edit - @title = "Editing: “#{@prediction.description_with_group}”" - end - - def update - @prediction.update!(prediction_params) - redirect_to prediction_path(@prediction) - rescue ActiveRecord::RecordInvalid => invalid - @prediction = invalid.record - render action: 'edit', status: :unprocessable_entity - end - def home visibility = current_user.try(:visibility_default) || 'visible_to_everyone' group_id = current_user.try(:group_default_id) @@ -105,17 +71,8 @@ def index def show if current_user.present? @prediction_response = Response.new(user: current_user) - @deadline_notification = @prediction.deadline_notification_for_user(current_user) - @response_notification = @prediction.response_notification_for_user(current_user) - @response_notification.viewed! end - @edit_path = if @prediction.prediction_group_id.present? - edit_prediction_group_path(@prediction.prediction_group) - else - edit_prediction_path(@prediction) - end - @events = @prediction.events @title = @prediction.description_with_group end @@ -150,17 +107,6 @@ def happenstance @responses = Response.recent(limit: 25).includes(prediction: :judgements, user: nil) end - def judge - @prediction.judge!(params[:outcome], current_user) - flash[:judged] = 'judged' - redirect_to @prediction - end - - def withdraw - @prediction.withdraw! - redirect_to @prediction - end - private def ensure_statistics @@ -177,15 +123,4 @@ def must_be_authorized_for_prediction def find_prediction @prediction = Prediction.find(params[:id]) end - - def prediction_params - result = params.require(:prediction).permit! - visibility = result[:visibility] - result.merge!(Visibility.option_to_attributes(visibility)) if visibility.present? - if @prediction.nil? - result[:visibility] = current_user.try(:visibility_default) unless result.key?(:visibility) - result[:creator_id] = current_user.id - end - result - end end diff --git a/app/controllers/response_notifications_controller.rb b/app/controllers/response_notifications_controller.rb deleted file mode 100644 index b8863ded..00000000 --- a/app/controllers/response_notifications_controller.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -class ResponseNotificationsController < NotificationsController - def notification_type - ResponseNotification - end -end diff --git a/app/controllers/responses_controller.rb b/app/controllers/responses_controller.rb index e4456c47..6ac40014 100644 --- a/app/controllers/responses_controller.rb +++ b/app/controllers/responses_controller.rb @@ -6,27 +6,4 @@ class ResponsesController < ApplicationController def index @responses = Response.recent(limit: 50) end - - def create - @prediction_response = prediction.responses.new(response_params) - flash[:error] = 'You must enter an estimate or comment' unless @prediction_response.save - redirect_to prediction_path(prediction) - end - - def preview - @response = Response.new(response_params) - render partial: 'preview' - end - - private - - def prediction - @prediction ||= Prediction.find(params[:prediction_id]) - end - - def response_params - result = params.require(:response).permit! - result[:user_id] = current_user.id if params[:action] == 'create' - result - end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 8bef005d..bd68cfa0 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,20 +1,11 @@ # frozen_string_literal: true class UsersController < ApplicationController - before_action :lookup_user, only: %i[destroy show update settings due_for_judgement statistics] - before_action :authenticate_user!, only: %i[destroy settings update generate_api_token] - before_action :user_must_be_current_user, only: %i[destroy settings update] + before_action :lookup_user, only: %i[show settings statistics] + before_action :authenticate_user!, only: %i[settings] + before_action :user_must_be_current_user, only: %i[settings] before_action :allow_iframe_requests, only: :statistics - def destroy - if current_user.pseudonymize! && current_user.destroy - redirect_to root_url - else - flash[:error] = 'The user record failed to be destroyed!' - redirect_to settings_user_url(current_user) - end - end - def show @title = "Most recent predictions by #{@user}" @predictions = showable_predictions @@ -22,18 +13,6 @@ def show @score_calculator = ScoreCalculator.new(@user, start_date: 6.months.ago, interval: 1.month) end - def update - @user.assign_attributes(user_params) - if @user.valid? - @user.save! - show - render action: :show - else - settings - render action: :settings - end - end - def settings @title = "Settings for #{current_user}" end @@ -59,22 +38,8 @@ def due_for_judgement @predictions = @predictions.select(&:due_for_judgement?) end - def generate_api_token - if updated_user_api_token? - flash[:notice] = 'Generated a new API token!' - else - flash[:error] = update_api_token_error_message - end - - redirect_to settings_user_url(current_user) - end - protected - def ensure_statistics - @statistics = Statistics.new - end - def lookup_user id_param = UserLogin.new(params[:id]).to_s @user = User.find_by(login: id_param) || User.find_by(id: id_param) @@ -103,28 +68,6 @@ def showable_predictions ).call end - def updated_user_api_token? - current_user&.update(api_token: User.generate_api_token) - end - - def update_api_token_error_message - 'Unable to generate new API token due to these errors:' + - current_user.errors.full_messages.to_sentence + '.' \ - 'Please ensure your user profile is complete.' - end - - def user_params - permitted_params = params.require(:user).permit(:login, :email, :name, :timezone, - :visibility_default, :api_token) - visibility_default = permitted_params[:visibility_default] - if visibility_default.present? - attributes = Visibility.option_to_attributes(visibility_default) - permitted_params[:visibility_default] = attributes[:visibility] - permitted_params[:group_default_id] = attributes[:group_id] - end - permitted_params - end - def allow_iframe_requests response.headers.delete('X-Frame-Options') end diff --git a/app/helpers/deadline_helper.rb b/app/helpers/deadline_helper.rb deleted file mode 100644 index 78124a3e..00000000 --- a/app/helpers/deadline_helper.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -module DeadlineHelper - def deadline_notification_info(deadline_notification) - set_your_email = link_to 'set your email', settings_user_path(deadline_notification.user) - if deadline_notification.sent? - notification_text = 'Your email notification has been sent.' - elsif deadline_notification.sendable? - notification_text = 'Your email notification will be sent soon.' - elsif deadline_notification.due_for_judgement? && deadline_notification.enabled? - notification_text = "To receive your notifications, #{set_your_email}" - elsif deadline_notification.deadline < Time.current && deadline_notification.enabled? - notification_text = "You would have been notified #{TimeInContentTagPresenter.new(deadline_notification.deadline).tag}, but it was already judged." - else - time = TimeInContentTagPresenter.new(deadline_notification.deadline).tag - will_would = deadline_notification.enabled ? 'will be' : 'would be' - will_would = 'would have been' if deadline_notification.overdue? - notification_text = if deadline_notification.has_email? - "You #{will_would} notified #{time}." - else - "You #{will_would} notified #{time} if you #{set_your_email}." - end - notification_text.html_safe - end - end - - def deadline_notification_disabled(deadline_notification) - content_tag(:em, 'Notification when the outcome should be known') + ' ' + - if deadline_notification.withdrawn? - 'unavailable as the prediction has been withdrawn.' - elsif !deadline_notification.open? - 'unavailable as the prediction has already been judged.' - elsif deadline_notification.overdue? - 'unavailable as the deadline has already passed.' - else - 'unavailable.' - end - end -end diff --git a/app/mailers/deliverer.rb b/app/mailers/deliverer.rb deleted file mode 100644 index 9ca4a15b..00000000 --- a/app/mailers/deliverer.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -class Deliverer < ApplicationMailer - def deadline_notification(dn) - @prediction = dn.prediction - @deadline = dn - - mail( - subject: "[PredictionBook] Judgement Day for ‘#{dn.description}’", - to: dn.email_with_name - ) - end - - def response_notification(rn) - @prediction = rn.prediction - @notification = rn - - mail( - subject: "[PredictionBook] There has been some activity on ‘#{rn.description}’", - to: rn.email_with_name - ) - end -end diff --git a/app/mailers/group_member_mailer.rb b/app/mailers/group_member_mailer.rb deleted file mode 100644 index 3cf60545..00000000 --- a/app/mailers/group_member_mailer.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -class GroupMemberMailer < ApplicationMailer - def invitation(group_member) - @uuid = group_member.uuid - @group = group_member.group - @user = group_member.user - - mail(subject: "[PredictionBook] Invitation to join ‘#{@group.name}’ user group", - to: @user.email) - end - - def ejection(group_member) - @group = group_member.group - @user = group_member.user - - mail(subject: "[PredictionBook] Ejection from ‘#{@group.name}’ user group", - to: @user.email) - end -end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb deleted file mode 100644 index d783117b..00000000 --- a/app/mailers/user_mailer.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -class UserMailer < ApplicationMailer - def password_reset(user) - @new_password = user.password - - mail( - to: user.email, - subject: '[PredictionBook] Your password was reset' - ) - end -end diff --git a/app/methods/tag_adder.rb b/app/methods/tag_adder.rb deleted file mode 100644 index 3dcc617b..00000000 --- a/app/methods/tag_adder.rb +++ /dev/null @@ -1,38 +0,0 @@ -class TagAdder - def initialize(prediction:, string:) - @prediction = prediction - @string = string || '' - end - - def call - if string.present? && prediction.present? - tag_names.each do |tag_name| - prediction.tag_names << tag_name - end - end - - tag_names.any? - end - - def string_without_tags - new_string = string.dup - - tag_names.each do |tag_name| - new_string.gsub!("##{tag_name}", '') - end - - new_string.strip.squeeze(' ') - end - - def tag_names - @tag_names ||= string.scan(HASH_TAG_REGEX).flatten.map do |name| - name.tr('#', '') - end - end - - private - - HASH_TAG_REGEX = /#[\w-]+/ - - attr_reader :prediction, :string -end diff --git a/app/models/credence_answer.rb b/app/models/credence_answer.rb deleted file mode 100644 index 4e3deb40..00000000 --- a/app/models/credence_answer.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -class CredenceAnswer < ApplicationRecord - belongs_to :credence_question - - validates :rank, uniqueness: { scope: :credence_question_id } - - def format - # Would be nice to format the text in bold. - "#{text} (#{credence_question.prefix}#{value}#{credence_question.suffix})" - end -end diff --git a/app/models/credence_game.rb b/app/models/credence_game.rb deleted file mode 100644 index 73158bba..00000000 --- a/app/models/credence_game.rb +++ /dev/null @@ -1,67 +0,0 @@ -# frozen_string_literal: true - -class CredenceGame < ApplicationRecord - belongs_to :user - belongs_to :current_response, class_name: CredenceGameResponse.name, autosave: true, optional: true - has_many :responses, class_name: CredenceGameResponse.name, dependent: :destroy - - after_create :ensure_current_response - - def responses_selected_by_users - responses.answered - end - - def average_score - average = score.to_f / num_answered - average.finite? ? average : 0 - end - - def most_recently_answered(limit) - responses_selected_by_users.order('answered_at desc').limit(limit) - end - - def recent_score(limit) - most_recently_answered(limit).map(&:score).reduce(&:+) - end - - def recent_average(limit) - average = recent_score(limit).to_f / most_recently_answered(limit).length - average.finite? ? average : 0 - end - - def calibration_graph - CredenceStatistics.new(responses_selected_by_users) - end - - def add_score(new_score) - self.score += new_score - self.num_answered += 1 - set_current_response - save! - end - - private - - def enabled_questions - @enabled_questions ||= CredenceQuestion.enabled - end - - def ensure_current_response - unless current_response.present? - set_current_response - save! - end - end - - def set_current_response - enabled_question_count = enabled_questions.count - return if enabled_question_count.zero? - - random_offset = Random.rand(enabled_question_count) - question = enabled_questions.offset(random_offset).first - - if question.present? - self.current_response = question.build_random_response(self) - end - end -end diff --git a/app/models/credence_game_response.rb b/app/models/credence_game_response.rb deleted file mode 100644 index 471dc8d5..00000000 --- a/app/models/credence_game_response.rb +++ /dev/null @@ -1,61 +0,0 @@ -# frozen_string_literal: true - -class CredenceGameResponse < ApplicationRecord - belongs_to :credence_game - belongs_to :credence_question - belongs_to :first_answer, class_name: 'CredenceAnswer' - belongs_to :second_answer, class_name: 'CredenceAnswer' - - validates :credence_game, presence: true - validates :credence_question, presence: true - validates :first_answer, presence: true - validates :second_answer, presence: true - validates :correct_index, presence: true - validates :answer_credence, inclusion: { in: 1..99, message: 'must be between 1 and 99' }, - allow_nil: true - - delegate :text, to: :credence_question - - scope :answered, -> { where('answered_at IS NOT NULL') } - - def answers - [first_answer, second_answer] - end - - def answer_correct? - given_answer == correct_index - end - - # Check whether answer is the correct answer, and scores according to - # credence. credence is a _percentage_, i.e. in the range [1, 99]. - def score_answer - return nil if answer_credence.nil? || !valid? - - correct = answer_correct? - truth_credence = correct ? answer_credence : (100 - answer_credence) - score = (Math.log(2 * truth_credence.to_f / 100.0, 2) * 100).round - [correct, score] - end - - def score - (score_answer || [0]).last - end - - def answer_message - # In the original game, you got a different message if you guessed 50% - # (which gave you no points). If 50% becomes a valid guess, we'll want to do - # the same here. - right = answers[correct_index].format - wrong = answers[1 - correct_index].format - - if answer_correct? - %(Correct! - +#{score} points.
).html_safe + - " The answer is #{right} versus #{wrong}." - else - %(Incorrect. - #{score} points.
).html_safe + - " The right answer is #{right} versus #{wrong}." - end - end -end diff --git a/app/models/credence_question.rb b/app/models/credence_question.rb deleted file mode 100644 index 39cf1989..00000000 --- a/app/models/credence_question.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -class CredenceQuestion < ApplicationRecord - has_many :answers, class_name: CredenceAnswer.name, dependent: :destroy, autosave: true - has_many :responses, class_name: CredenceGameResponse.name, dependent: :destroy, autosave: true - - scope :enabled, -> { where(enabled: true) } - - def build_random_response(game) - random_answers = [] - answer_count = answers.size - while answer_count >= 0 && random_answers.length < [answer_count, 5].minmax.first - random_index = Random.rand(answer_count) - random_answer = answers[random_index] - random_answers << random_answer unless random_answers.include?(random_answer) - end - raise 'Not enough answers to create a random response' unless random_answers.length >= 2 - - first_answer = random_answers.first - second_answer = random_answers.find { |answer| answer.value != first_answer.value } - which = first_answer.rank < second_answer.rank ? 0 : 1 - responses.new(credence_game: game, first_answer: first_answer, second_answer: second_answer, - correct_index: which) - end - - def self.create_from_xml_element!(generator, id_prefix) - question_type = generator['Type'].to_s - return if question_type != 'Sorted' - - question = CredenceQuestion.create( - enabled: generator['Used'].to_s == 'y', text_id: "#{id_prefix}:#{generator['Id']}", - question_type: question_type, text: generator['QuestionText'].to_s, - prefix: generator['InfoPrefix'].to_s, suffix: generator['InfoSuffix'].to_s, - adjacent_within: generator['AdjacentWithin'].to_s.to_i, weight: generator['Weight'].to_s.to_f - ) - generator.search('Answer').each_with_index do |answer, index| - question.answers.create!(text: answer['Text'], value: answer['Value'].to_s, rank: index) - end - question - end -end diff --git a/app/models/credence_statistics.rb b/app/models/credence_statistics.rb deleted file mode 100644 index dba555e1..00000000 --- a/app/models/credence_statistics.rb +++ /dev/null @@ -1,50 +0,0 @@ -# frozen_string_literal: true - -class CredenceStatistics < BaseStatistics - attr_reader :groups, :intervals - - def initialize(responses) - self.groups = Hash.new { |hash, key| hash[key] = GroupedWagers.new } - - responses.each do |response| - groups[response.answer_credence].add_figures_for_response(response) - end - - create_intervals - end - - private - - attr_writer :groups, :intervals - - def create_intervals - self.intervals = groups.map do |credence, group| - [credence, group.interval(credence)] - end.to_h - end - - class GroupedWagers - def initialize - self.count = 0 - self.correct = 0 - end - - def add_figures_for_response(response) - response_correct = response.answer_correct? - self.count = count + 1 - self.correct = correct + 1 if response_correct - end - - def interval(credence) - BaseStatistics::Interval.new(credence, count, accuracy) - end - - private - - attr_accessor :count, :correct - - def accuracy - count > 0 ? (correct.to_f / count) : 0 - end - end -end diff --git a/app/models/deadline_notification.rb b/app/models/deadline_notification.rb deleted file mode 100644 index 3c88d99d..00000000 --- a/app/models/deadline_notification.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -class DeadlineNotification < Notification - def deliver - Deliverer.deadline_notification(self).deliver_now - end - - def <=>(other) - deadline <=> other.deadline - end - - def sendable? - enabled? && has_email? && due_for_judgement? && !withdrawn? - end - - def self.known - all.reject(&:unknown?) - end - - def self.unknown - all.select(&:unknown?) - end -end diff --git a/app/models/group.rb b/app/models/group.rb index 8d3efc82..a09569cd 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -5,8 +5,6 @@ class Group < ApplicationRecord has_many :group_members, autosave: true, dependent: :destroy - before_destroy :make_predictions_private - def user_role(user) group_member = group_members.find { |member| member.user_id == user.id } group_member.role if group_member.present? @@ -15,12 +13,4 @@ def user_role(user) def statistics Statistics.new("p.visibility = #{Visibility::VALUES[:visible_to_group]} AND p.group_id = #{id}") end - - private - - def make_predictions_private - Prediction - .visible_to_group.where(group_id: id) - .update_all(visibility: Visibility::VALUES[:visible_to_creator], group_id: nil) - end end diff --git a/app/models/group_member.rb b/app/models/group_member.rb index 1cce391d..1e6056d7 100644 --- a/app/models/group_member.rb +++ b/app/models/group_member.rb @@ -19,16 +19,4 @@ class GroupMember < ApplicationRecord after_initialize do self.uuid ||= UUIDTools::UUID.random_create.to_s end - - def send_invitation - return if !invitee? || user.email.nil? - - GroupMemberMailer.invitation(self).deliver_later - end - - def send_ejection - return if user.email.nil? - - GroupMemberMailer.ejection(self).deliver_later - end end diff --git a/app/models/notification.rb b/app/models/notification.rb deleted file mode 100644 index ed47a5e0..00000000 --- a/app/models/notification.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -class Notification < ApplicationRecord - delegate :email_with_name, :has_email?, to: :user - delegate :description, :deadline, :judged_at, to: :prediction - delegate :open?, :unknown?, :right?, :wrong?, :due_for_judgement?, :overdue?, :withdrawn?, - to: :prediction - - belongs_to :prediction - belongs_to :user, optional: true - - validates :user, presence: true, uniqueness: { scope: %i[prediction type] } - - scope :unsent, -> { where(sent: false) } - scope :sent, -> { where(sent: true) } - scope :enabled, -> { where(enabled: true) } - scope :disabled, -> { where(enabled: false) } - - def initialize(attributes = nil) - super - self.uuid ||= UUIDTools::UUID.random_create.to_s - end - - def use_token! - update_attribute(:token_used, true) - end - - def self.use_token!(token) - dn = find_by(uuid: token) - if dn.present? - unless dn.token_used? - yield dn - dn.use_token! - end - end - end - - def self.sendable - unsent.select(&:sendable?) - end - - def self.send_all! - # `includes(:prediction, :user)`, eager loading, used to increase efficiency - # `find_each`, loading records in batches, used to reduce RAM consumption - default_includes = [{ prediction: %i[judgements creator] }, :user] - unsent.enabled.includes(default_includes).find_each do |notification| - notification.deliver! if notification.sendable? - end - end - - def sendable? - false - end - - def deliver! - deliver - update_attribute(:sent, true) - end -end diff --git a/app/models/prediction.rb b/app/models/prediction.rb index 3ff90d26..73fff8ba 100644 --- a/app/models/prediction.rb +++ b/app/models/prediction.rb @@ -20,9 +20,7 @@ class Prediction < ApplicationRecord class DuplicateRecord < ActiveRecord::RecordInvalid; end # == Relationships ======================================================== - has_many :deadline_notifications, dependent: :destroy has_many :judgements, dependent: :destroy - has_many :response_notifications, dependent: :destroy has_many :responses, dependent: :destroy, autosave: true has_many :versions, autosave: true, class_name: PredictionVersion.name, dependent: :destroy @@ -36,43 +34,11 @@ class DuplicateRecord < ActiveRecord::RecordInvalid; end validates :creator, presence: { message: 'Who are you?' } validates :description, presence: { message: 'What are you predicting?' } validates :initial_confidence, presence: { message: 'How sure are you?', on: :create } - validate :confidence_on_response, on: :create validate :bound_deadline # == Scopes =============================================================== scope :not_withdrawn, -> { where(withdrawn: false) } - # == Callbacks ============================================================ - after_initialize do - self.uuid ||= UUIDTools::UUID.random_create.to_s if has_attribute?(:uuid) - end - - before_validation(on: :create) do - @initial_response ||= responses.build( - confidence: initial_confidence, - prediction: self, - user: creator - ) - - if notify_creator - deadline_notifications.build(prediction: self, user: creator) - end - - tag_adder = TagAdder.new(prediction: self, string: description) - if tag_adder.call - self.description = tag_adder.string_without_tags - end - end - - after_validation do - if errors[:deadline].any? - errors.add(:deadline_text, errors[:deadline]) - end - end - - before_save :create_version_if_required - after_save :synchronise_group_visibility_and_deadline - # == Class Methods ======================================================== def self.parse_deadline(date) Chronic.parse(date, context: :future) @@ -117,11 +83,6 @@ def self.popular # == Instance Methods ===================================================== - def create_version_if_required - PredictionVersion.create_from_current_prediction_if_required(self) - true # Never halt save - end - def initialize(attributes = nil) notify_creator_bool = extract_notify_creator_bool(attributes) super @@ -134,16 +95,6 @@ def assign_attributes(attributes) assign_notify_creator_with_default(notify_creator_bool) end - def save! - super - rescue ActiveRecord::RecordNotUnique => error - if error.message.match?(/index_predictions_on_uuid/) - raise DuplicateRecord, Prediction.find_by(uuid: uuid) - end - - raise - end - def initial_confidence @initial_confidence || responses.first.try(:confidence) end @@ -178,10 +129,6 @@ def events [responses, versions[1..-1], judgements].flatten.sort_by(&:created_at) end - def judge!(outcome, user = nil) - judgements.create!(user: user, outcome: outcome) - end - def judgement judgements.last end @@ -246,12 +193,8 @@ def count_wagers_by(user) wagers.where(user_id: user.id).count end - def deadline_notification_for_user(user) - deadline_notifications.find_by(user_id: user) || deadline_notifications.build(user: user, enabled: false) - end - - def response_notification_for_user(user) - response_notifications.find_by(user_id: user) || response_notifications.build(user: user, enabled: false) + def judge!(outcome, user = nil) + judgements.create!(user: user, outcome: outcome) end def confidence_on_response diff --git a/app/models/response_notification.rb b/app/models/response_notification.rb deleted file mode 100644 index 12c599af..00000000 --- a/app/models/response_notification.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -class ResponseNotification < Notification - def deliver - Deliverer.response_notification(self).deliver - end - - def viewed! - unless new_record? - update!( - sent: false, - token_used: false, - new_activity: false - ) - end - end - - def new_activity! - update!(new_activity: true) - end - - def sendable? - enabled? && has_email? && new_activity? - end -end diff --git a/app/models/updated_prediction_group.rb b/app/models/updated_prediction_group.rb deleted file mode 100644 index 78abf377..00000000 --- a/app/models/updated_prediction_group.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -class UpdatedPredictionGroup - attr_reader :prediction_group - - def initialize(prediction_group, creator, params) - self.prediction_group = prediction_group - self.creator = creator - self.params = params - update_prediction_group_from_params - update_predictions_from_params - end - - private - - attr_accessor :params, :creator - attr_writer :prediction_group - - def update_prediction_group_from_params - prediction_group.description = params['description'] - end - - def update_predictions_from_params - if params['predictions'].present? - PredictionGroupPredictionsFromNestedParams.new(prediction_group, creator, params).execute - else - PredictionGroupPredictionsFromFlatParams.new(prediction_group, creator, params).execute - end - end -end diff --git a/app/models/user.rb b/app/models/user.rb index 5ebff16c..501c115c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -10,9 +10,6 @@ class User < ApplicationRecord has_many :group_members, dependent: :destroy has_many :groups, through: :group_members delegate :wagers, to: :responses - has_many :deadline_notifications, dependent: :destroy - has_many :response_notifications, dependent: :destroy - has_one :credence_game, dependent: :destroy has_many :judgements, dependent: :destroy has_many :predictions, dependent: :destroy, foreign_key: :creator_id @@ -62,11 +59,6 @@ def pseudonymize! UserPseudonymizer.call(self) end - def predictions - prediction_ids = wagers.select(:prediction_id).map(&:prediction_id) - Prediction.where(id: prediction_ids).order(updated_at: :desc) - end - def devise_password_specified? encrypted_password.present? end diff --git a/app/observers/response_observer.rb b/app/observers/response_observer.rb deleted file mode 100644 index d9043bd9..00000000 --- a/app/observers/response_observer.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -class ResponseObserver < ActiveRecord::Observer - observe :response - - def after_create(response) - response.prediction.tap do |prediction| - prediction.response_notifications.each(&:new_activity!) - - tag_adder = TagAdder.new(prediction: response.prediction, string: response.comment) - if tag_adder.call - prediction.save - end - end - end -end diff --git a/app/sweepers/statistics_sweeper.rb b/app/sweepers/statistics_sweeper.rb deleted file mode 100644 index b3b77819..00000000 --- a/app/sweepers/statistics_sweeper.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -class StatisticsSweeper < ActionController::Caching::Sweeper - observe Judgement - - include CacheKeys - - def after_create(judgement) - associated_users = judgement.prediction.wagers.collect(&:user).uniq - clear_user_caches(associated_users) - clear_group_caches(associated_users) - Rails.cache.delete(global_statistics_cache_key) - end - - private - - def clear_user_caches(associated_users) - cache = Rails.cache - associated_users.each do |user| - cache.delete(user_statistics_cache_key(user)) - cache.delete(user_calibration_scores_cache_key(user)) - end - end - - def clear_group_caches(associated_users) - cache = Rails.cache - associated_groups = - GroupMember.includes(:group).where(user_id: associated_users.map(&:id)).map(&:group).uniq - associated_groups.each do |group| - cache.delete(group_statistics_cache_key(group)) - cache.delete(group_calibration_scores_cache_key(group)) - cache.delete(group_leaderboard_cache_key(group)) - end - end -end diff --git a/app/views/credence_games/show.html.erb b/app/views/credence_games/show.html.erb deleted file mode 100644 index c6c5e7dd..00000000 --- a/app/views/credence_games/show.html.erb +++ /dev/null @@ -1,63 +0,0 @@ -

The Credence Game

- -<% if flash[:score] %> -

<%= flash[:message] %>

-<% end %> - -<% if @game.present? %> - - - - - - - - - - - -
<%= @game.average_score.round(1).to_s.chomp('.0') %><%= @game.recent_average(10).round(1).to_s.chomp('.0') %><%= @game.score %>
Average scoreLast 10 averageTotal score
(<%= @game.num_answered %> answers)
- -
- -

Question <%= @game.num_answered + 1 %>: - <%= @response.text %>

- - - <% @response.answers.each_with_index do |answer, i| %> - - - <% for pct in [51, 60, 70, 80, 90, 99] %> - - <% end %> - - <% end %> -
<%= answer.text %> - <%= form_tag(credence_game_response_path(@response), method: :put) do %> - <%= hidden_field_tag "response[answer_credence]", pct %> - <%= hidden_field_tag "response[given_answer]", i %> - <%= submit_tag "#{pct}%" %> - <% end %> -
- - <% if @game.num_answered > 0 %> -
- <% if @show_graph %> -

-

- -
- <% else %> -

Show calibration graph

-
-

Be careful: looking at this graph too often can be bad for your score. If - you find yourself giving low-confidence answers to questions that you know - very well, consider looking less often.

- -
- <% end %> -
- <% end %> -<% else %> - No games are available yet. Please try again later. -<% end %> diff --git a/app/views/deadline_notifications/_deadline_notification.html.erb b/app/views/deadline_notifications/_deadline_notification.html.erb deleted file mode 100644 index 017c573b..00000000 --- a/app/views/deadline_notifications/_deadline_notification.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -<% if deadline_notification.open? && !deadline_notification.overdue? %> - <%= form_for(deadline_notification, html: {class: 'single-checkbox-form'}) do |f| %> - <%= f.hidden_field :prediction_id %> - <%= f.check_box :enabled, label: 'Email me when the outcome should be known' %> - <%= f.submit 'Update', class: 'noscript' %> - <% end %> -

- <%= deadline_notification_info(deadline_notification) %> -

-<% else %> -

<%= deadline_notification_disabled(deadline_notification) %>

-<% end %> diff --git a/app/views/deadline_notifications/_list.html.erb b/app/views/deadline_notifications/_list.html.erb deleted file mode 100644 index 53f48243..00000000 --- a/app/views/deadline_notifications/_list.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -<% deadline_notifications.each do |deadline_notification| %> - <%= render deadline_notification.prediction %> -<% end %> \ No newline at end of file diff --git a/app/views/deadline_notifications/index.html.erb b/app/views/deadline_notifications/index.html.erb deleted file mode 100644 index 3c5dca95..00000000 --- a/app/views/deadline_notifications/index.html.erb +++ /dev/null @@ -1,21 +0,0 @@ -

Email Notifications for <%= show_user(@user) %>

- -<% unless @pending.empty? %> -

Waiting to be sent

- -<% end %> - -<% unless @waiting.empty? %> -

Waiting for deadline

- -<% end %> - -<% unless @known.empty? %> -

Judged before deadline

- -<% end %> - -<% unless @sent.empty? %> -

Sent

- -<% end %> diff --git a/app/views/deliverer/deadline_notification.html.erb b/app/views/deliverer/deadline_notification.html.erb deleted file mode 100644 index 66e48bf1..00000000 --- a/app/views/deliverer/deadline_notification.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -

Prediction: <%= @prediction.description_with_group %>

-<% if @prediction.responses.size > 1 %> -

- Since you created this prediction <%= pluralize(@prediction.responses.size - 1, 'other person') %> - had something to say about your prediction, of which <%= @prediction.wagers.size - 1 %> - placed a wager where their mouth is. -

-<% end %> -

- Now that <%= @prediction.prettied_deadline %> has been reached, it's judgement time. -

-

<%= link_to 'Go forth and deliver your judgement', prediction_url(@prediction, token: @deadline.uuid) %>

diff --git a/app/views/deliverer/deadline_notification.text.erb b/app/views/deliverer/deadline_notification.text.erb deleted file mode 100644 index d2469946..00000000 --- a/app/views/deliverer/deadline_notification.text.erb +++ /dev/null @@ -1,10 +0,0 @@ -Prediction: <%= @prediction.description_with_group %> - -<% if @prediction.responses.size > 1 %> -Since you created this prediction <%= pluralize(@prediction.responses.size - 1, 'other person') %> had something to say about your prediction, of which <%= @prediction.wagers.size - 1 %> placed a wager where their mouth is. - -<% end %> -Now that “<%= @prediction.prettied_deadline %>” has been reached, it's judgement time. - -Go forth and deliver your judgement at: -<%= prediction_url(@prediction, token: @deadline.uuid) %> diff --git a/app/views/deliverer/response_notification.html.erb b/app/views/deliverer/response_notification.html.erb deleted file mode 100644 index 55a9a1fb..00000000 --- a/app/views/deliverer/response_notification.html.erb +++ /dev/null @@ -1,9 +0,0 @@ -

Prediction: <%= @prediction.description_with_group %>

- -

- There <%= @prediction.wagers.size == 1 ? 'is' : 'are' %> currently - <%= pluralize(@prediction.wagers.size, 'wager') %>, and - <%= pluralize(@prediction.comments.size, 'comment') %>. -

- -

<%= link_to "Check out what's happened", prediction_url(@prediction, token: @notification.uuid) %>

diff --git a/app/views/deliverer/response_notification.text.erb b/app/views/deliverer/response_notification.text.erb deleted file mode 100644 index eb63e750..00000000 --- a/app/views/deliverer/response_notification.text.erb +++ /dev/null @@ -1,6 +0,0 @@ -Prediction: <%= @prediction.description_with_group %> - -There <%= @prediction.wagers.size == 1 ? 'is' : 'are' %> currently <%= pluralize(@prediction.wagers, 'wager') %>, and <%= pluralize(@prediction.comments, 'comment') %>. - -Check out what's happened: -<%= prediction_url(@prediction, token: @notification.uuid) %> diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb deleted file mode 100644 index 580869f2..00000000 --- a/app/views/devise/confirmations/new.html.erb +++ /dev/null @@ -1,15 +0,0 @@ -

Resend confirmation instructions

- -<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> - <%= devise_error_messages! %> - -
- <%= f.email_field :email, autofocus: true, value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %> -
- -
- <%= f.submit "Resend confirmation instructions" %> -
-<% end %> - -<%= render "devise/shared/links" %> diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb deleted file mode 100644 index b144a1c0..00000000 --- a/app/views/devise/passwords/edit.html.erb +++ /dev/null @@ -1,22 +0,0 @@ -

Change your password

- -<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %> - <%= devise_error_messages! %> - <%= f.hidden_field :reset_password_token %> - - <% placeholder = "(#{@minimum_password_length} characters minimum)" if @minimum_password_length %> -
- <%= f.password_field :password, autocomplete: "off", placeholder: placeholder %> -
-
- -
- <%= f.password_field :password_confirmation, autocomplete: "off", placeholder: placeholder %> -
- -
- <%= f.submit "Change my password" %> -
-<% end %> - -<%= render "devise/shared/links" %> diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb deleted file mode 100644 index a8c253ef..00000000 --- a/app/views/devise/passwords/new.html.erb +++ /dev/null @@ -1,15 +0,0 @@ -

Forgot your password?

- -<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %> - <%= devise_error_messages! %> - -
- <%= f.email_field :email, autofocus: true %> -
- -
- <%= f.submit "Send me reset password instructions" %> -
-<% end %> - -<%= render "devise/shared/links" %> diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb deleted file mode 100644 index 465aff02..00000000 --- a/app/views/devise/registrations/edit.html.erb +++ /dev/null @@ -1,38 +0,0 @@ -

Change password

- -<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> - <%= devise_error_messages! %> - - <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> -
Currently waiting confirmation for: <%= resource.unconfirmed_email %>
- <% end %> - - <% if resource.email.nil? %> -
- <%= f.email_field :email, placeholder: 'Please provide an email address' %> -
- <% end %> - - <% placeholder = "(#{@minimum_password_length} characters minimum)" if @minimum_password_length %> -
- <%= f.password_field :password, autocomplete: "off", placeholder: placeholder %> -
- -
- <%= f.password_field :password_confirmation, autocomplete: "off", placeholder: placeholder %> -
- -
- <%= f.password_field :current_password, autocomplete: "off", placeholder: 'confirm with your current password' %> -
- - <% # This is clearing the old password and salt fields %> - <%= f.hidden_field :crypted_password, value: '' %> - <%= f.hidden_field :salt, value: '' %> - -
- <%= f.submit "Update" %> -
-<% end %> - -<%= link_to "Back", :back %> diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb deleted file mode 100644 index 443407bb..00000000 --- a/app/views/devise/registrations/new.html.erb +++ /dev/null @@ -1,28 +0,0 @@ -

Sign up

- -<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> - <%= devise_error_messages! %> - -
- <%= f.text_field :login, autofocus: true %> -
- -
- <%= f.email_field :email %> -
- - <% placeholder = "(#{@minimum_password_length} characters minimum)" if @minimum_password_length %> -
- <%= f.password_field :password, autocomplete: "off", placeholder: placeholder %> -
- -
- <%= f.password_field :password_confirmation, autocomplete: "off", placeholder: placeholder %> -
- -
- <%= f.submit "Sign up" %> -
-<% end %> - -<%= render "devise/shared/links" %> diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb deleted file mode 100644 index 16586bc7..00000000 --- a/app/views/devise/unlocks/new.html.erb +++ /dev/null @@ -1,16 +0,0 @@ -

Resend unlock instructions

- -<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %> - <%= devise_error_messages! %> - -
- <%= f.label :email %>
- <%= f.email_field :email, autofocus: true %> -
- -
- <%= f.submit "Resend unlock instructions" %> -
-<% end %> - -<%= render "devise/shared/links" %> diff --git a/app/views/group_members/new.html.haml b/app/views/group_members/new.html.haml deleted file mode 100644 index 20029cfd..00000000 --- a/app/views/group_members/new.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -%h2= "Invite another user to the #{@group.name} group" - -= form_for([@group, @group_member], html: { honeypot: true }) do |f| - = label_tag :login, "Enter the login of the user you wish to invite" - = text_field_tag :login, '' - - = f.submit 'Send an invite' diff --git a/app/views/groups/_form.html.haml b/app/views/groups/_form.html.haml deleted file mode 100644 index fc9bbcc0..00000000 --- a/app/views/groups/_form.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -= form_for(@group, html: { honeypot: true }) do |f| - = f.text_field :name, label: "Name your group" - - - if @group.new_record? - = label_tag :invitees, "Enter the logins of up to 20 PredictionBook friends, one per line. Invalid logins will be ignored. You can invite more friends later. Note: the login is the slug that appears in the URL when you view someone's profile (i.e. in http://predictionbook.com/users/a_predictionbook_user, a_predictionbook_user is that user's login)." - = text_area_tag :invitees, "", rows: 20, cols: 100 - - = f.submit 'Create my group!' diff --git a/app/views/groups/index.html.haml b/app/views/groups/index.html.haml index b187db39..292f5970 100644 --- a/app/views/groups/index.html.haml +++ b/app/views/groups/index.html.haml @@ -5,5 +5,3 @@ .popular %ul = render @groups - -= button_to "Create a new group", new_group_path, method: :get diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 39360d8f..1f010caa 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -11,9 +11,6 @@ %p= link_to "Group members (#{@group.group_members.size})", group_group_members_path(@group) -- if @group_member.admin? - %p= link_to 'Delete this group', group_path(@group), method: :delete, data: { confirm: 'Are you sure you want to delete this group?' } - %p - cache(group_statistics_cache_key @group) do diff --git a/app/views/layouts/_messages.html.erb b/app/views/layouts/_messages.html.erb index ca680f44..29dfb2bb 100644 --- a/app/views/layouts/_messages.html.erb +++ b/app/views/layouts/_messages.html.erb @@ -5,11 +5,4 @@ <% if flash[:notice] %>
<%=flash[:notice] %>
<% end %> - -<% if current_user.try(:has_overdue_judgements?) %> -
- You have one or more predictions with overdue judgements. - <%= link_to 'Judge them!', due_for_judgement_user_path(current_user) %> -
-<% end %> - \ No newline at end of file + diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 1eeafec7..8d89045c 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -31,7 +31,7 @@

- PredictionBook transitioning to read-only mode at the end of 2023 + PredictionBook is now read-only ( read more ). @@ -43,17 +43,14 @@