course.rb 2.37 KB
Newer Older
Zac Wood's avatar
Zac Wood committed
1
2
3
# Contains logic regarding the +Course+ model.
#
# TODO: Add more docs
Zac Wood's avatar
Zac Wood committed
4
class Course < ApplicationRecord
Zac Wood's avatar
Zac Wood committed
5
  # Each course belongs to a +Semester+
Zac Wood's avatar
Zac Wood committed
6
  belongs_to :semester
7
  has_many :course_sections
Zac Wood's avatar
Zac Wood committed
8

9
  # Ensure all necessary are fields present.
Zac Wood's avatar
Zac Wood committed
10
11
12
13
  validates :course_number, presence: true
  validates :subject, presence: true
  validates :semester_id, presence: true

Zac Wood's avatar
Zac Wood committed
14
  # Returns all +CourseSection+ objects that belong to this course.
15
  # @return [Array]
Zac Wood's avatar
Zac Wood committed
16
17
  def course_sections
    CourseSection.where course_id: id
Zac Wood's avatar
Zac Wood committed
18
  end
Zach Perkins's avatar
Zach Perkins committed
19

20
21
22
  def self.from_subject(base_query, subject)
    base_query.where("courses.subject = ?", subject.upcase)
  end
Zach Perkins's avatar
Zach Perkins committed
23

24
  def self.from_course_number(base_query, course_number)
Zach Perkins's avatar
Zach Perkins committed
25
    base_query.where("courses.course_number = ?", course_number)
Zach Perkins's avatar
Zach Perkins committed
26
  end
Zach Perkins's avatar
Zach Perkins committed
27

Zach Perkins's avatar
Zach Perkins committed
28
29
  def self.from_title(base_query, title)
    # Temporary really disgusting regex that I hate with all my heart
Zach Perkins's avatar
Zach Perkins committed
30
    title = (title + " ").gsub(" 1", " I").gsub(" 2", " II").gsub(" 3", " III").upcase.gsub(/(I+) +/, '\1$').gsub(/ +/, "% ").tr('$', ' ')
31
    base_query.where("UPPER(courses.title) LIKE UPPER(?) or UPPER(courses.title) LIKE UPPER(?)", "%#{title.strip}", "%#{title}%")
32
  end
Zach Perkins's avatar
Zach Perkins committed
33

Zach Perkins's avatar
Zach Perkins committed
34
35
36
  # Given a list of filters, collect a list of matching elements. This makes it
  # so you can just pass the arguments straight thru
  def self.fetch(filters)
37
38
    # join with course_sections so that we can get a section count for each course and then sort by that
    query = Course.left_outer_joins(:course_sections).select("courses.*, COUNT(course_sections.id) AS section_count").group("courses.id").order("section_count DESC")
39
40
41
42
43
    if filters.include? "query"
      filters = Course.parse_generic_query(filters["query"])
    end

    filters.each do |filter, value|
Zach Perkins's avatar
Zach Perkins committed
44
45
46
47
48
49
50
      case filter
      when "subject"
        query = from_subject(query, value)
      when "course_number"
        query = from_course_number(query, value)
      when "title"
        query = from_title(query, value)
Zach Perkins's avatar
Zach Perkins committed
51
52
      end
    end
Zach Perkins's avatar
Zach Perkins committed
53

54
    query
Zach Perkins's avatar
Zach Perkins committed
55
  end
Zach Perkins's avatar
Zach Perkins committed
56

Zach Perkins's avatar
Zach Perkins committed
57
58
59
60
  # Splits a generic string (i.e. "CS 211") into a series of components that can
  # be used to run a query with fetch()
  def self.parse_generic_query(query)
    # In the future when there is more info, this will be more complex to
61
    # include class names/descriptions
Zach Perkins's avatar
Zach Perkins committed
62
    filters = {}
Zach Perkins's avatar
Zach Perkins committed
63
    q = query.delete(" ")
Zach Perkins's avatar
Zach Perkins committed
64
65
    /[a-zA-Z]+/.match(q) { |a| filters["subject"] = a.to_s }
    /\d+/.match(q) { |a| filters["course_number"] = a.to_s }
Zach Perkins's avatar
Zach Perkins committed
66
    filters
Zach Perkins's avatar
Zach Perkins committed
67
  end
Zac Wood's avatar
Zac Wood committed
68
end