course.rb 2.87 KB
Newer Older
Zac Wood's avatar
Zac Wood committed
1
2
3
# Contains logic regarding the +Course+ model.
#
# TODO: Add more docs
4
5
6

require("course_replacement_helper.rb")

Zac Wood's avatar
Zac Wood committed
7
class Course < ApplicationRecord
Zac Wood's avatar
Zac Wood committed
8
  # Each course belongs to a +Semester+
Zac Wood's avatar
Zac Wood committed
9
  belongs_to :semester
10
  has_many :course_sections
Zac Wood's avatar
Zac Wood committed
11

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

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

23
24
25
  def self.from_subject(base_query, subject)
    base_query.where("courses.subject = ?", subject.upcase)
  end
Zach Perkins's avatar
Zach Perkins committed
26

27
  def self.from_course_number(base_query, course_number)
Zach Perkins's avatar
Zach Perkins committed
28
    base_query.where("courses.course_number = ?", course_number)
Zach Perkins's avatar
Zach Perkins committed
29
  end
Zach Perkins's avatar
Zach Perkins committed
30

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

Zach Perkins's avatar
Zach Perkins committed
38
39
40
  # 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)
41
42
    # 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")
43
44
45
46
47
    if filters.include? "query"
      filters = Course.parse_generic_query(filters["query"])
    end

    filters.each do |filter, value|
Zach Perkins's avatar
Zach Perkins committed
48
49
50
51
52
53
54
      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)
55
56
      when "instructor"
        query = Instructor.from_name(query.joins("INNER JOIN instructors ON course_sections.instructor_id = instructors.id"), value)
Zach Perkins's avatar
Zach Perkins committed
57
58
      end
    end
Zach Perkins's avatar
Zach Perkins committed
59

60
    query
Zach Perkins's avatar
Zach Perkins committed
61
  end
Zach Perkins's avatar
Zach Perkins committed
62

Zach Perkins's avatar
Zach Perkins committed
63
64
65
  # 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)
66
67
    query.upcase!
    CourseReplacementHelper.replace!(query)
68

Zach Perkins's avatar
Zach Perkins committed
69
    filters = {}
70
71
    query.scan(/(?<= |^)([a-zA-Z]{2,4})(?=$| )/).each do |a|
      s = a[0]
72
      if from_subject(select("*"), s).count.positive?
73
74
75
76
        filters["subject"] = s
        query.remove!(s)
      end
    end
Zach Perkins's avatar
Zach Perkins committed
77
 
78
79
    query.scan(/(?<= |^)(\d{3})(?=$| )/).each do |a|
      s = a[0]
Zach Perkins's avatar
Zach Perkins committed
80
      next unless filters.include?("subject") && from_course_number(from_subject(select("*"), filters["subject"]), s).count.positive?
81
82
83
      filters["course_number"] = s
      query.remove!(s)
      return filters
84
    end
85

86
    filters["title"] = query.gsub(/ +/, " ").strip
Zach Perkins's avatar
Zach Perkins committed
87
    filters
Zach Perkins's avatar
Zach Perkins committed
88
  end
Zac Wood's avatar
Zac Wood committed
89
end