From e9ef73c2302452b5c7d38fffcf5bb1a36a64917c Mon Sep 17 00:00:00 2001 From: Malaber Date: Fri, 24 Apr 2026 11:39:26 +0200 Subject: [PATCH] refactor: rename timer db column --- app/controllers/tournaments_controller.rb | 12 +- app/models/tournament.rb | 2 +- app/serializers/tournament_serializer.rb | 2 +- app/services/tournament_snapshot_builder.rb | 2 +- app/services/tournament_snapshot_importer.rb | 2 +- app/services/tournament_sync_schema.rb | 2 +- .../tournament_timer_broadcast_payload.rb | 2 +- ...e_timer_end_to_timestamp_on_tournaments.rb | 7 + db/schema.rb | 223 ++++++++++++++++++ .../tournaments_controller_spec.rb | 16 +- .../tournament_snapshot_importer_spec.rb | 2 +- 11 files changed, 251 insertions(+), 21 deletions(-) create mode 100644 db/migrate/20260424120000_rename_timer_end_to_timestamp_on_tournaments.rb create mode 100644 db/schema.rb diff --git a/app/controllers/tournaments_controller.rb b/app/controllers/tournaments_controller.rb index d2e4233..5fe10f0 100644 --- a/app/controllers/tournaments_controller.rb +++ b/app/controllers/tournaments_controller.rb @@ -173,14 +173,14 @@ class TournamentsController < ApplicationController def timer_params { - timer_end: params[:timer_end], + timestamp: params[:timestamp], timer_mode: params[:timer_mode] } end def serialize_timer_state(tournament) { - timestamp: tournament.timer_end&.iso8601, + timestamp: tournament.timestamp&.iso8601, timer_mode: tournament.timer_mode } end @@ -344,7 +344,7 @@ class TournamentsController < ApplicationController status: :unprocessable_entity end - params[:timer_end] = nil + params[:timestamp] = nil params[:timer_mode] = nil return end @@ -372,7 +372,7 @@ class TournamentsController < ApplicationController return render json: { error: 'Timestamp must be in the future for countdown mode' }, status: :unprocessable_entity if timestamp_seconds <= 0 parsed_time = Time.zone.now + timestamp_seconds - params[:timer_end] = parsed_time + params[:timestamp] = parsed_time params[:timer_mode] = 'countdown' elsif timestamp.present? begin @@ -383,13 +383,13 @@ class TournamentsController < ApplicationController return render json: { error: 'Timestamp must be in the future for countdown mode' }, status: :unprocessable_entity end - params[:timer_end] = parsed_time + params[:timestamp] = parsed_time params[:timer_mode] = timer_mode.presence || 'countdown' rescue ArgumentError return render json: { error: 'Invalid timestamp format' }, status: :unprocessable_entity end elsif timer_mode == 'countup' - params[:timer_end] = Time.zone.now + params[:timestamp] = Time.zone.now elsif timer_mode.present? return render json: { error: 'timer_mode requires timestamp, timestamp_seconds, or clear_timer' }, status: :unprocessable_entity diff --git a/app/models/tournament.rb b/app/models/tournament.rb index 653f738..65c22d0 100644 --- a/app/models/tournament.rb +++ b/app/models/tournament.rb @@ -96,7 +96,7 @@ class Tournament < ApplicationRecord end def saved_change_to_timer_state? - saved_change_to_timer_end? || saved_change_to_timer_mode? + saved_change_to_timestamp? || saved_change_to_timer_mode? end def broadcast_timer_state_change diff --git a/app/serializers/tournament_serializer.rb b/app/serializers/tournament_serializer.rb index e5d525a..46009a1 100644 --- a/app/serializers/tournament_serializer.rb +++ b/app/serializers/tournament_serializer.rb @@ -12,7 +12,7 @@ class TournamentSerializer < SimpleTournamentSerializer has_many :team_action_lists attribute :timestamp do - object.timer_end&.iso8601 + object.timestamp&.iso8601 end attribute :timer_mode diff --git a/app/services/tournament_snapshot_builder.rb b/app/services/tournament_snapshot_builder.rb index 9932cc8..8ca4a26 100644 --- a/app/services/tournament_snapshot_builder.rb +++ b/app/services/tournament_snapshot_builder.rb @@ -22,7 +22,7 @@ class TournamentSnapshotBuilder def build payload = tournament.attributes.symbolize_keys.slice(*ROOT_ATTRIBUTES) - payload[:timestamp] = tournament.timer_end&.iso8601 + payload[:timestamp] = tournament.timestamp&.iso8601 payload[:timer_mode] = tournament.timer_mode payload[:sync_generated_at] = Time.current.iso8601(6) payload[:teams] = teams_payload diff --git a/app/services/tournament_snapshot_importer.rb b/app/services/tournament_snapshot_importer.rb index 3108d5e..d03beba 100644 --- a/app/services/tournament_snapshot_importer.rb +++ b/app/services/tournament_snapshot_importer.rb @@ -67,7 +67,7 @@ class TournamentSnapshotImporter def update_tournament! root_attrs = snapshot.slice(*ROOT_ATTRIBUTES) root_attrs[:code] = tournament.code if code_taken_by_another_tournament?(root_attrs[:code]) - root_attrs[:timer_end] = parse_time(snapshot[:timestamp]) + root_attrs[:timestamp] = parse_time(snapshot[:timestamp]) root_attrs[:timer_mode] = snapshot[:timer_mode] root_attrs[:sync_source_tournament_id] = snapshot[:id] root_attrs[:sync_last_imported_snapshot_at] = snapshot_generated_at diff --git a/app/services/tournament_sync_schema.rb b/app/services/tournament_sync_schema.rb index bf03726..3d85a2d 100644 --- a/app/services/tournament_sync_schema.rb +++ b/app/services/tournament_sync_schema.rb @@ -12,7 +12,7 @@ class TournamentSyncSchema playoff_teams_amount instant_finalists_amount intermediate_round_participants_amount - timer_end + timestamp timer_mode ].freeze, ignored: %w[ diff --git a/app/services/tournament_timer_broadcast_payload.rb b/app/services/tournament_timer_broadcast_payload.rb index 97fa866..9b790de 100644 --- a/app/services/tournament_timer_broadcast_payload.rb +++ b/app/services/tournament_timer_broadcast_payload.rb @@ -6,7 +6,7 @@ class TournamentTimerBroadcastPayload { type: 'timer.updated', tournament_id: tournament.id, - timestamp: tournament.timer_end&.iso8601, + timestamp: tournament.timestamp&.iso8601, timer_mode: tournament.timer_mode } end diff --git a/db/migrate/20260424120000_rename_timer_end_to_timestamp_on_tournaments.rb b/db/migrate/20260424120000_rename_timer_end_to_timestamp_on_tournaments.rb new file mode 100644 index 0000000..d02680c --- /dev/null +++ b/db/migrate/20260424120000_rename_timer_end_to_timestamp_on_tournaments.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class RenameTimerEndToTimestampOnTournaments < ActiveRecord::Migration[7.0] + def change + rename_column :tournaments, :timer_end, :timestamp + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..656b26b --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,223 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema[7.0].define(version: 2026_04_24_120000) do + create_table "beamers", force: :cascade do |t| + t.string "name", null: false + t.string "display_state", default: "matches_all", null: false + t.boolean "is_default", default: false + t.string "qr_code_url" + t.string "logo_url" + t.integer "tournament_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "sync_source_id" + t.index ["tournament_id", "name"], name: "index_beamers_on_tournament_id_and_name", unique: true + t.index ["tournament_id"], name: "index_beamers_on_tournament_id" + end + + create_table "bets", force: :cascade do |t| + t.integer "user_id", null: false + t.integer "match_id", null: false + t.integer "team_id" + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false + t.index ["match_id"], name: "index_bets_on_match_id" + t.index ["team_id"], name: "index_bets_on_team_id" + t.index ["user_id"], name: "index_bets_on_user_id" + end + + create_table "group_scores", force: :cascade do |t| + t.integer "group_points", default: 0 + t.integer "scored_points", default: 0 + t.integer "received_points", default: 0 + t.integer "team_id", null: false + t.integer "group_id", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false + t.integer "position", default: 0 + t.integer "sync_source_id" + t.index ["group_id"], name: "index_group_scores_on_group_id" + t.index ["team_id"], name: "index_group_scores_on_team_id" + end + + create_table "groups", force: :cascade do |t| + t.integer "number" + t.integer "stage_id", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false + t.integer "sync_source_id" + t.index ["stage_id"], name: "index_groups_on_stage_id" + end + + create_table "match_scores", force: :cascade do |t| + t.integer "points", default: 0 + t.integer "match_id", null: false + t.integer "team_id", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false + t.integer "hidden_points", default: 0, null: false + t.integer "sync_source_id" + t.index ["match_id"], name: "index_match_scores_on_match_id" + t.index ["team_id"], name: "index_match_scores_on_team_id" + end + + create_table "matches", force: :cascade do |t| + t.integer "state", default: 0 + t.integer "position" + t.integer "stage_id" + t.integer "group_id" + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false + t.boolean "decider", default: false, null: false + t.integer "sync_source_id" + t.index ["group_id"], name: "index_matches_on_group_id" + t.index ["stage_id"], name: "index_matches_on_stage_id" + end + + create_table "stages", force: :cascade do |t| + t.integer "level" + t.integer "state", default: 0 + t.integer "tournament_id", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false + t.integer "sync_source_id" + t.index ["tournament_id"], name: "index_stages_on_tournament_id" + end + + create_table "team_action_items", force: :cascade do |t| + t.integer "team_action_list_id", null: false + t.integer "team_id", null: false + t.boolean "completed", default: false, null: false + t.datetime "completed_at" + t.integer "sync_source_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["sync_source_id"], name: "index_team_action_items_on_sync_source_id" + t.index ["team_action_list_id", "team_id"], name: "index_team_action_items_on_team_action_list_id_and_team_id", unique: true + t.index ["team_action_list_id"], name: "index_team_action_items_on_team_action_list_id" + t.index ["team_id"], name: "index_team_action_items_on_team_id" + end + + create_table "team_action_lists", force: :cascade do |t| + t.integer "tournament_id", null: false + t.string "name", null: false + t.string "key", null: false + t.string "action_name", null: false + t.string "source", null: false + t.integer "sync_source_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["sync_source_id"], name: "index_team_action_lists_on_sync_source_id" + t.index ["tournament_id", "key"], name: "index_team_action_lists_on_tournament_id_and_key", unique: true + t.index ["tournament_id"], name: "index_team_action_lists_on_tournament_id" + end + + create_table "teams", force: :cascade do |t| + t.string "name" + t.integer "tournament_id" + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false + t.integer "sync_source_id" + t.index ["tournament_id"], name: "index_teams_on_tournament_id" + end + + create_table "tournament_sync_queue_entries", force: :cascade do |t| + t.integer "tournament_id", null: false + t.text "snapshot_json", null: false + t.datetime "next_attempt_at", null: false + t.datetime "last_attempt_at" + t.datetime "locked_at" + t.string "lock_token" + t.integer "attempts", default: 0, null: false + t.string "status", default: "pending", null: false + t.string "last_error" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["next_attempt_at"], name: "index_tournament_sync_queue_entries_on_next_attempt_at" + t.index ["status"], name: "index_tournament_sync_queue_entries_on_status" + t.index ["tournament_id"], name: "index_tournament_sync_queue_entries_on_tournament_id", unique: true + end + + create_table "tournaments", force: :cascade do |t| + t.string "name", null: false + t.string "code", null: false + t.string "description" + t.boolean "public", default: true + t.integer "playoff_teams_amount", default: 0 + t.integer "instant_finalists_amount", default: 0 + t.integer "intermediate_round_participants_amount", default: 0 + t.integer "user_id", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false + t.datetime "timestamp" + t.boolean "read_only_mode", default: false, null: false + t.string "sync_target_url" + t.string "sync_auth_token" + t.integer "sync_source_tournament_id" + t.datetime "sync_last_pushed_at" + t.string "sync_last_push_error" + t.datetime "sync_last_imported_snapshot_at" + t.string "timer_mode" + t.index ["code"], name: "index_tournaments_on_code", unique: true + t.index ["user_id"], name: "index_tournaments_on_user_id" + end + + create_table "users", force: :cascade do |t| + t.string "provider", default: "email", null: false + t.string "uid", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "reset_password_token" + t.datetime "reset_password_sent_at", precision: nil + t.boolean "allow_password_change", default: false + t.datetime "remember_created_at", precision: nil + t.integer "sign_in_count", default: 0, null: false + t.datetime "current_sign_in_at", precision: nil + t.datetime "last_sign_in_at", precision: nil + t.string "current_sign_in_ip" + t.string "last_sign_in_ip" + t.string "confirmation_token" + t.datetime "confirmed_at", precision: nil + t.datetime "confirmation_sent_at", precision: nil + t.string "unconfirmed_email" + t.string "username" + t.string "email" + t.text "tokens" + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false + t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true + t.index ["email"], name: "index_users_on_email", unique: true + t.index ["provider"], name: "index_users_on_provider" + t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true + t.index ["uid"], name: "index_users_on_uid", unique: true + t.index ["username"], name: "index_users_on_username", unique: true + end + + add_foreign_key "beamers", "tournaments", on_delete: :cascade + add_foreign_key "bets", "matches", on_delete: :cascade + add_foreign_key "bets", "teams", on_delete: :cascade + add_foreign_key "bets", "users", on_delete: :cascade + add_foreign_key "group_scores", "groups", on_delete: :cascade + add_foreign_key "group_scores", "teams", on_delete: :cascade + add_foreign_key "groups", "stages", on_delete: :cascade + add_foreign_key "match_scores", "matches", on_delete: :cascade + add_foreign_key "match_scores", "teams", on_delete: :cascade + add_foreign_key "matches", "groups", on_delete: :cascade + add_foreign_key "matches", "stages", on_delete: :cascade + add_foreign_key "stages", "tournaments", on_delete: :cascade + add_foreign_key "team_action_items", "team_action_lists", on_delete: :cascade + add_foreign_key "team_action_items", "teams", on_delete: :cascade + add_foreign_key "team_action_lists", "tournaments", on_delete: :cascade + add_foreign_key "teams", "tournaments", on_delete: :cascade + add_foreign_key "tournament_sync_queue_entries", "tournaments", on_delete: :cascade + add_foreign_key "tournaments", "users", on_delete: :cascade +end diff --git a/spec/controllers/tournaments_controller_spec.rb b/spec/controllers/tournaments_controller_spec.rb index dd263ca..6ba0ed7 100644 --- a/spec/controllers/tournaments_controller_spec.rb +++ b/spec/controllers/tournaments_controller_spec.rb @@ -222,13 +222,13 @@ RSpec.describe TournamentsController, type: :controller do describe 'GET #timer' do it 'returns timer state in ISO 8601 format' do - timer_end = Time.utc(2026, 4, 13, 18, 0, 0) - @tournament.update!(timer_end: timer_end, timer_mode: 'countdown') + timestamp = Time.utc(2026, 4, 13, 18, 0, 0) + @tournament.update!(timestamp: timestamp, timer_mode: 'countdown') get :timer, params: { id: @tournament.to_param } expect(response).to be_successful - expect(deserialize_response(response)[:timestamp]).to eq(timer_end.iso8601) + expect(deserialize_response(response)[:timestamp]).to eq(timestamp.iso8601) expect(deserialize_response(response)[:timer_mode]).to eq('countdown') end end @@ -244,8 +244,8 @@ RSpec.describe TournamentsController, type: :controller do patch :set_timer, params: { id: @tournament.to_param, timestamp: timestamp } expect(response).to be_successful - expect(@tournament.reload.timer_end.iso8601).to eq(Time.iso8601(timestamp).utc.iso8601) - expect(deserialize_response(response)[:timestamp]).to eq(@tournament.timer_end.iso8601) + expect(@tournament.reload.timestamp.iso8601).to eq(Time.iso8601(timestamp).utc.iso8601) + expect(deserialize_response(response)[:timestamp]).to eq(@tournament.timestamp.iso8601) expect(deserialize_response(response)[:timer_mode]).to eq('countdown') end @@ -256,17 +256,17 @@ RSpec.describe TournamentsController, type: :controller do expect(response).to be_successful expect(@tournament.reload.timer_mode).to eq('countup') - expect(@tournament.timer_end).to be_between(before_request - 1.second, Time.zone.now + 1.second) + expect(@tournament.timestamp).to be_between(before_request - 1.second, Time.zone.now + 1.second) expect(deserialize_response(response)[:timer_mode]).to eq('countup') end it 'clears timer state when clear_timer=true' do - @tournament.update!(timer_end: Time.utc(2030, 4, 13, 18, 0, 0), timer_mode: 'countdown') + @tournament.update!(timestamp: Time.utc(2030, 4, 13, 18, 0, 0), timer_mode: 'countdown') patch :set_timer, params: { id: @tournament.to_param, clear_timer: true } expect(response).to be_successful - expect(@tournament.reload.timer_end).to be_nil + expect(@tournament.reload.timestamp).to be_nil expect(@tournament.timer_mode).to be_nil expect(deserialize_response(response)[:timestamp]).to be_nil expect(deserialize_response(response)[:timer_mode]).to be_nil diff --git a/spec/services/tournament_snapshot_importer_spec.rb b/spec/services/tournament_snapshot_importer_spec.rb index 4c9d1ef..29292ee 100644 --- a/spec/services/tournament_snapshot_importer_spec.rb +++ b/spec/services/tournament_snapshot_importer_spec.rb @@ -183,7 +183,7 @@ RSpec.describe TournamentSnapshotImporter do playoff_teams_amount: tournament.playoff_teams_amount, instant_finalists_amount: tournament.instant_finalists_amount, intermediate_round_participants_amount: tournament.intermediate_round_participants_amount, - timestamp: tournament.timer_end&.iso8601, + timestamp: tournament.timestamp&.iso8601, timer_mode: tournament.timer_mode, teams: tournament.teams.order(:name).map { |team| team.name }, team_action_lists: tournament.team_action_lists.order(:key).map do |list|