diff --git a/assets/src/components/activities/common/delivery/evaluation/Evaluation.tsx b/assets/src/components/activities/common/delivery/evaluation/Evaluation.tsx index 7683a835f31..398ffefa32b 100644 --- a/assets/src/components/activities/common/delivery/evaluation/Evaluation.tsx +++ b/assets/src/components/activities/common/delivery/evaluation/Evaluation.tsx @@ -6,17 +6,12 @@ import { isDefined } from 'utils/common'; interface Props { shouldShow?: boolean; - showExplanation?: boolean; attemptState: ActivityState; context: WriterContext; partOrder?: string[]; } -export function renderPartFeedback( - partState: PartState, - context: WriterContext, - showExplanation: boolean, -) { +export function renderPartFeedback(partState: PartState, context: WriterContext) { if (!partState.score && !partState.outOf) { return null; } @@ -43,7 +38,7 @@ export function renderPartFeedback( direction={feedbackDirection} /> - {showExplanation && explanation && resultCl !== 'correct' && ( + {explanation && resultCl !== 'correct' && ( = ({ shouldShow = true, - showExplanation = true, attemptState, context, partOrder, @@ -80,7 +74,7 @@ export const Evaluation: React.FC = ({ } if (parts.length === 1) { - return renderPartFeedback(parts[0], context, showExplanation); + return renderPartFeedback(parts[0], context); } // part order for migrated multi-inputs may be random, so allow caller to specify appropriate one @@ -90,9 +84,7 @@ export const Evaluation: React.FC = ({ if (newOrder.length === parts.length) orderedParts = newOrder; } - return ( - <>{orderedParts.map((partState) => renderPartFeedback(partState, context, showExplanation))} - ); + return <>{orderedParts.map((partState) => renderPartFeedback(partState, context))}; }; interface ComponentProps { diff --git a/assets/src/components/activities/custom_dnd/FocusedFeedback.tsx b/assets/src/components/activities/custom_dnd/FocusedFeedback.tsx index 334d68fffb0..837009f6d3e 100644 --- a/assets/src/components/activities/custom_dnd/FocusedFeedback.tsx +++ b/assets/src/components/activities/custom_dnd/FocusedFeedback.tsx @@ -28,7 +28,7 @@ export const FocusedFeedback: React.FC = (props: FocusedFe {uiState.attemptState.parts.map((part) => ( - {renderPartFeedback(part, writerContext, true)} + {renderPartFeedback(part, writerContext)} ))} @@ -36,6 +36,6 @@ export const FocusedFeedback: React.FC = (props: FocusedFe } else { // otherwise, only render the currently focused part feedback const part = uiState.attemptState.parts.find((ps) => ps.partId === focusedPart); - return part !== undefined ? renderPartFeedback(part, writerContext, true) : null; + return part !== undefined ? renderPartFeedback(part, writerContext) : null; } }; diff --git a/lib/oli/activities/state.ex b/lib/oli/activities/state.ex index e6261ef679c..69f029aadf3 100644 --- a/lib/oli/activities/state.ex +++ b/lib/oli/activities/state.ex @@ -7,7 +7,7 @@ defmodule Oli.Activities.State do produce the corresponding activity state representation, returning those in a map of activity ids to the state. """ - def from_attempts(latest_attempts, resource_attempt, page_revision) do + def from_attempts(latest_attempts, resource_attempt, page_revision, effective_settings) do Enum.map(latest_attempts, fn {id, {activity_attempt, part_attempts}} -> {:ok, model} = Oli.Delivery.Attempts.Core.select_model(activity_attempt) |> Model.parse() @@ -17,7 +17,8 @@ defmodule Oli.Activities.State do Map.values(part_attempts), model, resource_attempt, - page_revision + page_revision, + effective_settings )} end) |> Map.new() diff --git a/lib/oli/activities/state/activity_state.ex b/lib/oli/activities/state/activity_state.ex index 204109ed61b..ce9dd50b46f 100644 --- a/lib/oli/activities/state/activity_state.ex +++ b/lib/oli/activities/state/activity_state.ex @@ -34,11 +34,12 @@ defmodule Oli.Activities.State.ActivityState do ] @spec from_attempt( - Oli.Delivery.Attempts.Core.ActivityAttempt.t(), - [Oli.Delivery.Attempts.Core.PartAttempt.t()], - Oli.Activities.Model.t(), - Oli.Delivery.Attempts.Core.ResourceAttempt.t(), - Oli.Resources.Revision.t() + %Oli.Delivery.Attempts.Core.ActivityAttempt{}, + [%Oli.Delivery.Attempts.Core.PartAttempt{}], + %Oli.Activities.Model{}, + %Oli.Delivery.Attempts.Core.ResourceAttempt{} | nil, + %Oli.Resources.Revision{} | nil, + %Oli.Delivery.Settings.Combined{} ) :: %Oli.Activities.State.ActivityState{} def from_attempt( @@ -46,7 +47,8 @@ defmodule Oli.Activities.State.ActivityState do part_attempts, %Model{} = model, resource_attempt, - page_revision + page_revision, + effective_settings ) do # Create the part states, and where we encounter parts from the model # that do not have an attempt we create the default state @@ -63,7 +65,8 @@ defmodule Oli.Activities.State.ActivityState do part_attempt: part_attempt, activity_attempt: attempt, resource_attempt: resource_attempt, - resource_revision: page_revision + resource_revision: page_revision, + effective_settings: effective_settings }) end end @@ -74,7 +77,7 @@ defmodule Oli.Activities.State.ActivityState do end) has_more_attempts = - case attempt.revision.max_attempts do + case effective_settings.max_attempts do 0 -> true max -> attempt.attempt_number < max end diff --git a/lib/oli/delivery/attempts/activity_lifecycle.ex b/lib/oli/delivery/attempts/activity_lifecycle.ex index 15d97440b8d..e76999c1815 100644 --- a/lib/oli/delivery/attempts/activity_lifecycle.ex +++ b/lib/oli/delivery/attempts/activity_lifecycle.ex @@ -100,6 +100,8 @@ defmodule Oli.Delivery.Attempts.ActivityLifecycle do activity_attempt = get_activity_attempt_by(attempt_guid: activity_attempt_guid) resource_attempt = get_resource_attempt_and_revision(activity_attempt.resource_attempt_id) + effective_settings = Oli.Delivery.Settings.get_combined_settings(resource_attempt) + result = Repo.transaction(fn -> if is_nil(activity_attempt) do @@ -119,8 +121,8 @@ defmodule Oli.Delivery.Attempts.ActivityLifecycle do activity_attempt.resource_id ) - if activity_attempt.revision.max_attempts > 0 and - activity_attempt.revision.max_attempts <= attempt_count do + if effective_settings.max_attempts > 0 and + effective_settings.max_attempts <= attempt_count do Repo.rollback({:no_more_attempts}) else part_attempts = get_latest_part_attempts(activity_attempt_guid) @@ -166,7 +168,8 @@ defmodule Oli.Delivery.Attempts.ActivityLifecycle do new_part_attempts, model, resource_attempt, - resource_attempt.revision + resource_attempt.revision, + effective_settings ), ModelPruner.prune(working_model)} else {:error, error} -> Repo.rollback(error) @@ -293,7 +296,8 @@ defmodule Oli.Delivery.Attempts.ActivityLifecycle do part_attempt: part_attempt, activity_attempt: activity_attempt, resource_attempt: resource_attempt, - resource_revision: resource_attempt.revision + resource_revision: resource_attempt.revision, + effective_settings: Oli.Delivery.Settings.get_combined_settings(resource_attempt) }) end diff --git a/lib/oli/delivery/attempts/activity_lifecycle/evaluate.ex b/lib/oli/delivery/attempts/activity_lifecycle/evaluate.ex index def2597ef86..8d923e8e9ac 100644 --- a/lib/oli/delivery/attempts/activity_lifecycle/evaluate.ex +++ b/lib/oli/delivery/attempts/activity_lifecycle/evaluate.ex @@ -409,7 +409,11 @@ defmodule Oli.Delivery.Attempts.ActivityLifecycle.Evaluate do {:ok, evaluations} end - def update_part_attempts_and_get_activity_attempts(resource_attempt, datashop_session_id) do + def update_part_attempts_and_get_activity_attempts( + resource_attempt, + datashop_session_id, + effective_settings + ) do activity_attempts = case resource_attempt.revision do %{content: %{"advancedDelivery" => true}} -> @@ -432,7 +436,11 @@ defmodule Oli.Delivery.Attempts.ActivityLifecycle.Evaluate do if activity_attempt.lifecycle_state != :evaluated and activity_attempt.scoreable do activity_attempt = Map.put(activity_attempt, :resource_attempt, resource_attempt) - case update_part_attempts_for_activity(activity_attempt, datashop_session_id) do + case update_part_attempts_for_activity( + activity_attempt, + datashop_session_id, + effective_settings + ) do {:ok, part_inputs} -> part_attempts = get_latest_part_attempts(activity_attempt.attempt_guid) @@ -474,7 +482,7 @@ defmodule Oli.Delivery.Attempts.ActivityLifecycle.Evaluate do ) end - def update_part_attempts_for_activity(activity_attempt, datashop_session_id) do + def update_part_attempts_for_activity(activity_attempt, datashop_session_id, effective_settings) do part_attempts = get_latest_part_attempts(activity_attempt.attempt_guid) part_inputs = @@ -493,7 +501,7 @@ defmodule Oli.Delivery.Attempts.ActivityLifecycle.Evaluate do |> filter_already_evaluated(part_attempts) case activity_attempt - |> do_evaluate_submissions(part_inputs, part_attempts) + |> do_evaluate_submissions(part_inputs, part_attempts, effective_settings) |> persist_evaluations(part_inputs, fn result -> result end, datashop_session_id) do {:ok, _} -> {:ok, part_inputs} e -> e @@ -724,13 +732,28 @@ defmodule Oli.Delivery.Attempts.ActivityLifecycle.Evaluate do do_evaluate_submissions(activity_attempt, part_inputs, part_attempts) end + defp do_evaluate_submissions( + activity_attempt, + part_inputs, + part_attempts, + effective_settings \\ nil + ) + + defp do_evaluate_submissions(activity_attempt, part_inputs, part_attempts, nil) do + effective_settings = + Oli.Delivery.Settings.get_combined_settings(activity_attempt.resource_attempt) + + do_evaluate_submissions(activity_attempt, part_inputs, part_attempts, effective_settings) + end + defp do_evaluate_submissions( %ActivityAttempt{ resource_attempt: resource_attempt, attempt_number: attempt_number } = activity_attempt, part_inputs, - part_attempts + part_attempts, + effective_settings ) do activity_model = select_model(activity_attempt) @@ -766,7 +789,8 @@ defmodule Oli.Delivery.Attempts.ActivityLifecycle.Evaluate do part_attempt: attempt, activity_attempt: activity_attempt, resource_attempt: resource_attempt, - resource_revision: resource_attempt.revision + resource_revision: resource_attempt.revision, + effective_settings: effective_settings }) end) diff --git a/lib/oli/delivery/attempts/page_lifecycle/graded.ex b/lib/oli/delivery/attempts/page_lifecycle/graded.ex index 0da34789e06..c55122692fd 100644 --- a/lib/oli/delivery/attempts/page_lifecycle/graded.ex +++ b/lib/oli/delivery/attempts/page_lifecycle/graded.ex @@ -149,7 +149,11 @@ defmodule Oli.Delivery.Attempts.PageLifecycle.Graded do }) do # Collect all of the part attempt guids for all of the activities that get finalized with {:ok, part_attempt_guids} <- - finalize_activity_and_part_attempts(resource_attempt, datashop_session_id), + finalize_activity_and_part_attempts( + resource_attempt, + datashop_session_id, + effective_settings + ), {:ok, resource_attempt} <- roll_up_activities_to_resource_attempt(resource_attempt, effective_settings), {:ok, resource_attempt} <- cancel_pending_auto_submit(resource_attempt) do @@ -201,7 +205,11 @@ defmodule Oli.Delivery.Attempts.PageLifecycle.Graded do def finalize(_), do: {:error, {:already_submitted}} @decorate transaction_event("Graded.finalize_activity_and_part_attempts") - defp finalize_activity_and_part_attempts(resource_attempt, datashop_session_id) do + defp finalize_activity_and_part_attempts( + resource_attempt, + datashop_session_id, + effective_settings + ) do case resource_attempt.revision do # For adaptive pages, we never want to evaluate anything at finalization time %{content: %{"advancedDelivery" => true}} -> @@ -211,7 +219,8 @@ defmodule Oli.Delivery.Attempts.PageLifecycle.Graded do with {_, activity_attempt_values, activity_attempt_params, part_attempt_guids} <- Evaluate.update_part_attempts_and_get_activity_attempts( resource_attempt, - datashop_session_id + datashop_session_id, + effective_settings ), {:ok, _} <- Persistence.bulk_update_activity_attempts( diff --git a/lib/oli/delivery/evaluation/explanation.ex b/lib/oli/delivery/evaluation/explanation.ex index 13222a064c0..46b16046e6e 100644 --- a/lib/oli/delivery/evaluation/explanation.ex +++ b/lib/oli/delivery/evaluation/explanation.ex @@ -4,6 +4,7 @@ defmodule Oli.Delivery.Evaluation.Explanation do alias Oli.Delivery.Evaluation.ExplanationContext alias Oli.Delivery.Attempts.Core.{ActivityAttempt, ResourceAttempt} alias Oli.Delivery.Evaluation.Actions.FeedbackAction + alias Oli.Delivery.Settings.Combined @doc """ Determines whether an explanation should be shown using the given explanation context. @@ -51,14 +52,16 @@ defmodule Oli.Delivery.Evaluation.Explanation do # (ignore unscored pages for now) defp check_explanation_condition(%ExplanationContext{ resource_revision: %Revision{ - graded: true, + graded: true + }, + resource_attempt: %ResourceAttempt{ + attempt_number: resource_attempt_number + }, + effective_settings: %Combined{ explanation_strategy: %ExplanationStrategy{ type: :after_max_resource_attempts_exhausted }, max_attempts: max_attempts - }, - resource_attempt: %ResourceAttempt{ - attempt_number: resource_attempt_number } }) do if resource_attempt_number >= max_attempts && max_attempts > 0 do @@ -71,14 +74,16 @@ defmodule Oli.Delivery.Evaluation.Explanation do # show after set number of attempts strategy for scored pages defp check_explanation_condition(%ExplanationContext{ resource_revision: %Revision{ - graded: true, + graded: true + }, + resource_attempt: %ResourceAttempt{ + attempt_number: resource_attempt_number + }, + effective_settings: %Combined{ explanation_strategy: %ExplanationStrategy{ type: :after_set_num_attempts, set_num_attempts: set_num_attempts } - }, - resource_attempt: %ResourceAttempt{ - attempt_number: resource_attempt_number } }) do if resource_attempt_number >= set_num_attempts do @@ -91,17 +96,19 @@ defmodule Oli.Delivery.Evaluation.Explanation do # show after set number of attempts strategy for unscored pages defp check_explanation_condition(%ExplanationContext{ resource_revision: %Revision{ - graded: false, - explanation_strategy: %ExplanationStrategy{ - type: :after_set_num_attempts, - set_num_attempts: set_num_attempts - } + graded: false }, activity_attempt: %ActivityAttempt{ attempt_number: activity_attempt_number, part_attempts: part_attempts }, - part_attempt: part_attempt + part_attempt: part_attempt, + effective_settings: %Combined{ + explanation_strategy: %ExplanationStrategy{ + type: :after_set_num_attempts, + set_num_attempts: set_num_attempts + } + } }) do if length(part_attempts) > 1 do if part_attempt.attempt_number >= set_num_attempts, diff --git a/lib/oli/delivery/evaluation/explanation_context.ex b/lib/oli/delivery/evaluation/explanation_context.ex index 4079c6f72f9..10c7f390a24 100644 --- a/lib/oli/delivery/evaluation/explanation_context.ex +++ b/lib/oli/delivery/evaluation/explanation_context.ex @@ -4,13 +4,15 @@ defmodule Oli.Delivery.Evaluation.ExplanationContext do alias Oli.Delivery.Attempts.Core.ActivityAttempt alias Oli.Resources.Revision alias Oli.Delivery.Attempts.Core.ResourceAttempt + alias Oli.Delivery.Settings.Combined @enforce_keys [ :part, :part_attempt, :activity_attempt, :resource_attempt, - :resource_revision + :resource_revision, + :effective_settings ] defstruct [ @@ -18,14 +20,16 @@ defmodule Oli.Delivery.Evaluation.ExplanationContext do :part_attempt, :activity_attempt, :resource_attempt, - :resource_revision + :resource_revision, + :effective_settings ] @type t() :: %__MODULE__{ - part: Part.t(), - part_attempt: PartAttempt.t(), - activity_attempt: ActivityAttempt.t(), - resource_attempt: ResourceAttempt.t(), - resource_revision: Revision.t() + part: %Part{}, + part_attempt: %PartAttempt{}, + activity_attempt: %ActivityAttempt{}, + resource_attempt: %ResourceAttempt{}, + resource_revision: %Revision{}, + effective_settings: %Combined{} } end diff --git a/lib/oli/delivery/page/activity_context.ex b/lib/oli/delivery/page/activity_context.ex index fb95c4a7e3e..a9edb6951ea 100644 --- a/lib/oli/delivery/page/activity_context.ex +++ b/lib/oli/delivery/page/activity_context.ex @@ -21,16 +21,25 @@ defmodule Oli.Delivery.Page.ActivityContext do @spec create_context_map( boolean(), %{}, - Oli.Delivery.Attempts.Core.ResourceAttempt.t(), - Oli.Resources.Revision.t() + %Oli.Delivery.Attempts.Core.ResourceAttempt{}, + %Oli.Resources.Revision{}, + %Oli.Delivery.Settings.Combined{} ) :: %{} @decorate transaction_event() - def create_context_map(graded, latest_attempts, resource_attempt, page_revision, opts \\ []) do + def create_context_map( + graded, + latest_attempts, + resource_attempt, + page_revision, + effective_settings, + opts \\ [] + ) do # get a view of all current registered activity types registrations = Activities.list_activity_registrations() reg_map = Enum.reduce(registrations, %{}, fn r, m -> Map.put(m, r.id, r) end) - activity_states = State.from_attempts(latest_attempts, resource_attempt, page_revision) + activity_states = + State.from_attempts(latest_attempts, resource_attempt, page_revision, effective_settings) ordinal_assign_fn = create_ordinal_assignment_fn(graded, opts) diff --git a/lib/oli/delivery/page/page_context.ex b/lib/oli/delivery/page/page_context.ex index e59e6a79f48..6d70ba51539 100644 --- a/lib/oli/delivery/page/page_context.ex +++ b/lib/oli/delivery/page/page_context.ex @@ -88,6 +88,7 @@ defmodule Oli.Delivery.Page.PageContext do resource_attempt, latest_attempts, resource_attempt.revision, + effective_settings, Oli.Delivery.Settings.show_feedback?(effective_settings) ) @@ -209,7 +210,14 @@ defmodule Oli.Delivery.Page.PageContext do {:ok, {state, %AttemptState{resource_attempt: resource_attempt, attempt_hierarchy: latest_attempts}}} -> - assemble_final_context(state, resource_attempt, latest_attempts, page_revision, true) + assemble_final_context( + state, + resource_attempt, + latest_attempts, + page_revision, + effective_settings, + true + ) {:error, _} -> {:error, [], %{}} @@ -278,7 +286,8 @@ defmodule Oli.Delivery.Page.PageContext do %{ content: %{"advancedDelivery" => true} }, - _ + _effective_settings, + _show_feedback ) do {state, [resource_attempt], latest_attempts, latest_attempts} end @@ -289,6 +298,7 @@ defmodule Oli.Delivery.Page.PageContext do resource_attempt, latest_attempts, page_revision, + effective_settings, show_feedback ) do content_for_ordinal_assignment = @@ -303,6 +313,7 @@ defmodule Oli.Delivery.Page.PageContext do latest_attempts, resource_attempt, page_revision, + effective_settings, assign_ordinals_from: content_for_ordinal_assignment, show_feedback: show_feedback ) diff --git a/lib/oli/delivery/settings.ex b/lib/oli/delivery/settings.ex index 158cd0bfd3c..42af9f11555 100644 --- a/lib/oli/delivery/settings.ex +++ b/lib/oli/delivery/settings.ex @@ -32,6 +32,24 @@ defmodule Oli.Delivery.Settings do combine(resolved_revision, section_resource, nil) end + @doc """ + For a ResourceAttempt, return the combined settings for that page, section, and user. This is the settings that will + be used for the user when they are viewing the page. This takes into account any + instructor customizations for the section, as well as any student exceptions for the + user. + """ + def get_combined_settings(%ResourceAttempt{} = resource_attempt) do + %{revision: resolved_revision, resource_access: %{user_id: user_id, section_id: section_id}} = + Repo.preload(resource_attempt, [:revision, :resource_access]) + + section_resource = + Oli.Delivery.Sections.get_section_resource(section_id, resolved_revision.resource_id) + + student_exception = get_student_exception(resolved_revision.resource_id, section_id, user_id) + + combine(resolved_revision, section_resource, student_exception) + end + def get_combined_settings_for_all_resources(section_id, user_id, resource_ids \\ nil) do section = Oli.Delivery.Sections.get_section!(section_id) diff --git a/lib/oli/delivery/settings/combined.ex b/lib/oli/delivery/settings/combined.ex index b57300cf0d0..ce54d6ef611 100644 --- a/lib/oli/delivery/settings/combined.ex +++ b/lib/oli/delivery/settings/combined.ex @@ -35,7 +35,7 @@ defmodule Oli.Delivery.Settings.Combined do review_submission: :allow | :disallow, feedback_mode: :allow | :disallow | :scheduled, feedback_scheduled_date: DateTime.t(), - collab_space_config: Oli.Resources.Collaboration.CollabSpaceConfig.t(), - explanation_strategy: Oli.Resources.ExplanationStrategy.t() + collab_space_config: %Oli.Resources.Collaboration.CollabSpaceConfig{}, + explanation_strategy: %Oli.Resources.ExplanationStrategy{} } end diff --git a/lib/oli/rendering/activity/html.ex b/lib/oli/rendering/activity/html.ex index a3ec40098a4..3fee8df893d 100644 --- a/lib/oli/rendering/activity/html.ex +++ b/lib/oli/rendering/activity/html.ex @@ -4,6 +4,7 @@ defmodule Oli.Rendering.Activity.Html do """ import Oli.Utils + alias Oli.Delivery.Settings alias Oli.Rendering.Context alias Oli.Rendering.Error alias Oli.Rendering.Activity.ActivitySummary @@ -179,7 +180,7 @@ defmodule Oli.Rendering.Activity.Html do groupId: group_id, bibParams: bib_params, learningLanguage: learning_language, - showFeedback: Oli.Delivery.Settings.show_feedback?(effective_settings), + showFeedback: Settings.show_feedback?(effective_settings), pageAttemptGuid: if is_nil(resource_attempt) do "" diff --git a/lib/oli_web/controllers/api/attempt_controller.ex b/lib/oli_web/controllers/api/attempt_controller.ex index 85e34febe55..e10e992c8df 100644 --- a/lib/oli_web/controllers/api/attempt_controller.ex +++ b/lib/oli_web/controllers/api/attempt_controller.ex @@ -347,6 +347,13 @@ defmodule OliWeb.Api.AttemptController do {:ok, [attempt]} -> model = Oli.Delivery.Attempts.Core.select_model(attempt) + resource_attempt = + Oli.Delivery.Attempts.Core.get_resource_attempt_and_revision( + attempt.resource_attempt_id + ) + + effective_settings = Oli.Delivery.Settings.get_combined_settings(resource_attempt) + {:ok, parsed_model} = Oli.Activities.Model.parse(model) state = @@ -355,7 +362,8 @@ defmodule OliWeb.Api.AttemptController do Attempts.get_latest_part_attempts(attempt.attempt_guid), parsed_model, nil, - nil + nil, + effective_settings ) json(conn, %{ diff --git a/lib/oli_web/live/delivery/student/lesson/components/one_at_a_time_question.ex b/lib/oli_web/live/delivery/student/lesson/components/one_at_a_time_question.ex index f58dce338e8..c666023f69a 100644 --- a/lib/oli_web/live/delivery/student/lesson/components/one_at_a_time_question.ex +++ b/lib/oli_web/live/delivery/student/lesson/components/one_at_a_time_question.ex @@ -25,6 +25,7 @@ defmodule OliWeb.Delivery.Student.Lesson.Components.OneAtATimeQuestion do attr :revision_slug, :string attr :attempt_guid, :string attr :section_slug, :string + attr :effective_settings, :map def render(assigns) do ~H""" @@ -190,8 +191,7 @@ defmodule OliWeb.Delivery.Student.Lesson.Components.OneAtATimeQuestion do "Components.Evaluation", %{ attemptState: selected_question.state, - context: selected_question.context, - showExplanation: false + context: selected_question.context }, id: "activity_evaluation_for_question_#{selected_question.number}", container: [class: "flex flex-col w-full"] @@ -312,7 +312,8 @@ defmodule OliWeb.Delivery.Student.Lesson.Components.OneAtATimeQuestion do ) |> Oli.Repo.preload([:resource_attempt, :part_attempts, :revision]) |> Oli.Delivery.Attempts.ActivityLifecycle.Evaluate.update_part_attempts_for_activity( - socket.assigns.datashop_session_id + socket.assigns.datashop_session_id, + socket.assigns.effective_settings ) ## and update it's state in the assigns (to render the feedback in the UI) @@ -321,7 +322,7 @@ defmodule OliWeb.Delivery.Student.Lesson.Components.OneAtATimeQuestion do Enum.map(socket.assigns.questions, fn %{selected: true} = selected_question -> Map.merge(selected_question, %{ - state: get_updated_state(attempt_guid), + state: get_updated_state(attempt_guid, socket.assigns.effective_settings), submitted: true }) @@ -426,7 +427,7 @@ defmodule OliWeb.Delivery.Student.Lesson.Components.OneAtATimeQuestion do } end - defp get_updated_state(attempt_guid) do + defp get_updated_state(attempt_guid, effective_settings) do {:ok, [attempt]} = Oli.Delivery.Attempts.Core.get_activity_attempts([attempt_guid]) model = Oli.Delivery.Attempts.Core.select_model(attempt) @@ -437,7 +438,8 @@ defmodule OliWeb.Delivery.Student.Lesson.Components.OneAtATimeQuestion do Oli.Delivery.Attempts.Core.get_latest_part_attempts(attempt.attempt_guid), parsed_model, nil, - nil + nil, + effective_settings ) # string keys are expected... |> Jason.encode!() diff --git a/lib/oli_web/live/delivery/student/lesson_live.ex b/lib/oli_web/live/delivery/student/lesson_live.ex index 3b80da18b14..ebfd142aa85 100644 --- a/lib/oli_web/live/delivery/student/lesson_live.ex +++ b/lib/oli_web/live/delivery/student/lesson_live.ex @@ -825,6 +825,7 @@ defmodule OliWeb.Delivery.Student.LessonLive do revision_slug={@revision_slug} attempt_guid={@attempt_guid} section_slug={@section.slug} + effective_settings={@page_context.effective_settings} />
diff --git a/lib/oli_web/live/manual_grading/rendering.ex b/lib/oli_web/live/manual_grading/rendering.ex index 4179f2169f4..ac7bbade43c 100644 --- a/lib/oli_web/live/manual_grading/rendering.ex +++ b/lib/oli_web/live/manual_grading/rendering.ex @@ -15,6 +15,8 @@ defmodule OliWeb.ManualGrading.Rendering do resource_attempt = Oli.Delivery.Attempts.Core.get_resource_attempt(attempt_guid: attempt.resource_attempt_guid) + effective_settings = Oli.Delivery.Settings.get_combined_settings(resource_attempt) + %Context{ user: attempt.user, section_slug: section_slug, @@ -27,6 +29,7 @@ defmodule OliWeb.ManualGrading.Rendering do attempt_parts, nil, nil, + effective_settings, prune: false ), activity_types_map: activity_types_map, diff --git a/lib/oli_web/views/page_delivery_view.ex b/lib/oli_web/views/page_delivery_view.ex index 7c1b24d2abd..5208f95ff7e 100644 --- a/lib/oli_web/views/page_delivery_view.ex +++ b/lib/oli_web/views/page_delivery_view.ex @@ -196,13 +196,21 @@ defmodule OliWeb.PageDeliveryView do defp encode_attempt(registered_activity_slug_map, {activity_attempt, part_attempts_map}) do {:ok, model} = Core.select_model(activity_attempt) |> Oli.Activities.Model.parse() + resource_attempt = + Oli.Delivery.Attempts.Core.get_resource_attempt_and_revision( + activity_attempt.resource_attempt_id + ) + + effective_settings = Oli.Delivery.Settings.get_combined_settings(resource_attempt) + state = Oli.Activities.State.ActivityState.from_attempt( activity_attempt, Map.values(part_attempts_map), model, nil, - nil + nil, + effective_settings ) activity_type_slug = diff --git a/test/oli/delivery/evaluation/explanation_test.exs b/test/oli/delivery/evaluation/explanation_test.exs index 2e22af882ca..ebed91f4a15 100644 --- a/test/oli/delivery/evaluation/explanation_test.exs +++ b/test/oli/delivery/evaluation/explanation_test.exs @@ -7,6 +7,7 @@ defmodule Oli.Delivery.Evaluation.ExplanationTest do alias Oli.Delivery.Attempts.Core.StudentInput alias Oli.Resources.ExplanationStrategy alias Oli.Delivery.Attempts.Core.PartAttempt + alias Oli.Delivery.Sections defp setup_explanation(_) do %{} @@ -42,9 +43,13 @@ defmodule Oli.Delivery.Evaluation.ExplanationTest do test "after_max_resource_attempts_exhausted strategy explanation in a scored page", map do datashop_session_id_user1 = UUID.uuid4() + Sections.get_section_resource(map.section.id, map.scored_page2.resource_id) + |> Sections.update_section_resource(%{ + max_attempts: 3 + }) + map = map - |> Seeder.Project.set_revision_max_attempts(ref(:scored_page2), 3) |> Seeder.Project.set_revision_explanation_strategy( ref(:scored_page2), %ExplanationStrategy{ diff --git a/test/oli/delivery/page/activity_context_test.exs b/test/oli/delivery/page/activity_context_test.exs index 224e9336232..6e5f0748534 100644 --- a/test/oli/delivery/page/activity_context_test.exs +++ b/test/oli/delivery/page/activity_context_test.exs @@ -54,7 +54,15 @@ defmodule Oli.Delivery.Page.ActivityContextTest do a1: a1 } do latest_attempts = Hierarchy.get_latest_attempts(attempt1.id) - m = ActivityContext.create_context_map(false, latest_attempts, nil, nil) + + m = + ActivityContext.create_context_map( + false, + latest_attempts, + nil, + nil, + %Oli.Delivery.Settings.Combined{} + ) assert length(Map.keys(m)) == 1 assert Map.get(m, a1.resource.id).model == "{"stem":"1"}" diff --git a/test/oli/delivery/submission_test.exs b/test/oli/delivery/submission_test.exs index e63c0d946dd..a6e9424d03b 100644 --- a/test/oli/delivery/submission_test.exs +++ b/test/oli/delivery/submission_test.exs @@ -272,7 +272,7 @@ defmodule Oli.Delivery.AttemptsSubmissionTest do Seeder.base_project_with_resource2() |> Seeder.create_section() |> Seeder.add_objective("objective one", :o1) - |> Seeder.add_activity(%{title: "one", max_attempts: 2, content: content}, :activity) + |> Seeder.add_activity(%{title: "one", content: content}, :activity) |> Seeder.add_user(%{}, :user1) |> Seeder.add_user(%{}, :user2) @@ -290,7 +290,8 @@ defmodule Oli.Delivery.AttemptsSubmissionTest do } ] }, - objectives: %{"attached" => [Map.get(map, :o1).resource.id]} + objectives: %{"attached" => [Map.get(map, :o1).resource.id]}, + max_attempts: 2 }, :ungraded_page )