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

WCA Live Integration Pre Work #2: Add liveResult and liveAttempt models #10685

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions app/jobs/add_live_result_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,22 @@ class AddLiveResultJob < ApplicationJob
self.queue_adapter = :shoryuken if WcaLive.sqs_queued?
queue_as EnvConfig.LIVE_QUEUE if WcaLive.sqs_queued?

# params: { results, round_id, user_id, entered_by }
def perform(params)
FinnIckler marked this conversation as resolved.
Show resolved Hide resolved
ActionCable.server.broadcast(WcaLive.broadcast_key(params[:round_id]),
{ attempts: params[:results], user_id: params[:user_id] })
results = params[:results]
attempts = results.map.with_index(1) { |r, i| LiveAttempt.build(result: r, attempt_number: i) }
round = Round.find(params[:round_id])
event = round.event
format = round.format

r = Result.build({ value1: results[0], value2: results[1] || 0, value3: results[2] || 0, value4: results[3] || 0, value5: results[4] || 0, event_id: event.id, round_type_id: round.round_type_id, format_id: format.id })
FinnIckler marked this conversation as resolved.
Show resolved Hide resolved

LiveResult.create!(registration_id: params[:registration_id],
round: round,
live_attempts: attempts,
entered_by: params[:entered_by],
entered_at: Time.now.utc,
best: r.compute_correct_best,
average: r.compute_correct_average)
end
end
26 changes: 26 additions & 0 deletions app/models/live_attempt.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

class LiveAttempt < ApplicationRecord
include Comparable

belongs_to :live_result
# If the Attempt has been replaced, it no longer points to a live_result, but instead is being pointed to
FinnIckler marked this conversation as resolved.
Show resolved Hide resolved
# by another Attempt
belongs_to :replaced_by, class_name: "LiveAttempt", optional: true

validates :result, presence: true
validates :result, numericality: { only_integer: true }
validates :attempt_number, numericality: { only_integer: true }

DEFAULT_SERIALIZE_OPTIONS = {
only: %w[attempt_number result],
}.freeze

def serializable_hash(options = nil)
super(DEFAULT_SERIALIZE_OPTIONS.merge(options || {}))
end

def <=>(other)
result <=> other.result
end
end
53 changes: 53 additions & 0 deletions app/models/live_result.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# frozen_string_literal: true

class LiveResult < ApplicationRecord
has_many :live_attempts, -> { where(replaced_by_id: nil).order(:attempt_number) }

after_save :notify_users

belongs_to :registration

belongs_to :entered_by, class_name: 'User', foreign_key: 'entered_by_id'
FinnIckler marked this conversation as resolved.
Show resolved Hide resolved

belongs_to :round

has_one :event, through: :round


DEFAULT_SERIALIZE_OPTIONS = {
only: %w[ranking registration_id round live_attempts round best average single_record_tag average_record_tag advancing advancing_questionable entered_at entered_by_id],
methods: %w[event_id attempts result_id],
include: %w[event live_attempts round],
FinnIckler marked this conversation as resolved.
Show resolved Hide resolved
}.freeze

def serializable_hash(options = nil)
super(DEFAULT_SERIALIZE_OPTIONS.merge(options || {}))
end

def result_id
id
end
FinnIckler marked this conversation as resolved.
Show resolved Hide resolved

def event_id
event.id
end

def attempts
live_attempts.order(:attempt_number)
end

def potential_score
rank_by = round.format.sort_by == 'single' ? 'best' : 'average'
complete? ? self[rank_by.to_sym] : best_possible_score
end

def complete?
live_attempts.count == round.format.expected_solve_count
end

private

def notify_users
ActionCable.server.broadcast("results_#{round_id}", serializable_hash)
end
end
1 change: 1 addition & 0 deletions app/models/registration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class Registration < ApplicationRecord
has_many :registration_payments
has_many :competition_events, through: :registration_competition_events
has_many :events, through: :competition_events
has_many :live_results
has_many :assignments, as: :registration, dependent: :delete_all
has_many :wcif_extensions, as: :extendable, dependent: :delete_all
has_many :payment_intents, as: :holder, dependent: :delete_all
Expand Down
2 changes: 2 additions & 0 deletions app/models/round.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ def format

has_many :wcif_extensions, as: :extendable, dependent: :delete_all

has_many :live_results

MAX_NUMBER = 4
validates_numericality_of :number,
only_integer: true,
Expand Down
30 changes: 30 additions & 0 deletions db/migrate/20250124154917_create_live_result_tables.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

class CreateLiveResultTables < ActiveRecord::Migration[7.2]
def change
create_table :live_results do |t|
t.references :registration, null: false, foreign_key: { to_table: :registrations }, index: true
t.references :round, null: false, foreign_key: { to_table: :rounds }, index: true
t.references :entered_by, null: false, foreign_key: { to_table: :users }
t.datetime :entered_at, null: false
t.integer :ranking
t.integer :best, null: false
t.integer :average, null: false
t.string :single_record_tag, limit: 255
t.string :average_record_tag, limit: 255
t.boolean :advancing, default: false, null: false
t.boolean :advancing_questionable, default: false, null: false
t.timestamps
end

add_index :live_results, [:registration_id, :round_id], unique: true
FinnIckler marked this conversation as resolved.
Show resolved Hide resolved

create_table :live_attempts do |t|
t.integer :result, null: false
t.integer :attempt_number, null: false
t.references :replaced_by, foreign_key: { to_table: :live_attempts }
t.references :live_result, null: false, foreign_key: { to_table: :live_results }
t.timestamps
end
end
end
30 changes: 29 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.2].define(version: 2024_11_24_050607) do
ActiveRecord::Schema[7.2].define(version: 2025_01_24_154917) do
create_table "Competitions", id: { type: :string, limit: 32, default: "" }, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
t.string "name", limit: 50, default: "", null: false
t.string "cityName", limit: 50, default: "", null: false
Expand Down Expand Up @@ -785,6 +785,33 @@
t.index ["jti"], name: "index_jwt_denylist_on_jti"
end

create_table "live_attempts", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
t.integer "result", null: false
t.integer "replaces"
t.bigint "live_result_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["live_result_id"], name: "index_live_attempts_on_live_result_id"
end

create_table "live_results", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
t.integer "registration_id", null: false
t.integer "round_id", null: false
t.integer "entered_by_id", null: false
t.integer "ranking"
t.integer "best", null: false
t.integer "average", null: false
t.string "single_record_tag", limit: 255
t.string "average_record_tag", limit: 255
t.boolean "advancing", default: false, null: false
t.boolean "advancing_questionable", default: false, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["registration_id", "round_id"], name: "index_live_results_on_registration_id_and_round_id", unique: true
t.index ["registration_id"], name: "index_live_results_on_registration_id"
t.index ["round_id"], name: "index_live_results_on_round_id"
end

create_table "locations", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
t.integer "user_id", null: false
t.integer "latitude_microdegrees"
Expand Down Expand Up @@ -1394,6 +1421,7 @@

add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "live_attempts", "live_results"
add_foreign_key "microservice_registrations", "Competitions", column: "competition_id", on_update: :cascade, on_delete: :cascade
add_foreign_key "microservice_registrations", "users"
add_foreign_key "oauth_openid_requests", "oauth_access_grants", column: "access_grant_id", on_delete: :cascade
Expand Down
2 changes: 2 additions & 0 deletions lib/database_dumper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,8 @@ def self.actions_to_column_sanitizers(columns_by_action)
),
),
}.freeze,
"live_results" => :skip_all_rows,
"live_attempts" => :skip_all_rows,
"schedule_activities" => {
where_clause: "WHERE (holder_type=\"ScheduleActivity\" AND holder_id IN (#{VISIBLE_ACTIVITY_IDS}) or id in (#{VISIBLE_ACTIVITY_IDS}))",
column_sanitizers: actions_to_column_sanitizers(
Expand Down
Loading