Skip to content

Commit 16bcce1

Browse files
DimitrijeDimitrijevicDimitrije Dimitrijevic
andauthored
Fix duplicate key quiz when duplicate (#182)
* add quiz_responses association to user * bugfix possible duplicate key entries in multi when adding quiz responses * remove user_id from casting changeset in QuizResponse * pass whole user to submit_quiz function * update test to match changes * simplify submit_quiz/3 function for inserting quiz response --------- Co-authored-by: Dimitrije Dimitrijevic <me@dimitrijedimitrijevic.com>
1 parent fc667bb commit 16bcce1

File tree

5 files changed

+56
-20
lines changed

5 files changed

+56
-20
lines changed

lib/claper/accounts/user.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ defmodule Claper.Accounts.User do
3030

3131
has_many :events, Claper.Events.Event
3232
has_one :lti_user, Lti13.Users.User
33+
has_many :quiz_responses, Claper.Quizzes.QuizResponse
3334

3435
timestamps()
3536
end

lib/claper/quizzes.ex

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ defmodule Claper.Quizzes do
22
import Ecto.Query, warn: false
33
alias Claper.Repo
44

5+
alias Claper.Accounts.User
6+
57
alias Claper.Quizzes.Quiz
68
alias Claper.Quizzes.QuizQuestion
79
alias Claper.Quizzes.QuizQuestionOpt
@@ -265,37 +267,71 @@ defmodule Claper.Quizzes do
265267
{:ok, quiz}
266268
267269
"""
268-
def submit_quiz(user_id, quiz_opts, quiz_id)
269-
when is_number(user_id) and is_list(quiz_opts) do
270-
case Enum.reduce(quiz_opts, Ecto.Multi.new(), fn opt, multi ->
271-
Ecto.Multi.update(
272-
multi,
273-
{:update_quiz_opt, opt.id},
270+
271+
# Pattern match on user, from user we create QuizResponse struct
272+
# def submit_quiz(user_id, quiz_opts, quiz_id)
273+
# when is_number(user_id) and is_list(quiz_opts) do
274+
# quiz_opts = Enum.with_index(quiz_opts)
275+
276+
# case Enum.reduce(quiz_opts, Ecto.Multi.new(), fn {opt, index}, multi ->
277+
# unique_key = "#{opt.id}_#{user_id + index}"
278+
279+
# multi
280+
# |> Ecto.Multi.update(
281+
# "update_quiz_opt_#{unique_key}",
282+
# QuizQuestionOpt.changeset(opt, %{"response_count" => opt.response_count + 1})
283+
# )
284+
# |> Ecto.Multi.insert(
285+
# "insert_quiz_response_#{unique_key}",
286+
# QuizResponse.changeset(%QuizResponse{}, %{
287+
# user_id: user_id,
288+
# quiz_question_opt_id: opt.id,
289+
# quiz_question_id: opt.quiz_question_id,
290+
# quiz_id: quiz_id
291+
# })
292+
# )
293+
# end)
294+
# |> Repo.transact() do
295+
# {:ok, _} ->
296+
# quiz = get_quiz!(quiz_id, [:quiz_questions, quiz_questions: :quiz_question_opts])
297+
# Lti13.QuizScoreReporter.report_quiz_score(quiz, user_id)
298+
# {:ok, quiz}
299+
# end
300+
# end
301+
302+
def submit_quiz(%User{} = user, quiz_opts, quiz_id) do
303+
quiz_opts = Enum.with_index(quiz_opts)
304+
305+
case Enum.reduce(quiz_opts, Ecto.Multi.new(), fn {opt, index}, multi ->
306+
unique_key = "#{opt.id}_#{user.id + index}"
307+
308+
multi
309+
|> Ecto.Multi.update(
310+
"update_quiz_opt_#{unique_key}",
274311
QuizQuestionOpt.changeset(opt, %{"response_count" => opt.response_count + 1})
275312
)
276313
|> Ecto.Multi.insert(
277-
{:insert_quiz_response, opt.id},
278-
QuizResponse.changeset(%QuizResponse{}, %{
279-
user_id: user_id,
314+
"insert_quiz_response_#{unique_key}",
315+
Ecto.build_assoc(user, :quiz_responses, %{
280316
quiz_question_opt_id: opt.id,
281317
quiz_question_id: opt.quiz_question_id,
282318
quiz_id: quiz_id
283319
})
284320
)
285321
end)
286-
|> Repo.transaction() do
322+
|> Repo.transact() do
287323
{:ok, _} ->
288324
quiz = get_quiz!(quiz_id, [:quiz_questions, quiz_questions: :quiz_question_opts])
289-
Lti13.QuizScoreReporter.report_quiz_score(quiz, user_id)
325+
Lti13.QuizScoreReporter.report_quiz_score(quiz, user.id)
290326
{:ok, quiz}
291327
end
292328
end
293329

294330
def submit_quiz(attendee_identifier, quiz_opts, quiz_id)
295331
when is_binary(attendee_identifier) and is_list(quiz_opts) do
296332
case Enum.reduce(quiz_opts, Ecto.Multi.new(), fn opt, multi ->
297-
Ecto.Multi.update(
298-
multi,
333+
multi
334+
|> Ecto.Multi.update(
299335
{:update_quiz_opt, opt.id},
300336
QuizQuestionOpt.changeset(opt, %{"response_count" => opt.response_count + 1})
301337
)
@@ -309,7 +345,7 @@ defmodule Claper.Quizzes do
309345
})
310346
)
311347
end)
312-
|> Repo.transaction() do
348+
|> Repo.transact() do
313349
{:ok, _} ->
314350
quiz = get_quiz!(quiz_id, [:quiz_questions, quiz_questions: :quiz_question_opts])
315351
{:ok, quiz}

lib/claper/quizzes/quiz_response.ex

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ defmodule Claper.Quizzes.QuizResponse do
2020
:attendee_identifier,
2121
:quiz_id,
2222
:quiz_question_id,
23-
:quiz_question_opt_id,
24-
:user_id
23+
:quiz_question_opt_id
2524
])
2625
|> validate_required([:quiz_id, :quiz_question_id, :quiz_question_opt_id])
2726
end

lib/claper_web/live/event_live/show.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,7 @@ defmodule ClaperWeb.EventLive.Show do
693693
)
694694
when is_map(current_user) do
695695
case Claper.Quizzes.submit_quiz(
696-
current_user.id,
696+
current_user,
697697
opts,
698698
socket.assigns.current_interaction.id
699699
) do

test/claper/quizzes_test.exs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,14 @@ defmodule Claper.QuizzesTest do
113113
assert length(new_question.quiz_question_opts) == 2
114114
end
115115

116-
test "submit_quiz/3 with user_id records responses and updates counts" do
116+
test "submit_quiz/3 with user records responses and updates counts" do
117117
quiz = quiz_fixture()
118118
user = user_fixture()
119119
question = List.first(quiz.quiz_questions)
120120
option = List.first(question.quiz_question_opts)
121121

122122
assert {:ok, updated_quiz} =
123-
Quizzes.submit_quiz(user.id, [option], quiz.id)
123+
Quizzes.submit_quiz(user, [option], quiz.id)
124124

125125
updated_option =
126126
updated_quiz.quiz_questions
@@ -151,7 +151,7 @@ defmodule Claper.QuizzesTest do
151151
correct_option = Enum.find(question.quiz_question_opts, & &1.is_correct)
152152

153153
# Submit correct answer
154-
{:ok, _} = Quizzes.submit_quiz(user.id, [correct_option], quiz.id)
154+
{:ok, _} = Quizzes.submit_quiz(user, [correct_option], quiz.id)
155155
assert {1, 1} = Quizzes.calculate_user_score(user.id, quiz.id)
156156
end
157157

0 commit comments

Comments
 (0)