Skip to content

Commit

Permalink
Merge pull request #241 from camino-school/226-add-class-info-to-stud…
Browse files Browse the repository at this point in the history
…ent-records

add class info to student records
  • Loading branch information
endoooo authored Dec 16, 2024
2 parents bd87191 + df99594 commit 87b0468
Show file tree
Hide file tree
Showing 33 changed files with 1,089 additions and 664 deletions.
27 changes: 27 additions & 0 deletions lib/lanttern/schools.ex
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,8 @@ defmodule Lanttern.Schools do
@doc """
Returns the list of classes.
Cycle and years are always preloaded.
## Options:
- `:classes_ids` – filter results by given ids
Expand Down Expand Up @@ -1303,4 +1305,29 @@ defmodule Lanttern.Schools do

{:ok, response}
end

@doc """
Returns a list of classes ids linked to student for the giving date,
using the relationship between class and cycle.
## Examples
iex> list_classes_ids_for_student_in_date(student_id, ~D[2024-08-01])
[1, 2]
"""
@spec list_classes_ids_for_student_in_date(student_id :: pos_integer(), date: Date.t()) :: [
pos_integer()
]
def list_classes_ids_for_student_in_date(student_id, date) do
from(
c in Class,
join: s in assoc(c, :students),
on: s.id == ^student_id,
join: cy in assoc(c, :cycle),
where: cy.start_at <= ^date and cy.end_at >= ^date,
select: c.id
)
|> Repo.all()
end
end
16 changes: 14 additions & 2 deletions lib/lanttern/students_records.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ defmodule Lanttern.StudentsRecords do
- `:school_id` - filter results by school
- `:students_ids` - filter results by students
- `:classes_ids` - filter results by classes
- `:types_ids` - filter results by type
- `:statuses_ids` - filter results by status
- `:preloads` - preloads associated data
Expand All @@ -33,6 +34,7 @@ defmodule Lanttern.StudentsRecords do
[
school_id: pos_integer(),
students_ids: [pos_integer()],
classes_ids: [pos_integer()],
types_ids: [pos_integer()],
statuses_ids: [pos_integer()],
preloads: list()
Expand Down Expand Up @@ -63,8 +65,18 @@ defmodule Lanttern.StudentsRecords do
when is_list(students_ids) and students_ids != [] do
from(
sr in queryable,
join: s in assoc(sr, :students),
where: s.id in ^students_ids
join: srel in assoc(sr, :students_relationships),
where: srel.student_id in ^students_ids
)
|> apply_list_students_records_opts(opts)
end

defp apply_list_students_records_opts(queryable, [{:classes_ids, classes_ids} | opts])
when is_list(classes_ids) and classes_ids != [] do
from(
sr in queryable,
join: cr in assoc(sr, :classes_relationships),
where: cr.class_id in ^classes_ids
)
|> apply_list_students_records_opts(opts)
end
Expand Down
28 changes: 28 additions & 0 deletions lib/lanttern/students_records/student_record.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ defmodule Lanttern.StudentsRecords.StudentRecord do

import LantternWeb.Gettext

alias Lanttern.StudentsRecords.StudentRecordClassRelationship
alias Lanttern.StudentsRecords.StudentRecordRelationship
alias Lanttern.StudentsRecords.StudentRecordStatus
alias Lanttern.StudentsRecords.StudentRecordType
alias Lanttern.Schools.Class
alias Lanttern.Schools.School
alias Lanttern.Schools.Student

Expand All @@ -21,6 +23,9 @@ defmodule Lanttern.StudentsRecords.StudentRecord do
date: Date.t(),
time: Time.t(),
students: [Student.t()],
students_ids: [pos_integer()],
classes: [Class.t()],
classes_ids: [pos_integer()],
school_id: pos_integer(),
school: School.t(),
status_id: pos_integer(),
Expand All @@ -37,17 +42,21 @@ defmodule Lanttern.StudentsRecords.StudentRecord do
field :date, :date
field :time, :time
field :students_ids, {:array, :id}, virtual: true
field :classes_ids, {:array, :id}, virtual: true

belongs_to :school, School
belongs_to :status, StudentRecordStatus
belongs_to :type, StudentRecordType

has_many :students_relationships, StudentRecordRelationship, on_replace: :delete
has_many :classes_relationships, StudentRecordClassRelationship, on_replace: :delete

many_to_many :students, Student,
join_through: "students_students_records",
preload_order: [asc: :name]

many_to_many :classes, Class, join_through: "students_records_classes"

timestamps()
end

Expand All @@ -60,12 +69,14 @@ defmodule Lanttern.StudentsRecords.StudentRecord do
:date,
:time,
:students_ids,
:classes_ids,
:school_id,
:type_id,
:status_id
])
|> validate_required([:description, :date, :school_id, :type_id, :status_id])
|> cast_and_validate_students()
|> cast_classes()
end

def cast_and_validate_students(changeset) do
Expand Down Expand Up @@ -96,4 +107,21 @@ defmodule Lanttern.StudentsRecords.StudentRecord do
end

defp cast_students(changeset, _), do: changeset

defp cast_classes(changeset) do
case get_change(changeset, :classes_ids) do
classes_ids when is_list(classes_ids) ->
school_id = get_field(changeset, :school_id)

classes_relationships_params =
Enum.map(classes_ids, &%{class_id: &1, school_id: school_id})

changeset
|> put_change(:classes_relationships, classes_relationships_params)
|> cast_assoc(:classes_relationships)

_ ->
changeset
end
end
end
22 changes: 22 additions & 0 deletions lib/lanttern/students_records/student_record_class_relationship.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule Lanttern.StudentsRecords.StudentRecordClassRelationship do
@moduledoc """
The `StudentRecordClassRelationship` schema (join table)
"""

use Ecto.Schema
import Ecto.Changeset

@primary_key false
schema "students_records_classes" do
field :student_record_id, :id, primary_key: true
field :class_id, :id, primary_key: true
field :school_id, :id
end

@doc false
def changeset(student_record_class_relationship, attrs) do
student_record_class_relationship
|> cast(attrs, [:class_id, :student_record_id, :school_id])
|> validate_required([:class_id, :student_record_id, :school_id])
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ defmodule Lanttern.StudentsRecords.StudentRecordRelationship do
@doc false
def changeset(student_record_relationship, attrs) do
student_record_relationship
|> cast(attrs, [:student_id, :student_record_i, :school_id])
|> cast(attrs, [:student_id, :student_record_id, :school_id])
|> validate_required([:student_id, :student_record_id, :school_id])
end
end
2 changes: 2 additions & 0 deletions lib/lanttern/students_records_log/student_record_log.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ defmodule Lanttern.StudentsRecordsLog.StudentRecordLog do
field :date, :date
field :time, :time
field :students_ids, {:array, :id}
field :classes_ids, {:array, :id}
field :school_id, :id
field :type_id, :id
field :status_id, :id
Expand All @@ -36,6 +37,7 @@ defmodule Lanttern.StudentsRecordsLog.StudentRecordLog do
:date,
:time,
:students_ids,
:classes_ids,
:school_id,
:type_id,
:status_id
Expand Down
14 changes: 10 additions & 4 deletions lib/lanttern_web/components/core_components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ defmodule LantternWeb.CoreComponents do
import Phoenix.HTML, only: [raw: 1]
import LantternWeb.Gettext

import LantternWeb.SchoolsHelpers, only: [class_with_cycle: 2]

@doc """
Renders a `<button>` or `<.link>` with icon.
Expand Down Expand Up @@ -155,7 +157,7 @@ defmodule LantternWeb.CoreComponents do
<span
id={@id}
class={[
"inline-flex items-center rounded-sm px-1 py-1 font-mono font-normal text-xs",
"inline-flex items-center rounded-sm px-1 py-1 font-mono font-normal text-xs whitespace-nowrap",
badge_theme(@theme),
@class
]}
Expand All @@ -172,7 +174,7 @@ defmodule LantternWeb.CoreComponents do
<span class="sr-only"><%= gettext("Remove") %></span>
<.icon
name="hero-x-mark-mini"
class="w-3.5 text-ltrn-subtle hover:text-ltrn-dark"
class="w-3.5 text-ltrn-dark"
style={create_color_map_text_style(@color_map)}
/>
<span class="absolute -inset-1"></span>
Expand Down Expand Up @@ -335,6 +337,10 @@ defmodule LantternWeb.CoreComponents do
default: nil,
doc: "supports \"class_with_cycle\" opt, which will render the class + cycle name"

attr :current_user_or_cycle, :any,
default: nil,
doc: "used when `label_setter` is \"class_with_cycle\""

attr :on_select, :any,
required: true,
doc: "expects a function with arity 1. will receive the `item.id` as arg"
Expand All @@ -358,7 +364,7 @@ defmodule LantternWeb.CoreComponents do
phx-click={@on_select.(item.id)}
>
<%= case @label_setter do
"class_with_cycle" -> "#{item.name} (#{item.cycle.name})"
"class_with_cycle" -> class_with_cycle(item, @current_user_or_cycle)
_ -> item.name
end %>
</.badge_button>
Expand Down Expand Up @@ -704,7 +710,7 @@ defmodule LantternWeb.CoreComponents do
|> assign(:grid_col_span_style, grid_col_span_style)

~H"""
<div class={@class}>
<div class={["bg-white shadow-xl", @class]}>
<div class="grid" style={@grid_template_cols_style}>
<div
class="sticky z-10 grid grid-cols-subgrid font-display font-bold text-sm bg-white shadow"
Expand Down
92 changes: 68 additions & 24 deletions lib/lanttern_web/helpers/filters_helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,6 @@ defmodule LantternWeb.FiltersHelpers do
- `:selected_years_ids`
- `:selected_years`
### `:classes` assigns
- `:classes`
- `:selected_classes_ids`
- `:selected_classes`
### `:assessment_view assigns
- `:current_assessment_view`
Expand Down Expand Up @@ -124,20 +118,6 @@ defmodule LantternWeb.FiltersHelpers do
|> assign_filter_type(current_user, current_filters, filter_types)
end

defp assign_filter_type(socket, current_user, current_filters, [:classes | filter_types]) do
classes =
Schools.list_user_classes(current_user)

selected_classes_ids = Map.get(current_filters, :classes_ids) || []
selected_classes = Enum.filter(classes, &(&1.id in selected_classes_ids))

socket
|> assign(:classes, classes)
|> assign(:selected_classes_ids, selected_classes_ids)
|> assign(:selected_classes, selected_classes)
|> assign_filter_type(current_user, current_filters, filter_types)
end

defp assign_filter_type(
socket,
current_user,
Expand Down Expand Up @@ -267,6 +247,68 @@ defmodule LantternWeb.FiltersHelpers do
starred_strands: :only_starred_strands
}

@doc """
Handle classes filter assigns in socket.
## Opts
Any opts accepted in `list_user_classes/2`.
## Expected assigns in socket
- `current_user` - used to get the current cycle information
## Returned socket assigns
- `:classes`
- `:selected_classes_ids`
- `:selected_classes`
## Examples
iex> assign_classes_filter(socket)
socket
"""
@spec assign_classes_filter(Phoenix.LiveView.Socket.t(), opts :: Keyword.t()) ::
Phoenix.LiveView.Socket.t()
def assign_classes_filter(socket, opts \\ []) do
classes =
Schools.list_user_classes(socket.assigns.current_user, opts)

selected_classes_ids =
case Personalization.get_profile_settings(socket.assigns.current_user.current_profile_id) do
%{current_filters: current_filters} when not is_nil(current_filters) -> current_filters
_ -> %{}
end
|> Map.get(:classes_ids) || []

selected_classes = Enum.filter(classes, &(&1.id in selected_classes_ids))

# as classes may be filtered (by cycle, for example), selected classes
# may have more classes than the listed. we check for this case below,
# and adjust classes and selected classes as needed

classes_ids = Enum.map(classes, & &1.id)

selected_classes_ids_not_in_classes =
Enum.filter(selected_classes_ids, &(&1 not in classes_ids))

selected_classes_not_in_classes =
if selected_classes_ids_not_in_classes != [] do
Schools.list_classes(classes_ids: selected_classes_ids_not_in_classes)
else
[]
end

classes = classes ++ selected_classes_not_in_classes
selected_classes = selected_classes ++ selected_classes_not_in_classes

socket
|> assign(:classes, classes)
|> assign(:selected_classes_ids, selected_classes_ids)
|> assign(:selected_classes, selected_classes)
end

@doc """
Handle strand classes filter assigns in socket.
Expand Down Expand Up @@ -585,11 +627,13 @@ defmodule LantternWeb.FiltersHelpers do
socket
end

defp apply_save_profile_filters(current_user, attrs, strand_id: strand_id),
do: Filters.set_profile_strand_filters(current_user, strand_id, attrs)
defp apply_save_profile_filters(current_user, attrs, strand_id: strand_id)
when is_integer(strand_id),
do: Filters.set_profile_strand_filters(current_user, strand_id, attrs)

defp apply_save_profile_filters(current_user, attrs, report_card_id: report_card_id),
do: Filters.set_profile_report_card_filters(current_user, report_card_id, attrs)
defp apply_save_profile_filters(current_user, attrs, report_card_id: report_card_id)
when is_integer(report_card_id),
do: Filters.set_profile_report_card_filters(current_user, report_card_id, attrs)

defp apply_save_profile_filters(current_user, attrs, _),
do: Filters.set_profile_current_filters(current_user, attrs)
Expand Down
Loading

0 comments on commit 87b0468

Please sign in to comment.