Files
puzzle-scoreboard/app/controllers/contests_controller.rb
sto cce090587a
All checks were successful
CI / scan_ruby (push) Successful in 21s
CI / scan_js (push) Successful in 14s
CI / lint (push) Successful in 14s
CI / test (push) Successful in 36s
Public scoreboard stopwatch feature
#10
2025-12-09 10:10:54 +01:00

314 lines
8.4 KiB
Ruby

class ContestsController < ApplicationController
include CompletionsConcern
include ContestantsConcern
before_action :set_contest, only: %i[ destroy show ]
before_action :set_settings_contest, only: %i[ stopwatch stopwatch_continue stopwatch_pause stopwatch_reset stopwatch_start settings_general_edit settings_general_update settings_public_edit settings_public_update settings_onsite_edit settings_onsite_update settings_online_edit settings_online_update settings_categories_edit ]
before_action :offline_setup, only: %i[ offline_new offline_create offline_edit offline_update offline_completed ]
skip_before_action :require_authentication, only: %i[ scoreboard offline_new offline_create offline_edit offline_update offline_completed ]
def index
authorize :contest
@contests = current_user.contests
@title = I18n.t("contests.index.title", username: current_user.username)
end
def show
authorize @contest
redirect_to contest_contestants_path(@contest)
end
def settings_general_edit
authorize @contest
end
def settings_public_edit
authorize @contest
end
def settings_onsite_edit
authorize @contest
end
def settings_online_edit
authorize @contest
end
def settings_categories_edit
authorize @contest
end
def settings_general_update
authorize @contest
if @contest.update(settings_general_params)
redirect_to "/contests/#{@contest.id}/settings/general", notice: t("contests.edit.notice")
else
render :settings_general_edit, status: :unprocessable_entity
end
end
def settings_public_update
authorize @contest
if @contest.update(settings_public_params)
redirect_to "/contests/#{@contest.id}/settings/public", notice: t("contests.edit.notice")
else
render :settings_public_edit, status: :unprocessable_entity
end
end
def settings_onsite_update
authorize @contest
if @contest.update(settings_onsite_params)
redirect_to "/contests/#{@contest.id}/settings/onsite", notice: t("contests.edit.notice")
else
render :settings_onsite_edit, status: :unprocessable_entity
end
end
def settings_online_update
authorize @contest
if @contest.update(settings_online_params)
redirect_to "/contests/#{@contest.id}/settings/online", notice: t("contests.edit.notice")
else
render :settings_online_edit, status: :unprocessable_entity
end
end
def stopwatch
authorize @contest
end
def stopwatch_continue
authorize @contest
pause_duration = Time.now() - @contest.pause_time
@contest.start_time = @contest.start_time + pause_duration
@contest.pause_time = nil
@contest.save
redirect_to "/contests/#{@contest.id}/stopwatch"
end
def stopwatch_pause
authorize @contest
authorize @contest
@contest.pause_time = Time.now()
@contest.save
redirect_to "/contests/#{@contest.id}/stopwatch"
end
def stopwatch_reset
authorize @contest
@contest.start_time = nil
@contest.pause_time = nil
@contest.save
redirect_to "/contests/#{@contest.id}/stopwatch"
end
def stopwatch_start
authorize @contest
@contest.start_time = Time.now()
@contest.pause_time = nil
@contest.save
redirect_to "/contests/#{@contest.id}/stopwatch"
end
def new
authorize :contest
@contest = Contest.new
@title = I18n.t("contests.new.title")
@nonav = true
end
def create
authorize :contest
@contest = Contest.new(new_contest_params)
@contest.lang = @current_user.lang
@contest.ranking_mode = "actual"
@contest.user_id = current_user.id
if @contest.save
redirect_to "/contests/#{@contest.id}/settings/general", notice: t("contests.new.notice")
else
@title = I18n.t("contests.new.title")
@nonav = true
render :new, status: :unprocessable_entity
end
end
def destroy
authorize @contest
@contest.destroy
redirect_to contests_path, notice: t("contests.destroy.notice")
end
def scoreboard
@contest = Contest.find_by(slug: params[:id])
unless @contest && @contest.public
skip_authorization
not_found and return
end
authorize @contest
I18n.locale = @contest.lang
@title = I18n.t("contests.scoreboard.title", name: @contest.name)
@contestants = ranked_contestants(@contest)
if params.key?(:category)
@category = params[:category]
filter_contestants_per_category
end
if params.key?(:hide_offline) && params[:hide_offline] == "true"
@contestants = @contestants.select { |contestant| !contestant.offline.present? }
@hide_offline = true
end
if params.key?(:autorefresh)
@autorefresh = true
end
@puzzles = @contest.puzzles.where(hidden: false).or(@contest.puzzles.where(hidden: nil)).order(:id)
@action_path = "/public/#{@contest.friendly_id}"
@space = " "
render :scoreboard
end
def offline_new
authorize @contest
@offline = Offline.new
end
def offline_create
authorize @contest
@offline = Offline.new(offline_start_params)
@offline.contest = @contest
@offline.start_time = Time.now()
if @offline.save
redirect_to "/public/#{@contest.friendly_id}/offline/#{@offline.generate_token_for(:token)}"
else
render :offline_new, status: :unprocessable_entity
end
end
def offline_edit
authorize @contest
@offline = Offline.find_by_token_for(:token, params[:token])
if !@offline
not_found and return
end
if @offline.submitted
render :offline_already_submitted and return
end
end
def offline_update
authorize @contest
@offline = Offline.find_by_token_for(:token, params[:token])
if !@offline
not_found and return
end
@offline.submitted = true
@offline.completed = params[:offline][:completed]
@offline.end_time = Time.now()
@offline.images.attach(params[:offline][:end_image])
if @offline.completed
@offline.missing_pieces = params[:offline][:missing_pieces]
else
@offline.remaining_pieces = params[:offline][:remaining_pieces]
end
if @offline.save
if @contest.puzzles.length > 0
dp = display_time(@offline.end_time.to_i - @offline.start_time.to_i)
contestant = Contestant.create(contest: @contest, name: @offline.name, offline: @offline)
Completion.create(contest: @contest,
contestant: contestant,
offline: @offline,
puzzle: @contest.puzzles[0],
completed: @offline.completed,
display_time_from_start: dp,
missing_pieces: @offline.missing_pieces,
remaining_pieces: @offline.remaining_pieces)
extend_completions!(contestant)
end
redirect_to "/public/#{@contest.friendly_id}/offline/#{@offline.generate_token_for(:token)}/completed"
else
render :offline_edit, status: :unprocessable_entity
end
end
def offline_completed
authorize @contest
@offline = Offline.find_by_token_for(:token, params[:token])
if !@offline
not_found and return
end
end
private
def offline_setup
@contest = Contest.find_by(slug: params[:id])
I18n.locale = @contest.lang
@title = I18n.t("contests.scoreboard.title", name: @contest.name)
end
def set_contest
@contest = Contest.find(params[:id])
end
def set_settings_contest
@contest = Contest.find(params[:contest_id])
end
def new_contest_params
params.expect(contest: [ :name, :duration ])
end
def settings_general_params
params.expect(contest: [ :lang, :name, :duration, :team, :allow_registration ])
end
def settings_public_params
params.expect(contest: [ :public, :ranking_mode, :show_stopwatch ])
end
def settings_onsite_params
params.expect(contest: [ :code ])
end
def settings_online_params
params.expect(contest: [ :offline_form ])
end
def filter_contestants_per_category
if @category != "-1"
if @category == "-2"
@contestants = @contestants.select { |contestant| contestant.categories.size == 0 }
else
@contestants = @contestants.select { |contestant| contestant.categories.where(id: @category).any? }
end
end
end
def offline_start_params
params.expect(offline: [ :name, :images ])
end
def offline_end_params
params.expect(offline: [ :completed, :end_image, :remaining_pieces, :missing_pieces ])
end
end