diff --git a/lib/lanttern/learning_context/strand.ex b/lib/lanttern/learning_context/strand.ex index 16546c0b..7e051ca5 100644 --- a/lib/lanttern/learning_context/strand.ex +++ b/lib/lanttern/learning_context/strand.ex @@ -18,6 +18,7 @@ defmodule Lanttern.LearningContext.Strand do has_many :moments, Lanttern.LearningContext.Moment has_many :assessment_points, Lanttern.Assessments.AssessmentPoint + has_many :strand_reports, Lanttern.Reporting.StrandReport many_to_many :subjects, Lanttern.Taxonomy.Subject, join_through: "strands_subjects", diff --git a/lib/lanttern/reporting.ex b/lib/lanttern/reporting.ex index 3d317c68..00e36318 100644 --- a/lib/lanttern/reporting.ex +++ b/lib/lanttern/reporting.ex @@ -9,6 +9,9 @@ defmodule Lanttern.Reporting do alias Lanttern.Reporting.ReportCard + alias Lanttern.Assessments.AssessmentPointEntry + alias Lanttern.LearningContext.Strand + @doc """ Returns the list of report cards. @@ -347,4 +350,54 @@ defmodule Lanttern.Reporting do def change_student_report_card(%StudentReportCard{} = student_report_card, attrs \\ %{}) do StudentReportCard.changeset(student_report_card, attrs) end + + @doc """ + Returns the list of strands linked to the report card, with assessment entries. + + **Preloaded data:** + + - strand: subjects and years + - assessment entries: assessment point, scale, and ordinal value + + ## Examples + + iex> list_student_strand_reports(student_report_card) + [{%Strand{}, [%AssessmentPointEntry{}, ...]}, ...] + + """ + @spec list_student_strand_reports(StudentReportCard.t()) :: [ + {Strand.t(), [AssessmentPointEntry.t()]} + ] + + def list_student_strand_reports(%StudentReportCard{} = student_report_card) do + %{ + report_card_id: report_card_id, + student_id: student_id + } = student_report_card + + strands = + from(s in Strand, + join: sr in assoc(s, :strand_reports), + where: sr.report_card_id == ^report_card_id, + order_by: sr.position + ) + |> Repo.all() + |> maybe_preload(preloads: [:subjects, :years]) + + ast_entries_map = + from(e in AssessmentPointEntry, + join: ap in assoc(e, :assessment_point), + join: s in assoc(ap, :strand), + join: sr in assoc(s, :strand_reports), + where: sr.report_card_id == ^report_card_id and e.student_id == ^student_id, + order_by: ap.position, + preload: [assessment_point: ap] + ) + |> Repo.all() + |> maybe_preload(preloads: [:scale, :ordinal_value]) + |> Enum.group_by(& &1.assessment_point.strand_id) + + strands + |> Enum.map(&{&1, Map.get(ast_entries_map, &1.id, [])}) + end end diff --git a/test/lanttern/reporting_test.exs b/test/lanttern/reporting_test.exs index b13235bf..a4739825 100644 --- a/test/lanttern/reporting_test.exs +++ b/test/lanttern/reporting_test.exs @@ -270,4 +270,132 @@ defmodule Lanttern.ReportingTest do assert %Ecto.Changeset{} = Reporting.change_student_report_card(student_report_card) end end + + describe "student strand reports" do + import Lanttern.ReportingFixtures + + alias Lanttern.AssessmentsFixtures + alias Lanttern.GradingFixtures + alias Lanttern.LearningContextFixtures + alias Lanttern.SchoolsFixtures + alias Lanttern.TaxonomyFixtures + + test "list_student_strand_reports/1 returns the list of the strands in the report with the student assessment point entries" do + report_card = report_card_fixture() + + subject_1 = TaxonomyFixtures.subject_fixture() + subject_2 = TaxonomyFixtures.subject_fixture() + year = TaxonomyFixtures.year_fixture() + + strand_1 = + LearningContextFixtures.strand_fixture(%{ + subjects_ids: [subject_1.id], + years_ids: [year.id] + }) + + strand_2 = + LearningContextFixtures.strand_fixture(%{ + subjects_ids: [subject_2.id], + years_ids: [year.id] + }) + + _strand_1_report = + strand_report_fixture(%{ + report_card_id: report_card.id, + strand_id: strand_1.id, + position: 1 + }) + + _strand_2_report = + strand_report_fixture(%{ + report_card_id: report_card.id, + strand_id: strand_2.id, + position: 2 + }) + + student = SchoolsFixtures.student_fixture() + + student_report_card = + student_report_card_fixture(%{report_card_id: report_card.id, student_id: student.id}) + + n_scale = GradingFixtures.scale_fixture(%{type: "numeric", start: 0, stop: 10}) + o_scale = GradingFixtures.scale_fixture(%{type: "ordinal"}) + ov_1 = GradingFixtures.ordinal_value_fixture(%{scale_id: o_scale.id}) + ov_2 = GradingFixtures.ordinal_value_fixture(%{scale_id: o_scale.id}) + + assessment_point_1_1 = + AssessmentsFixtures.assessment_point_fixture(%{ + strand_id: strand_1.id, + scale_id: o_scale.id + }) + + assessment_point_1_2 = + AssessmentsFixtures.assessment_point_fixture(%{ + strand_id: strand_1.id, + scale_id: o_scale.id + }) + + assessment_point_2_1 = + AssessmentsFixtures.assessment_point_fixture(%{ + strand_id: strand_2.id, + scale_id: n_scale.id + }) + + assessment_point_1_1_entry = + AssessmentsFixtures.assessment_point_entry_fixture(%{ + student_id: student.id, + assessment_point_id: assessment_point_1_1.id, + scale_id: o_scale.id, + scale_type: o_scale.type, + ordinal_value_id: ov_1.id + }) + + assessment_point_1_2_entry = + AssessmentsFixtures.assessment_point_entry_fixture(%{ + student_id: student.id, + assessment_point_id: assessment_point_1_2.id, + scale_id: o_scale.id, + scale_type: o_scale.type, + ordinal_value_id: ov_2.id + }) + + assessment_point_2_1_entry = + AssessmentsFixtures.assessment_point_entry_fixture(%{ + student_id: student.id, + assessment_point_id: assessment_point_2_1.id, + scale_id: n_scale.id, + scale_type: n_scale.type, + score: 5 + }) + + assert [ + {expected_strand_1, [expected_entry_1_1, expected_entry_1_2]}, + {expected_strand_2, [expected_entry_2_1]} + ] = Reporting.list_student_strand_reports(student_report_card) + + # strand 1 assertions + + assert expected_strand_1.id == strand_1.id + assert expected_strand_1.subjects == [subject_1] + assert expected_strand_1.years == [year] + + assert expected_entry_1_1.id == assessment_point_1_1_entry.id + assert expected_entry_1_1.scale == o_scale + assert expected_entry_1_1.ordinal_value == ov_1 + + assert expected_entry_1_2.id == assessment_point_1_2_entry.id + assert expected_entry_1_2.scale == o_scale + assert expected_entry_1_2.ordinal_value == ov_2 + + # strand 2 assertions + + assert expected_strand_2.id == strand_2.id + assert expected_strand_2.subjects == [subject_2] + assert expected_strand_2.years == [year] + + assert expected_entry_2_1.id == assessment_point_2_1_entry.id + assert expected_entry_2_1.scale == n_scale + assert expected_entry_2_1.score == 5 + end + end end