Huge!
I don’t have many monolithic functions, but this is one, mostly because it is a fairly direct post/rewrite of an older function in another language that is 20 years old…
And it would be larger if ecto was capable enough to support cross-schema joins… >.>
def query_classes(selected \\ :processed, refine) do
squery =
from course in DB.Banner.SCBCRSE,
join: section in DB.Banner.SSBSECT, on: section.ssbsect_subj_code == course.scbcrse_subj_code and section.ssbsect_crse_numb == course.scbcrse_crse_numb and section.ssbsect_ssts_code == "A",
join: dept in DB.Banner.STVDEPT, on: dept.stvdept_code == course.scbcrse_dept_code
squery =
Enum.reduce(refine, squery, fn
({:pidm, true}, squery) ->
join(squery, :inner, [course, section, dept],
student_course in DB.Banner.SFRSTCR,
student_course.sfrstcr_term_code == section.ssbsect_term_code and
student_course.sfrstcr_crn == section.ssbsect_crn
)
({:pidm, pidm}, squery) when is_integer(pidm) ->
join(squery, :inner, [course, section, dept],
student_course in DB.Banner.SFRSTCR,
student_course.sfrstcr_pidm == ^pidm and
student_course.sfrstcr_term_code == section.ssbsect_term_code and
student_course.sfrstcr_crn == section.ssbsect_crn
)
({:pidm, pidms}, squery) when is_list(pidms) ->
join(squery, :inner, [course, section, dept],
student_course in DB.Banner.SFRSTCR,
student_course.sfrstcr_pidm in ^pidms and
student_course.sfrstcr_term_code == section.ssbsect_term_code and
student_course.sfrstcr_crn == section.ssbsect_crn
)
({:registered, true}, squery) ->
where(squery, [course, section, dept, student_course], student_course.sfrstcr_rsts_code in ["RA", "RE", "RW"])
({:withdrawn, true}, squery) ->
where(squery, [course, section, dept, student_course], student_course.sfrstcr_rsts_code == "WD")
({:department, dept_code}, squery) when is_binary(dept_code) ->
where(squery, [course, section, dept], course.scbcrse_dept_code == ^dept_code)
({:department, dept_code}, squery) when is_list(dept_code) ->
where(squery, [course, section, dept], course.scbcrse_dept_code in ^dept_code)
({:subject, subject_code}, squery) when is_binary(subject_code) ->
where(squery, [course, section, dept], section.ssbsect_subj_code == ^subject_code)
({:subject, subject_code}, squery) when is_list(subject_code) ->
where(squery, [course, section, dept], section.ssbsect_subj_code in ^subject_code)
({:course, course_number}, squery) when is_binary(course_number) ->
where(squery, [course, section, dept], section.ssbsect_crse_numb == ^course_number)
({:course, course_number}, squery) when is_list(course_number) ->
where(squery, [course, section, dept], section.ssbsect_crse_numb in ^course_number)
({semester, year}, squery) when semester in [:spring, :summer, :fall] and is_integer(year) and year>=1900 and year<=9999 -> squery # Handled below in `dyn`
({:course_group, %DB.Course.Group{}=course_group}, squery) ->
squery = if(course_group.dept_codes, do: where(squery, [course, section, dept], course.scbcrse_dept_code in ^course_group.dept_codes), else: squery)
squery = if(course_group.subject_codes, do: where(squery, [course, section, dept], section.ssbsect_subj_code in ^course_group.subject_codes), else: squery)
squery = if(course_group.course_numbers, do: where(squery, [course, section, dept], section.ssbsect_crse_numb in ^course_group.course_numbers), else: squery)
# TODO: Test the term codes and such too once they are needed
squery
end)
dyn =
Enum.reduce(refine, false, fn
({semester, year}, dyn) when semester in [:spring, :summer, :fall] and is_integer(year) and year>=1900 and year<=9999 ->
term_code =
case semester do
:spring -> "#{year}10"
:summer -> "#{year}20"
:fall -> "#{year}30"
end
case dyn do
false -> dynamic([course, section, dept], section.ssbsect_term_code == ^term_code)
dyn -> dynamic([course, section, dept], ^dyn or section.ssbsect_term_code == ^term_code)
end
(_, dyn) -> dyn
end)
squery =
case dyn do
false -> squery
dyn -> where(squery, ^dyn)
end
squery =
case selected do
:all -> squery
:processed ->
case Keyword.get(refine, :pidm) do
v when v == true or is_list(v) ->
select(squery, [course, section, dept, student_course], %{
pidm: student_course.sfrstcr_pidm,
department_code: course.scbcrse_dept_code,
department_description: dept.stvdept_desc,
crn: section.ssbsect_crn,
subject: section.ssbsect_subj_code,
course_number: section.ssbsect_crse_numb,
section_number: section.ssbsect_seq_numb,
title: fragment("coalesce(?, ?)", section.ssbsect_crse_title, course.scbcrse_title),
section_begins: section.ssbsect_ptrm_start_date,
section_ends: section.ssbsect_ptrm_end_date,
registration_code: student_course.sfrstcr_rsts_code,
effective_term: course.scbcrse_eff_term,
_effective_term_rank: fragment("rank() OVER (PARTITION BY ?, ? ORDER BY ? DESC)", section.ssbsect_subj_code, section.ssbsect_crse_numb, course.scbcrse_eff_term),
})
_ ->
select(squery, [course, section, dept], %{
department_code: course.scbcrse_dept_code,
department_description: dept.stvdept_desc,
crn: section.ssbsect_crn,
subject: section.ssbsect_subj_code,
course_number: section.ssbsect_crse_numb,
section_number: section.ssbsect_seq_numb,
title: fragment("coalesce(?, ?)", section.ssbsect_crse_title, course.scbcrse_title),
section_begins: section.ssbsect_ptrm_start_date,
section_ends: section.ssbsect_ptrm_end_date,
effective_term: course.scbcrse_eff_term,
_effective_term_rank: fragment("rank() OVER (PARTITION BY ?, ? ORDER BY ? DESC)", section.ssbsect_subj_code, section.ssbsect_crse_numb, course.scbcrse_eff_term),
})
end
end
query =
from s in subquery(squery),
where: s._effective_term_rank == 1
query
end
But I can use it like:
# Get all classes of subject "blah" with all pidm information associated with that are fully registered (I.E. not Dropped) of a given term
Queries.Banner.query_classes([subject: "blah", pidm: true, registered: true] ++ [term])
# Get all registered (non-dropped) classes of a specific person (or persons)
Queries.Banner.query_classes(pidm: pidm, registered: true)
# Get low-level non-processed classes for a specific person(s) of registered classes:
Queries.Banner.query_classes(:all, pidm: pidm, registered: true)
This is one of the older ones, the more recent ones are a bit more clear and unified in comparison, but even larger (though with more helpers inside, which make them more clear).
Which happens to fit very well in to Absinthe. ^.^