app/presenters/cohorts/students_presenter.rb
module Cohorts
class StudentsPresenter < ApplicationPresenter
def initialize(view_context, cohort, organisation: nil)
@organisation = organisation
@course = cohort.course
@cohort = cohort
super(view_context)
end
def t(key, variables = {})
I18n.t("presenters.cohorts.students.#{key}", **variables)
end
def filter
@filter ||=
begin
milestone_targets_data =
milestone_targets
.pluck(:id, "assignments.milestone_number", :title)
.map do |id, number, title|
"#{id};#{I18n.t("presenters.cohorts.students.milestone_status_filter_value", m: I18n.t("shared.m"), number: number, title: title)}"
end
{
id: "cohort-students-filter",
filters: [
{
key: "milestone_completed",
label: t("milestone_completed"),
filterType: "MultiSelect",
values: milestone_targets_data,
color: "blue"
},
{
key: "milestone_incomplete",
label: t("milestone_incomplete"),
filterType: "MultiSelect",
values: milestone_targets_data,
color: "orange"
},
{
key: "course",
label: t("course_completion"),
filterType: "MultiSelect",
values: %w[Completed Incomplete],
color: "green"
},
{
key: "name",
label: t("search_by_name"),
filterType: "Search",
color: "red"
},
{
key: "email",
label: t("search_by_email"),
filterType: "Search",
color: "yellow"
}
],
placeholder: t("filter_placeholder"),
hint: t("filter_hint"),
sorter: {
key: "sort_by",
default: "Recently Seen",
options: [
"Recently Seen",
"Name",
"First Created",
"Last Created",
"Earliest Seen"
]
}
}
end
end
def counts
@counts = {
total: scope.count,
completed: scope.where.not(completed_at: nil).count
}
end
def filters_in_url
params
.slice(
:name,
:email,
:milestone_completed,
:milestone_incomplete,
:course
)
.permit(
:name,
:email,
:milestone_completed,
:milestone_incomplete,
:course
)
.compact
end
def students
@students ||=
begin
filter_1 = filter_students_by_milestone_completed(scope)
filter_2 = filter_students_by_milestone_incomplete(filter_1)
filter_3 = filter_students_by_course_completion(filter_2)
filter_4 = filter_students_by_name(filter_3)
filter_5 = filter_students_by_email(filter_4)
sorted = sort_students(filter_5)
included = sorted.includes(:user)
paged = included.page(params[:page]).per(24)
paged.count.zero? ? paged.page(paged.total_pages) : paged
end
end
def milestone_completion_status
status = Hash.new({ percentage: 0, students_count: 0 })
TimelineEvent
.from_students(scope)
.where(target: milestone_targets)
.passed
.live
.group(:target_id)
.joins(:students)
.select("target_id, COUNT(DISTINCT students.id) AS students_count")
.each do |submission|
target = milestone_targets.find { |t| t.id == submission.target_id }
percentage =
((submission.students_count / counts[:total].to_f) * 100).round
status[target.id] = {
percentage: percentage,
students_count: submission.students_count
}
end
status
end
def milestone_targets
@milestone_targets ||=
@course.targets.live.milestone.order("assignments.milestone_number")
end
def page_title
t("page_title", cohort_name: @cohort.name, course_name: @course.name)
end
private
def filter_students_by_name(scope)
if params[:name].present?
scope.joins(:user).where("users.name ILIKE ?", "%#{params[:name]}%")
else
scope
end
end
def filter_students_by_email(scope)
if params[:email].present?
scope.joins(:user).where(
"lower(users.email) ILIKE ?",
"%#{params[:email].downcase}%"
)
else
scope
end
end
def milestone_completed_students(param)
scope
.joins(timeline_events: { target: :assignments })
.where(targets: { id: param, assignments: { milestone: true } })
.merge(TimelineEvent.passed)
.merge(TimelineEvent.live)
end
def filter_students_by_milestone_completed(scope)
if params[:milestone_completed].present?
milestone_completed_students(params[:milestone_completed])
else
scope
end
end
def filter_students_by_milestone_incomplete(scope)
if params[:milestone_incomplete].present?
scope.where.not(
id: milestone_completed_students(params[:milestone_incomplete])
)
else
scope
end
end
def filter_students_by_course_completion(scope)
if params[:course] == "Completed"
scope.where.not(completed_at: nil)
elsif params[:course] == "Incomplete"
scope.where(completed_at: nil)
else
scope
end
end
def sort_students(scope)
case params[:sort_by]
when "Name"
scope.joins(:user).order("users.name")
when "First Created"
scope.order(created_at: :asc)
when "Earliest Seen"
scope.joins(:user).order("users.last_seen_at ASC NULLS FIRST")
when "Last Created"
scope.order(created_at: :desc)
else
scope.joins(:user).order("users.last_seen_at DESC NULLS LAST")
end
end
def scope
if @organisation.present?
@scope ||=
@organisation.students.not_dropped_out.where(cohort_id: @cohort.id)
else
@scope ||= @cohort.students.not_dropped_out
end
end
end
end