Skip to content

Commit

Permalink
[BUG FIX] [MER-3153] Re-implement lost quiz answers second tab
Browse files Browse the repository at this point in the history
[BUG FIX] [MER-3153] Re-implement lost quiz answers second tab
  • Loading branch information
darrensiegel authored May 2, 2024
2 parents d0a38fe + 2d01f2f commit fdd823d
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 3 deletions.
6 changes: 5 additions & 1 deletion assets/src/phoenix/activity_bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ function makeRequest(
.then((result) => continuation(result))
.catch((error) => continuation(undefined, error));
}

const saveTransform = (result: any) => {
return result.error ? Promise.reject({ message: result.message }) : Promise.resolve(result);
};
const nothingTransform = (result: any) => Promise.resolve(result);
const submissionTransform = (key: string, result: any) => {
return result.error
Expand All @@ -47,6 +49,7 @@ export const initActivityBridge = (elementId: string) => {
'PATCH',
{ partInputs: e.detail.payload },
e.detail.continuation,
saveTransform,
);
},
false,
Expand Down Expand Up @@ -94,6 +97,7 @@ export const initActivityBridge = (elementId: string) => {
'PATCH',
{ response: e.detail.payload },
e.detail.continuation,
saveTransform,
);
},
false,
Expand Down
14 changes: 12 additions & 2 deletions lib/oli/delivery/attempts/activity_lifecycle.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ defmodule Oli.Delivery.Attempts.ActivityLifecycle do
alias Oli.Delivery.Evaluation.{Explanation, ExplanationContext}

import Oli.Delivery.Attempts.Core
require Logger

@doc """
Retrieve a hint for an attempt.
Expand Down Expand Up @@ -343,10 +344,19 @@ defmodule Oli.Delivery.Attempts.ActivityLifecycle do
VALUES
#{part_input_values}
) AS batch_values (attempt_guid, response)
WHERE part_attempts.attempt_guid = batch_values.attempt_guid
WHERE part_attempts.attempt_guid = batch_values.attempt_guid and lifecycle_state = 'active'
"""

Ecto.Adapters.SQL.query(Oli.Repo, sql, params)
case Ecto.Adapters.SQL.query(Oli.Repo, sql, params) do
{:ok, %Postgrex.Result{num_rows: n}} when n > 0 ->
{:ok, %{num_rows: n}}

{:ok, %Postgrex.Result{num_rows: 0}} ->
{:error, :already_submitted}

{:error, _} ->
{:error, "Failed to save student input"}
end
end

@doc """
Expand Down
18 changes: 18 additions & 0 deletions lib/oli_web/controllers/api/attempt_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,15 @@ defmodule OliWeb.Api.AttemptController do
{:ok, _} ->
json(conn, %{"type" => "success"})

{:error, :already_submitted} ->
conn
|> put_status(403)
|> json(%{
"error" => true,
"message" =>
"These changes could not be saved as this attempt may have already been submitted"
})

{:error, e} ->
{_, msg} = Oli.Utils.log_error("Could not save part", e)
error(conn, 500, msg)
Expand Down Expand Up @@ -532,6 +541,15 @@ defmodule OliWeb.Api.AttemptController do
{:ok, _} ->
json(conn, %{"type" => "success"})

{:error, :already_submitted} ->
conn
|> put_status(403)
|> json(%{
"error" => true,
"message" =>
"These changes could not be saved as this attempt may have already been submitted"
})

{:error, e} ->
{_, msg} = Oli.Utils.log_error("Could not save activity", e)
error(conn, 500, msg)
Expand Down
231 changes: 231 additions & 0 deletions test/oli_web/controllers/api/attempt_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,225 @@ defmodule OliWeb.AttemptControllerTest do
end
end

describe "activity and attempt already submitted" do
setup [:setup_session]

test "cannot submit an already submitted activity in a graded page", %{
conn: conn,
map: map
} do
# Mark activity attempt as submitted
{:ok, activity_attempt} =
Core.get_latest_activity_attempts(map.attempt1.id)
|> hd
|> Core.update_activity_attempt(%{
lifecycle_state: "submitted",
date_submitted: DateTime.utc_now()
})

# Submit activity attempt endpoint
conn =
put(
conn,
~p"/api/v1/state/course/#{map.section.slug}/activity_attempt/#{activity_attempt.attempt_guid}",
%{
"section_slug" => map.section.slug,
"activity_attempt_guid" => activity_attempt.attempt_guid,
"partInputs" => []
}
)

assert %{
"message" =>
"These changes could not be saved as this attempt may have already been submitted",
"error" => true
} =
json_response(conn, 403)
end

test "cannot change an already input submitted in a graded page", %{
conn: conn,
map: map
} do
# Mark part attempt as submitted
{:ok, part_attempt} =
Core.get_latest_part_attempts(map.activity_attempt1.attempt_guid)
|> hd
|> Core.update_part_attempt(%{
lifecycle_state: "submitted",
date_submitted: DateTime.utc_now()
})

# Save activity attempt endpoint
conn =
patch(
conn,
~p"/api/v1/state/course/#{map.section.slug}/activity_attempt/#{map.activity_attempt1.attempt_guid}",
%{
"activity_attempt_guid" => map.activity_attempt1.attempt_guid,
"partInputs" => [
%{
"attemptGuid" => part_attempt.attempt_guid,
"response" => %{"input" => "Hello World"}
}
]
}
)

assert %{
"message" =>
"These changes could not be saved as this attempt may have already been submitted",
"error" => true
} =
json_response(conn, 403)
end

test "cannot save an already part attempt submitted in a graded page", %{
conn: conn,
map: map
} do
# Mark part attempt as submitted
{:ok, part_attempt} =
Core.get_latest_part_attempts(map.activity_attempt1.attempt_guid)
|> hd
|> Core.update_part_attempt(%{
lifecycle_state: "submitted",
date_submitted: DateTime.utc_now()
})

# Save part attempt endpoint
conn =
patch(
conn,
~p"/api/v1/state/course/#{map.section.slug}/activity_attempt/#{map.activity_attempt1.attempt_guid}/part_attempt/#{part_attempt.attempt_guid}",
%{
"activity_attempt_guid" => map.activity_attempt1.attempt_guid,
"part_attempt_guid" => part_attempt.attempt_guid,
"response" => "Hello World"
}
)

assert %{
"message" =>
"These changes could not be saved as this attempt may have already been submitted",
"error" => true
} =
json_response(conn, 403)
end

test "cannot submit an already submitted activity in a adaptive page", %{
conn: conn,
map: map
} do
# Mark activity attempt as submitted
{:ok, activity_attempt} =
Core.get_latest_activity_attempts(map.adaptive_attempt1.id)
|> hd
|> Core.update_activity_attempt(%{
lifecycle_state: "submitted",
date_submitted: DateTime.utc_now()
})

# Submit activity attempt endpoint
conn =
put(
conn,
~p"/api/v1/state/course/#{map.section.slug}/activity_attempt/#{activity_attempt.attempt_guid}",
%{
"section_slug" => map.section.slug,
"activity_attempt_guid" => activity_attempt.attempt_guid,
"partInputs" => []
}
)

assert %{
"message" =>
"These changes could not be saved as this attempt may have already been submitted",
"error" => true
} =
json_response(conn, 403)
end

test "cannot change an already input submitted in a adaptive page", %{
conn: conn,
map: map
} do
# Mark part attempt as submitted
{:ok, part_attempt} =
Core.get_latest_part_attempts(map.adaptive_activity_attempt1.attempt_guid)
|> hd
|> Core.update_part_attempt(%{
lifecycle_state: "submitted",
date_submitted: DateTime.utc_now()
})

# Save activity attempt endpoint
conn =
patch(
conn,
~p"/api/v1/state/course/#{map.section.slug}/activity_attempt/#{map.adaptive_activity_attempt1.attempt_guid}",
%{
"activity_attempt_guid" => map.adaptive_activity_attempt1.attempt_guid,
"partInputs" => [
%{
"attemptGuid" => part_attempt.attempt_guid,
"response" => %{"input" => "Hello World"}
}
]
}
)

assert %{
"message" =>
"These changes could not be saved as this attempt may have already been submitted",
"error" => true
} =
json_response(conn, 403)
end

test "cannot save an already part attempt submitted in a adaptive page", %{
conn: conn,
map: map
} do
# Mark part attempt as submitted
{:ok, part_attempt} =
Core.get_latest_part_attempts(map.adaptive_activity_attempt1.attempt_guid)
|> hd
|> Core.update_part_attempt(%{
lifecycle_state: "submitted",
date_submitted: DateTime.utc_now()
})

# Save part attempt endpoint
conn =
patch(
conn,
~p"/api/v1/state/course/#{map.section.slug}/activity_attempt/#{map.adaptive_activity_attempt1.attempt_guid}/part_attempt/#{part_attempt.attempt_guid}",
%{
"activity_attempt_guid" => map.adaptive_activity_attempt1.attempt_guid,
"part_attempt_guid" => part_attempt.attempt_guid,
"response" => "Hello World"
}
)

assert %{
"message" =>
"These changes could not be saved as this attempt may have already been submitted",
"error" => true
} =
json_response(conn, 403)
end
end

defp setup_session(%{conn: conn}) do
map =
Seeder.base_project_with_resource2()
|> Seeder.create_section()
|> Seeder.add_user(%{}, :user1)
|> Seeder.add_activity(%{}, :publication, :project, :author, :activity_a)
|> Seeder.add_activity(%{}, :publication, :project, :author, :adaptive_activity)
|> Seeder.add_page(%{graded: true}, :graded_page)
|> Seeder.add_adaptive_page(:adaptive_page)
|> Seeder.create_section_resources()
|> Seeder.create_resource_attempt(
%{attempt_number: 1},
Expand Down Expand Up @@ -153,6 +365,25 @@ defmodule OliWeb.AttemptControllerTest do
:activity_attempt2,
:part3_attempt2
)
|> Seeder.create_resource_attempt(
%{attempt_number: 1},
:user1,
:adaptive_page,
:adaptive_page_revision,
:adaptive_attempt1
)
|> Seeder.create_activity_attempt(
%{attempt_number: 1, transformed_model: nil},
:adaptive_activity,
:adaptive_attempt1,
:adaptive_activity_attempt1
)
|> Seeder.create_part_attempt(
%{attempt_number: 1},
%Part{id: "1", responses: [], hints: []},
:adaptive_activity_attempt1,
:adaptive_part1_attempt1
)

user = map.user1

Expand Down

0 comments on commit fdd823d

Please sign in to comment.