diff --git a/app/lib/discord/events/puzzle_answer.rb b/app/lib/discord/events/puzzle_answer.rb index d08b1c5..69b77c1 100644 --- a/app/lib/discord/events/puzzle_answer.rb +++ b/app/lib/discord/events/puzzle_answer.rb @@ -40,23 +40,29 @@ def handle(event) return end - server = Server.find_or_initialize_by(server_id: event.server.id) - # Create the answer record - answer = Answer.create!( + answer = Answer.create( puzzle_id: puzzle.id, user_id: user.id, server_id: server.id, choice: answer, # 'ruby' or 'rails' is_correct: puzzle.answer.to_s == answer # Correct answer check ) - - # Respond to the user with the result - event.respond( - content: nil, - embeds: [ answer_embed(answer, puzzle) ], - ephemeral: true # Only the user sees this message - ) + if answer + # Respond to the user with the result + event.respond( + content: nil, + embeds: [ answer_embed(answer, puzzle) ], + ephemeral: true # Only the user sees this message + ) + else + # If the user has already answered, prevent them from changing their answer + event.respond( + content: "You have already answered this puzzle. You cannot change your answer.", + ephemeral: true # Only the user sees this message + ) + nil + end end private diff --git a/app/models/answer.rb b/app/models/answer.rb index 93edb8e..c5b2ea8 100644 --- a/app/models/answer.rb +++ b/app/models/answer.rb @@ -1,4 +1,6 @@ class Answer < ApplicationRecord belongs_to :puzzle belongs_to :user + + validates :puzzle_id, uniqueness: { scope: [ :user_id, :server_id ] } end diff --git a/app/models/server.rb b/app/models/server.rb index f110d8c..d536ae7 100644 --- a/app/models/server.rb +++ b/app/models/server.rb @@ -1,3 +1,4 @@ class Server < ApplicationRecord + has_and_belongs_to_many :users, join_table: "users_servers" has_one :channel end diff --git a/app/models/user.rb b/app/models/user.rb index c99c151..7a3611c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,6 +1,6 @@ class User < ApplicationRecord - has_and_belongs_to_many :servers - has_many :servers + has_and_belongs_to_many :servers, join_table: "users_servers" + has_many :answers enum :role, admin: 0, member: 1 end diff --git a/db/migrate/20250917170711_add_unique_index_to_answers.rb b/db/migrate/20250917170711_add_unique_index_to_answers.rb new file mode 100644 index 0000000..d60b710 --- /dev/null +++ b/db/migrate/20250917170711_add_unique_index_to_answers.rb @@ -0,0 +1,5 @@ +class AddUniqueIndexToAnswers < ActiveRecord::Migration[8.0] + def change + add_index :answers, [ :puzzle_id, :user_id, :server_id ], unique: true, name: 'index_answers_on_puzzle_user_server' + end +end diff --git a/db/schema.rb b/db/schema.rb index b8a12c9..f01d438 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_06_02_180541) do +ActiveRecord::Schema[8.0].define(version: 2025_09_17_170711) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -21,8 +21,8 @@ t.boolean "is_correct" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.string "link" t.bigint "server_id", null: false + t.index ["puzzle_id", "user_id", "server_id"], name: "index_answers_on_puzzle_user_server", unique: true t.index ["puzzle_id"], name: "index_answers_on_puzzle_id" t.index ["server_id"], name: "index_answers_on_server_id" t.index ["user_id"], name: "index_answers_on_user_id" diff --git a/db/seeds.rb b/db/seeds.rb index 010d1aa..45850f8 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,19 +1,47 @@ - # db/seeds.rb -# Create Puzzle records -Puzzle.create!([ - { question: "Ruby or Rails provided this method? Array.new(5) { |i| i * 2 }", answer: :ruby, explanation: "`Array.new` is a core Ruby method that creates a new array with a specified size and optional block for initialization. This is part of Ruby’s core library." }, - { question: "Ruby or Rails provided this method? File.open('log.txt', 'w') { |file| file.write('Hello, World!') }", answer: :ruby, explanation: "`File.open` is a core Ruby method for opening files. It’s part of Ruby's standard library for file handling, not part of Rails." }, - { question: "Ruby or Rails provided this method? render json: @user", answer: :rails, explanation: "`render json:` is a Rails method used to render a JSON response from a controller action." }, - { question: "Ruby or Rails provided this method? link_to 'Home', root_path", answer: :rails, explanation: "`link_to` is a Rails helper method that generates HTML links. `root_path` is a Rails path helper." }, - { question: "Ruby or Rails provided this method? params[:id]", answer: :rails, explanation: "`params[:id]` is used in Rails to fetch query parameters or URL parameters in controller actions." } -]) +# ====== Create Puzzle records ====== +puzzles = [ + { + question: "Ruby or Rails provided this method? Array.new(5) { |i| i * 2 }", + answer: :ruby, + explanation: "`Array.new` is a core Ruby method that creates a new array with a specified size and optional block for initialization. This is part of Ruby’s core library." + }, + { + question: "Ruby or Rails provided this method? File.open('log.txt', 'w') { |file| file.write('Hello, World!') }", + answer: :ruby, + explanation: "`File.open` is a core Ruby method for opening files. It’s part of Ruby's standard library for file handling, not part of Rails." + }, + { + question: "Ruby or Rails provided this method? render json: @user", + answer: :rails, + explanation: "`render json:` is a Rails method used to render a JSON response from a controller action." + }, + { + question: "Ruby or Rails provided this method? link_to 'Home', root_path", + answer: :rails, + explanation: "`link_to` is a Rails helper method that generates HTML links. `root_path` is a Rails path helper." + }, + { + question: "Ruby or Rails provided this method? params[:id]", + answer: :rails, + explanation: "`params[:id]` is used in Rails to fetch query parameters or URL parameters in controller actions." + } +] + +puzzles.each do |p| + Puzzle.find_or_create_by!(question: p[:question]) do |puzzle| + puzzle.answer = p[:answer] + puzzle.explanation = p[:explanation] + end +end -# Create a server -Server.create!(server_id: 1179555097060061245, name: "OmbuTest") +# ====== Create the Server ====== +server = Server.find_or_create_by!(server_id: 1179555097060061245) do |s| + s.name = "OmbuTest" +end -# Seed data for 10 users with answers +# ====== Create Users and associate with Server ====== users = [ { user_id: 101, username: "user1", role: "member" }, { user_id: 102, username: "user2", role: "member" }, @@ -28,16 +56,24 @@ ] users.each do |user_data| - user = User.create(user_id: user_data[:user_id], username: user_data[:username], role: user_data[:role]) + user = User.find_or_create_by!(user_id: user_data[:user_id]) do |u| + u.username = user_data[:username] + u.role = user_data[:role] + end + + # Associate user with the server if not already linked + user.servers << server unless user.servers.include?(server) - # Randomly assign scores to answers (you can adjust as needed) - 3.times do - Answer.create!( - user_id: user.id, - puzzle_id: Puzzle.all.sample.id, - server_id: Server.first.id, - choice: [ "ruby", "rails" ].sample, - is_correct: [ true, false ].sample - ) + # Seed random answers for this user if they have none + if user.answers.where(server_id: server.id).empty? + 3.times do + Answer.create!( + user_id: user.id, + puzzle_id: Puzzle.all.sample.id, + server_id: server.id, + choice: [ "ruby", "rails" ].sample, + is_correct: [ true, false ].sample + ) + end end end