Я создаю механизм вопросов с Ruby, у меня хорошо работает элемент вопроса - пользователи могут создавать и пытаться questions
, а их попытки сохраняются в таблице question_attempts
.
Сейчас я пытаюсь создать функцию банка вопросов, в которой пользователи могут фильтровать все вопросы по нескольким различным параметрам - тегам, сложности и результату их последней попытки (если она существует). И фильтрация по тегу (через отдельную таблицу tags
и таблицу объединения taggings
), и сложность (строка из четырех параметров, сохраненных в таблице question
) работают должным образом.
У меня возникли проблемы с вопросами фильтра, основанными на попытках пользователя. Мне нужно иметь возможность фильтровать для трех разных условий:
- Исправить - где question.attempt.score == 100
- Неверно - где question.attempt.score <100 </li>
- Не предпринималось попыток - там, где question.attempt == ноль / не существует
Модели question_attempts
belongs_to: user
и belongs_to: question
. score
от их попытки сохраняется как стандартизированный процент с плавающей точкой с максимальным значением 100.0.
Для первых двух я могу использовать ту же методологию, что и для тегов, обе из них работают:
.where(question_attempts: { score: 100, user_id: current_user.id }
.where.not(question_attempts: { score: 100 }).where(user_id: current_user.id })
Я могу получить вопросы, на которые отдельный пользователь не отправил ответ, используя следующую строку:
.where.not(id: QuestionAttempt.where( user_id: current_user.id ).select( "question_id" ) )
Мои проблемы:
- Как мне объединить эти параметры фильтра? Я пытаюсь избежать написания длинной серии операторов
if else
, но сделаю это при необходимости (я могу сделать это без посторонней помощи, просто хочу проверить нет лучшего способа до взлома)
- Как ограничить проверку результатов только последней попыткой? , т.е. если они ответили на вопрос 2+ раза, я хочу проверить результат только самой последней. В этом ответе мне хотелось бы использовать решение Марка Свардстрома, в первую очередь потому, что я его понимаю, но обеспокоен, что это будет неэффективный метод.
Спасибо за вашу помощь
Дайте мне знать, если вам нужно больше деталей.
Модель
class Question < ApplicationRecord
has_many :taggings, dependent: :destroy
has_many :tags, through: :taggings
has_many :question_attempts, dependent: :destroy
end
class QuestionAttempt < ApplicationRecord
belongs_to :question
belongs_to :user
end
Контроллер
# Gather topics / tags
# This is working
if params[:topics] != nil
@tags = params[:topics].split(",")
end
# Gather difficulties
# This is working as expected
# If nil, then show all difficulties
if params[:difficulties] != nil
@difficulties = params[:difficulties].split(",")
else
flash[:notice] = 'Showing all difficulties as none selected'
@difficulties = ["Untested", "Easy", "Medium", "Hard"]
end
# This is the element not yet working
# I can gather the options Unattempted / Correct / Incorrect from the params
# Not sure how to then join this with my question_attempts table
@attempts = params[:attempts].split(",")
# Gather questions matching the filters
if @tags == nil
@questions = Question.joins(:question_attempts).where(difficulty: @difficulties, private: false, draft: false, approved: true).distinct
else
@questions = Question.joins(:question_attempts).joins(:tags).where(tags: { name: @tags }, difficulty: @difficulties, private: false, draft: false, approved: true).distinct
end
Текущее решение
Вероятно, это не самый эффективный способ сделать это, но, похоже, он работает благодаря советам Тома Лорда. Приветствуются любые конструктивные отзывы / советы.
def tags
params[:topics]&.split(",")
end
def difficulties
params[:difficulties]&.split(",")
end
def filter
# Check for previous answers
if params[:correct] == "true" && params[:incorrect] != "true"
@questions = Question.where( id: QuestionAttempt.where(user_id: current_user.id).where('score = 100').pluck(:question_id) )
elsif params[:incorrect] == "true" && params[:correct] != "true"
@questions = Question.where( id: QuestionAttempt.where(user_id: current_user.id).where('score < 100').pluck(:question_id) )
elsif params[:incorrect] == "true" && params[:correct] == "true"
@questions = Question.where( id: QuestionAttempt.where(user_id: current_user.id).pluck(:question_id) )
end
# Check for unanswered filters
if params[:unattempted] == "true"
if @questions == nil
@questions = Question.where.not( id: QuestionAttempt.where( user_id: current_user.id ).pluck(:question_id) )
else
@questions = Question.where.not( id: QuestionAttempt.where( user_id: current_user.id ).pluck(:question_id) ).or (
@questions
)
end
end
# Add all other filters afterwards as they should apply to all
@questions = @questions.where(difficulty: difficulties) if difficulties
@questions = @questions.joins(:tags).where(tags: { name: tags }) if tags
@questions = @questions.where(
private: false,
draft: false,
approved: true
)
@questions.distinct