Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
SRCT
schedules
Commits
d2dd1444
Commit
d2dd1444
authored
Oct 11, 2018
by
Zac Wood
Browse files
Merge branch '26-model-filters' into 'dev-v2'
Resolve "Model filter functionality" See merge request
!32
parents
69e7ffaa
5b489e50
Pipeline
#3213
passed with stage
in 2 minutes and 12 seconds
Changes
21
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
schedules/app/assets/javascripts/search.js
View file @
d2dd1444
...
...
@@ -5,7 +5,7 @@ const sectionWithCrn = crn => document.getElementById('search-list').querySelect
const
addCourse
=
(
event
,
id
)
=>
{
const
courseCard
=
document
.
getElementById
(
`course-
${
id
}
`
);
const
title
=
courseCard
.
querySelector
(
'
#
title
'
).
innerText
;
const
title
=
courseCard
.
querySelector
(
'
.
title
'
).
innerText
;
const
sectionsItems
=
Array
.
from
(
courseCard
.
querySelectorAll
(
'
li
'
));
const
sections
=
sectionsItems
.
map
(
li
=>
({
...
li
.
dataset
}));
...
...
@@ -48,11 +48,12 @@ const removeFromSchedule = section => {
* Toggles the display of the schedule
*/
const
toggleSections
=
course
=>
{
const
sections
=
course
.
querySelector
(
'
#sections
'
);
if
(
sections
.
style
.
display
===
'
block
'
)
{
course
.
querySelector
(
'
#sections
'
).
style
.
display
=
'
none
'
;
const
sections
=
course
.
querySelector
(
'
.sections
'
);
console
.
log
(
sections
);
if
(
sections
.
style
.
display
===
'
flex
'
)
{
sections
.
style
.
display
=
'
none
'
;
}
else
{
course
.
querySelector
(
'
#
sections
'
)
.
style
.
display
=
'
block
'
;
sections
.
style
.
display
=
'
flex
'
;
}
};
...
...
schedules/app/assets/stylesheets/application.scss
View file @
d2dd1444
...
...
@@ -27,6 +27,28 @@ body {
box-shadow
:
0
0
5px
rgba
(
0
,
0
,
0
,
0
.2
);
transition
:
0
.3s
;
.card-header
{
display
:
flex
;
flex-direction
:
column
;
}
.card-body
{
.attr-list
{
display
:
flex
;
flex-direction
:
row
;
justify-content
:
flex-start
;
padding-bottom
:
10px
;
.attr
{
margin-right
:
13px
;
}
}
}
}
.unpadded
{
padding
:
0px
;
}
/* On mouse-over, add a deeper shadow */
...
...
schedules/app/controllers/course_listings_controller.rb
0 → 100644
View file @
d2dd1444
class
CourseListingsController
<
ApplicationController
resource_description
do
short
'Working with courses and associated sections'
end
api
:GET
,
'/course_listings'
,
"Get all available courses and their sections"
param
:subject
,
String
,
desc:
'Course subject, e.g. "CS" or "ACCT"'
param
:number
,
Integer
,
desc:
'Course number, e.g. "112"'
def
index
# Make a separate list so that we can include sections
@courses
=
CourseListingsHelper
::
CourseListing
.
wrap
(
Course
.
fetch
(
params
).
all
)
render
json:
@courses
end
end
schedules/app/controllers/course_sections_controller.rb
View file @
d2dd1444
...
...
@@ -5,16 +5,14 @@ class CourseSectionsController < ApplicationController
short
'Working with course sections, e.g. CS 112 001'
end
api
:GET
,
'/course
s
_sections'
,
'Get a list of course sections'
api
:GET
,
'/course_sections'
,
'Get a list of course sections'
param
:course_id
,
Integer
,
desc:
"Only get the course sections belonging to the course with this ID"
param
:crn
,
String
,
desc:
"Get the course section with this CRN"
param
:instructor
,
String
,
desc:
"Get course sections being taught by this instructor"
def
index
@sections
=
CourseSection
.
with_instructor
(
name:
params
[
:instructor
])
@sections
=
@sections
.
where
(
course_id:
params
[
:course_id
])
if
params
.
key?
(
:course_id
)
@sections
=
@sections
.
where
(
crn:
params
[
:crn
])
if
params
.
key?
(
:crn
)
param
:query
,
String
,
desc:
'A generic query ex. "CS 110"'
def
index
@sections
=
CourseSection
.
fetch
(
params
).
all
render
json:
@sections
end
end
schedules/app/controllers/courses_controller.rb
View file @
d2dd1444
...
...
@@ -8,19 +8,14 @@ class CoursesController < ApplicationController
param
:subject
,
String
,
desc:
'Course subject, e.g. "CS" or "ACCT"'
param
:course_number
,
Integer
,
desc:
'Course number, e.g. "112"'
def
index
@courses
=
Course
.
all
# filter by subject + course number if the params are included
@courses
=
@courses
.
where
(
subject:
params
[
:subject
].
upcase
)
if
params
.
key?
(
:subject
)
@courses
=
@courses
.
where
(
course_number:
params
[
:course_number
])
if
params
.
key?
(
:course_number
)
@courses
=
Course
.
fetch
(
params
).
all
render
json:
@courses
end
api
:GET
,
'/courses/:id'
,
"Get a list of all course sections for the course with the given id."
param
:id
,
:number
,
desc:
'Course ID'
,
required:
true
def
show
@sections
=
CourseSection
.
where
(
course_id:
params
[
:id
])
@sections
=
CourseSection
.
where
(
course_id:
params
[
:id
])
.
all
render
json:
@sections
end
...
...
schedules/app/controllers/search_controller.rb
View file @
d2dd1444
class
SearchController
<
ApplicationController
def
index
@courses
=
Course
.
where
(
subject:
params
[
:q
],
semester:
@semester
).
select
do
|
course
|
course
.
course_sections
.
count
.
positive?
end
@results
=
SearchHelper
::
GenericItem
.
fetchall
(
params
[
:query
],
semester:
@semester
)
end
end
schedules/app/helpers/course_listings_helper.rb
0 → 100644
View file @
d2dd1444
module
CourseListingsHelper
class
CourseListing
def
initialize
(
course
)
@course
=
course
@sections
=
course
.
course_sections
end
def
self
.
name
:course
end
def
self
.
wrap
(
course_list
)
course_listings
=
[]
course_list
.
each
do
|
course
|
course_listings
.
push
(
CourseListing
.
new
(
course
))
end
course_listings
end
end
end
schedules/app/helpers/course_replacement_helper.rb
0 → 100644
View file @
d2dd1444
# Replaces various strings with other strings when needed
module
CourseReplacementHelper
@replacements
=
{
"BIO"
=>
"BIOL"
,
"HONORS"
=>
"_HONORS_"
,
# Sometimes the honors will be in parenthesis so this accounts for that -> lol no it doesn't jk this needs fixed
"ACADEMIC ENGLISH"
=>
"AE"
,
"ACCOUNTING"
=>
"ACCT"
,
"AFRICAN AND AFRICAN AMERICAN STUDIES"
=>
"AFAM"
,
"ANTHROPOLOGY"
=>
"ANTH"
,
"APPLIED INFORMATION TECHNOLOGY"
=>
"AIT"
,
"ARABIC"
=>
"ARAB"
,
"ART AND VISUAL TECHNOLOGY"
=>
"AVT"
,
"ART HISTORY"
=>
"ARTH"
,
"ARTS MANAGEMENT"
=>
"AMGT"
,
"ASSISTIVE TECHNOLOGY"
=>
"EDAT"
,
"ASTRONOMY"
=>
"ASTR"
,
"ATHLETIC TRAINING EDUCATION PROGRAM"
=>
"ATEP"
,
"BACHELOR OF INDIVIDUALIZED STUDY"
=>
"BIS"
,
"BACHELOR'S OF APPLIED SCIENCE"
=>
"BAS"
,
"BIODEFENSE"
=>
"BIOD"
,
"BIOENGINEERING"
=>
"BENG"
,
"BIOINFORMATICS"
=>
"BINF"
,
"BIOLOGY"
=>
"BIOL"
,
"BIOMEDICAL SCIENCES"
=>
"BMED"
,
"BIOSCIENCES"
=>
"BIOS"
,
"BUSINESS AND LEGAL STUDIES"
=>
"BULE"
,
"BUSINESS"
=>
"BUS"
,
"BUSINESS MANAGEMENT"
=>
"BMGT"
,
"BUSINESS MANAGEMENT OF SECURE INFORMATION SYSTEMS"
=>
"MSIS"
,
"CHEMISTRY"
=>
"CHEM"
,
"CHINESE"
=>
"CHIN"
,
"CIVIL AND INFRASTRUCTURE ENGINEERING"
=>
"CEIE"
,
"CLASSICS"
=>
"CLAS"
,
"CLIMATE DYNAMICS"
=>
"CLIM"
,
"COLLEGE OF SCIENCE"
=>
"COS"
,
"COLLEGE OF VISUAL AND PERFORMING ARTS"
=>
"CVPA"
,
"COMMUNICATION"
=>
"COMM"
,
"COMPUTATIONAL AND DATA SCIENCES"
=>
"CDS"
,
"COMPUTATIONAL SCIENCE AND INFORMATICS"
=>
"CSI"
,
"COMPUTATIONAL SOCIAL SCIENCE"
=>
"CSS"
,
"COMPUTER FORENSICS"
=>
"CFRS"
,
"COMPUTER GAME DESIGN"
=>
"GAME"
,
"COMPUTER SCIENCE"
=>
"CS"
,
"COMP SCI"
=>
"CS"
,
"CONFLICT ANALYSIS AND RESOLUTION"
=>
"CONF"
,
"CONSERVATION STUDIES"
=>
"CONS"
,
"COUNSELING AND DEVELOPMENT"
=>
"EDCD"
,
"CRIMINOLOGY"
=>
"CRIM"
,
"CULTURAL STUDIES"
=>
"CULT"
,
"CURRICULUM AND INSTRUCTION"
=>
"EDCI"
,
"CYBER SECURITY ENGINEERING"
=>
"CYSE"
,
"DANCE"
=>
"DANC"
,
"DATA ANALYTICS ENGINEERING"
=>
"DAEN"
,
"EARLY CHILDHOOD EDUCATION"
=>
"ECED"
,
"ECONOMICS"
=>
"ECON"
,
"EDUCATIONAL PSYCHOLOGY"
=>
"EDEP"
,
"EDUCATION"
=>
"EDUC"
,
"EDUCATION INSTRUCTIONAL TECHNOLOGY"
=>
"EDIT"
,
"EDUCATION LEADERSHIP"
=>
"EDLE"
,
"EDUCATION RESEARCH"
=>
"EDRS"
,
"ELECTRICAL AND COMPUTER ENGINEERING"
=>
"ECE"
,
"ENGINEERING"
=>
"ENGR"
,
"ENGLISH"
=>
"ENGH"
,
"ENGLISH FOR ACADEMIC PURPOSES"
=>
"EAP"
,
"ENVIRONMENTAL SCIENCE AND POLICY"
=>
"EVPP"
,
"EXECUTIVE MBA"
=>
"EMBA"
,
"EXERCISE, FITNESS, AND HEALTH PROMOTION"
=>
"EFHP"
,
"FILM AND VIDEO STUDIES"
=>
"FAVS"
,
"FINANCE"
=>
"FNAN"
,
"FOREIGN LANGUAGE"
=>
"FRLN"
,
"FORENSIC SCIENCE"
=>
"FRSC"
,
"FRENCH"
=>
"FREN"
,
"GEOGRAPHY AND GEOINFORMATION SCIENCE"
=>
"GGS"
,
"GEOLOGY"
=>
"GEOL"
,
"GERMAN"
=>
"GERM"
,
"GLOBAL AFFAIRS"
=>
"GLOA"
,
"GLOBAL AND COMMUNITY HEALTH"
=>
"GCH"
,
"GOVERNMENT"
=>
"GOVT"
,
"GRADUATE SCHOOL OF BUSINESS"
=>
"GBUS"
,
"GREEK"
=>
"GREE"
,
"HEALTH ADMINISTRATION AND POLICY"
=>
"HAP"
,
"HEALTH AND HUMAN SERVICES"
=>
"HHS"
,
"HEALTH"
=>
"HEAL"
,
"HEBREW"
=>
"HEBR"
,
"HIGHER EDUCATION"
=>
"HE"
,
"HISTORY"
=>
"HIST"
,
"HONORS COLLEGE"
=>
"HNRS"
,
"HUMAN DEVELOPMENT AND FAMILY SCIENCE"
=>
"HDFS"
,
"INFORMATION SECURITY ASSURANCE"
=>
"ISA"
,
"INFORMATION SYSTEMS"
=>
"INFS"
,
"INFORMATION TECHNOLOGY"
=>
"IT"
,
"INITIATIVES IN EDUCATIONAL TRANSFORMATION-TEACHING"
=>
"IETT"
,
"INTEGRATIVE STUDIES"
=>
"INTS"
,
"INTERDISCIPLINARY STUDIES"
=>
"MAIS"
,
"INTERNATIONAL COMMERCE AND POLICY"
=>
"ITRN"
,
"INTERNATIONAL YEAR ONE"
=>
"INYO"
,
"ITALIAN"
=>
"ITAL"
,
"JAPANESE"
=>
"JAPA"
,
"KINESIOLOGY"
=>
"KINE"
,
"KOREAN"
=>
"KORE"
,
"LATIN AMERICAN STUDIES"
=>
"LAS"
,
"LATIN"
=>
"LATN"
,
"LINGUISTICS"
=>
"LING"
,
"MANAGEMENT"
=>
"MGMT"
,
"MANAGEMENT OF INFORMATION SYSTEMS"
=>
"MIS"
,
"MANAGEMENT OF SECURE INFORMATION SYSTEMS"
=>
"MSEC"
,
"MARKETING"
=>
"MKTG"
,
"MATHEMATICS"
=>
"MATH"
,
"MECHANICAL ENGINEERING"
=>
"ME"
,
"MEDICAL LABORATORY SCIENCE"
=>
"MLAB"
,
"MIDDLE EAST AND ISLAMIC STUDIES"
=>
"MEIS"
,
"MILITARY SCIENCE"
=>
"MLSC"
,
"MINOR IN BUSINESS"
=>
"MBUS"
,
"MUSIC"
=>
"MUSI"
,
"NANOTECHNOLOGY AND NANOSCIENCE"
=>
"NANO"
,
"NATIVE AMERICAN AND INDIGENOUS STUDIES"
=>
"NAIS"
,
"NEUROSCIENCE"
=>
"NEUR"
,
"NURSING"
=>
"NURS"
,
"NUTRITION AND FOOD STUDIES"
=>
"NUTR"
,
"OPERATIONS MANAGEMENT"
=>
"OM"
,
"OPERATIONS RESEARCH"
=>
"OR"
,
"ORGANIZATION DEVELOPMENT AND KNOWLEDGE MANAGEMENT"
=>
"ODKM"
,
"PARKS, RECREATION, AND LEISURE STUDIES"
=>
"PRLS"
,
"PERSIAN"
=>
"PERS"
,
"PHILOSOPHY"
=>
"PHIL"
,
"PHYSICAL EDUCATION"
=>
"PHED"
,
"PHYSICS"
=>
"PHYS"
,
"POLICY AND GOVERNMENT"
=>
"POGO"
,
"PORTUGUESE"
=>
"PORT"
,
"PROFESSIONAL DEVELOPMENT IN EDUCATION"
=>
"EDPD"
,
"PROVOST"
=>
"PROV"
,
"PSYCHOLOGY"
=>
"PSYC"
,
"PUBLIC ADMINISTRATION"
=>
"PUAD"
,
"PUBLIC POLICY"
=>
"PUBP"
,
"READING"
=>
"EDRD"
,
"REAL ESTATE DEVELOPMENT"
=>
"REAL"
,
"RECREATION"
=>
"RECR"
,
"REHABILITATION SCIENCE"
=>
"RHBS"
,
"RELIGIOUS STUDIES"
=>
"RELI"
,
"RUSSIAN"
=>
"RUSS"
,
"SCHOOL OF MANAGEMENT"
=>
"SOM"
,
"SCHOOL PSYCHOLOGY"
=>
"SPSY"
,
"SOCIAL WORK"
=>
"SOCW"
,
"SOCIOLOGY AND ANTHROPOLOGY"
=>
"SOAN"
,
"SOCIOLOGY"
=>
"SOCI"
,
"SOFTWARE ENGINEERING"
=>
"SWE"
,
"SPANISH"
=>
"SPAN"
,
"SPECIAL EDUCATION"
=>
"EDSE"
,
"SPORT MANAGEMENT"
=>
"SPMT"
,
"SPORTS AND RECREATION STUDIES"
=>
"SRST"
,
"STATISTICS"
=>
"STAT"
,
"SYSTEM ENGINEERING"
=>
"SYST"
,
"SYSTEMS ENGINEERING AND OPERATIONS RESEARCH"
=>
"SEOR"
,
"TECHNOLOGY MANAGEMENT"
=>
"TECM"
,
"TELECOMMUNICATIONS"
=>
"TCOM"
,
"THEATER"
=>
"THR"
,
"TOURISM AND EVENTS MANAGEMENT"
=>
"TOUR"
,
"TURKISH"
=>
"TURK"
,
"UNIVERSITY STUDIES"
=>
"UNIV"
,
"URBAN AND SUBURBAN STUDIES"
=>
"USST"
,
"VOLGENAU SCHOOL OF ENGINEERING"
=>
"VSE"
,
"WOMEN AND GENDER STUDIES"
=>
"WMS"
,
"1"
=>
"I"
,
"2"
=>
"II"
,
"3"
=>
"III"
,
"4"
=>
"IV"
,
"5"
=>
"V"
}
def
self
.
replace!
(
input
)
@replacements
.
each
do
|
thing
,
replacement
|
# We want each instance of thing to be it's own word
input
.
gsub!
(
/(?<= |^)
#{
thing
}
(?= |$)/
,
replacement
)
end
end
end
schedules/app/helpers/search_helper.rb
View file @
d2dd1444
module
SearchHelper
class
GenericQueryData
attr_reader
:semester
attr_reader
:sort_mode
attr_reader
:search_string
def
initialize
(
search_string
,
sort_mode
,
semester
)
@semester
=
semester
@sort_mode
=
sort_mode
@search_string
=
search_string
end
end
class
GenericItem
attr_reader
:data
attr_reader
:type
def
initialize
(
type
,
data
)
@type
=
type
@data
=
data
end
def
self
.
fetchall
(
search_string
,
sort_mode: :auto
,
semester: :fall2018
)
query_data
=
GenericQueryData
.
new
(
search_string
,
sort_mode
,
semester
)
models
=
[]
models
+=
fetch_instructors
query_data
models
+=
fetch_courses
query_data
build_list
(
models
)
end
def
self
.
fetch_instructors
(
query_data
)
Instructor
.
from_name
(
Instructor
.
select
(
"instructors.*, COUNT(course_sections.id) AS section_count"
),
query_data
.
search_string
)
.
left_outer_joins
(
:course_sections
)
.
group
(
"instructors.id"
)
end
def
self
.
fetch_courses
(
query_data
)
query_string
=
query_data
.
search_string
query_string
.
upcase!
CourseReplacementHelper
.
replace!
(
query_string
)
base_query
=
Course
.
select
(
"courses.*, count(course_sections.id) AS section_count"
)
.
left_outer_joins
(
:course_sections
)
.
having
(
"section_count > 0"
)
.
where
(
"courses.semester_id = ?"
,
query_data
.
semester
)
.
group
(
"courses.id"
)
subj
=
nil
query_string
.
scan
(
/(?<= |^)([a-zA-Z]{2,4})(?=$| )/
).
each
do
|
a
|
s
=
a
[
0
]
next
unless
get_count
(
Course
.
from_subject
(
base_query
,
s
)).
positive?
subj
=
s
base_query
=
Course
.
from_subject
(
base_query
,
subj
)
query_string
.
remove!
(
s
)
end
query_string
.
scan
(
/(?<= |^)(\d{3})(?=$| )/
).
each
do
|
a
|
s
=
a
[
0
]
next
unless
!
subj
.
nil?
&&
get_count
(
Course
.
from_course_number
(
base_query
,
s
)).
positive?
base_query
=
Course
.
from_course_number
(
base_query
,
s
)
return
base_query
.
all
end
stripped_query_string
=
query_string
.
gsub
(
/ +/
,
" "
).
strip
# There's more to parse
base_query
=
if
stripped_query_string
.
length
.
positive?
Course
.
from_title
(
base_query
,
stripped_query_string
)
.
order
(
"section_count DESC"
)
else
base_query
.
order
(
"courses.course_number ASC"
)
end
base_query
.
all
end
# Given a set of models, create a list of GenericItems for each model's data
def
self
.
build_list
(
models
)
list
=
[]
models
.
each
do
|
model
|
list
.
push
(
GenericItem
.
new
(
model
.
class
.
name
.
underscore
.
to_sym
,
model
))
end
list
end
def
self
.
get_count
(
base_query
)
# I think I finally hit a limit of active record
ActiveRecord
::
Base
.
connection
.
execute
(
"SELECT COUNT(*) AS count FROM (
#{
base_query
.
to_sql
}
)"
)[
0
][
"count"
]
end
def
to_s
@type
end
end
end
schedules/app/models/course.rb
View file @
d2dd1444
# Contains logic regarding the +Course+ model.
#
# TODO: Add more docs
require
(
"course_replacement_helper.rb"
)
class
Course
<
ApplicationRecord
# Each course belongs to a +Semester+
belongs_to
:semester
...
...
@@ -10,4 +13,50 @@ class Course < ApplicationRecord
validates
:course_number
,
presence:
true
validates
:subject
,
presence:
true
validates
:semester_id
,
presence:
true
# Returns all +CourseSection+ objects that belong to this course.
# @return [Array]
def
course_sections
CourseSection
.
where
course_id:
id
end
def
self
.
from_subject
(
base_query
,
subject
)
base_query
.
where
(
"courses.subject = ?"
,
subject
.
upcase
)
end
def
self
.
from_course_number
(
base_query
,
course_number
)
base_query
.
where
(
"courses.course_number = ?"
,
course_number
)
end
def
self
.
from_title
(
base_query
,
title
)
puts
title
# Temporary really disgusting regex that I hate with all my heart
title
=
(
title
+
" "
).
upcase
.
gsub
(
/(I+) +/
,
'\1$'
).
gsub
(
/ +/
,
"% "
).
tr
(
'$'
,
' '
)
base_query
.
where
(
"UPPER(courses.title) LIKE UPPER(?) or UPPER(courses.title) LIKE UPPER(?)"
,
"%
#{
title
.
strip
}
"
,
"%
#{
title
}
%"
)
end
# 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
)
# 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"
)
filters
.
each
do
|
filter
,
value
|
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
)
when
"instructor"
query
=
Instructor
.
from_name
(
query
.
joins
(
"INNER JOIN instructors ON course_sections.instructor_id = instructors.id"
),
value
)
end
end
query
end
end
schedules/app/models/course_section.rb
View file @
d2dd1444
...
...
@@ -14,4 +14,34 @@ class CourseSection < ApplicationRecord
def
self
.
with_instructor
(
name:
""
)
joins
(
:instructor
).
where
(
"instructors.name LIKE ?"
,
"%
#{
name
}
%"
).
select
(
'course_sections.*, instructors.name as instructor_name'
)
end
def
self
.
from_crn
(
base_query
,
crn
)
base_query
.
where
(
"course_sections.crn = ?"
,
crn
)
end
def
self
.
from_course_id
(
base_query
,
course_id
)
base_query
.
where
(
"course_sections.course_id = ?"
,
course_id
)
end
# Select all revelevant course sections given the provided filters
def
self
.
fetch
(
filters
)
query
=
CourseSection
.
joins
(
:course
).
select
(
"course_sections.*"
)
filters
.
each
do
|
filter
,
value
|
case
filter
when
"crn"
query
=
from_crn
(
query
,
value
)
when
"course_id"
query
=
from_course_id
(
query
,
value
)
when
"course_number"
query
=
Course
.
from_course_number
(
query
,
value
)
when
"subject"
query
=
Course
.
from_subject
(
query
,
value
)
when
"title"
query
=
Course
.
from_title
(
query
,
value
)
end
end
query
end
end
schedules/app/models/instructor.rb
View file @
d2dd1444
class
Instructor
<
ApplicationRecord
has_many
:course_sections
def
self
.
named
(
name
)
where
(
"
name LIKE ?"
,
"%
#{
name
}
%"
)
def
self
.
from_name
(
base_query
,
name
)
base_query
.
where
(
"instructors.
name LIKE ?"
,
"%
#{
name
}
%"
)
end
end
schedules/app/views/search/index.html.erb
View file @
d2dd1444
<%
if
@courses
.
any?
%>
<%=
render
partial:
'shared/course'
,
collection:
@courses
%>
<%
if
@results
.
any?
%>
<%
@results
.
each
do
|
result
|
%>
<%
if
result
.
type
==
:course
%>
<%=
render
partial:
'shared/course'
,
object:
result
.
data
%>
<%
elsif
result
.
type
==
:instructor
%>
<%=
render
partial:
'shared/instructor'
,
object:
result
.
data
%>
<%
end
%>
<%
end
%>
<%
else
%>
<h1>
Sorry, we couldn't find anything matching your search.
</h1>
<p>
Please try again!
</p>
<h1>
Sorry, we couldn't find anything matching your search.
</h1>
<p>
Please try again!
</p>
<%
end
%>
schedules/app/views/shared/_course.html.erb
View file @
d2dd1444
<%
expanded
=
false
unless
defined?
expanded
%>
<div
class=
"card"
id=
"course-
<%=
course
.
id
%>
"
onclick=
"toggleSections(this)"
>
<div
class=
"card-body"
>
<div
style=
"display: flex; justify-content: space-between"
>
<h3
id=
"title"
>
<%=
"
#{
course
.
subject
}
#{
course
.
course_number
}
"
%>
</h3>
<div
style=
"display: flex; flex-direction: column; justify-content: center;"
>
<div
style=
"display: flex"
>
<h5><em>
<%=
course
.
title
%>
</em>
.
<%=
course
.
credits
%>
credits.
</h5>
<h4
id=
"add-course-btn"
onclick=
"addCourse(event, '
<%=
course
.
id
%>
');"
>
<div
class=
"card-header"
>
<h4
class=
"title"
>
<%=
"
#{
course
.
subject
}
#{
course
.
course_number
}
"
%>
</h4>
<h5><em>
<%=
course
.
title
%>
</em>
.
<%=
course
.
credits
%>
credits.
</h5>
<h4
id=
"add-course-btn"
onclick=
"addCourse(event, '
<%=
course
.
id
%>
');"
>
<i
class=
"fas fa-plus"
style=
"color: green"
></i>
</h4>
</div>
</div>
</div>
<div
style=
"clear: both"
>
</div>
<p
class=
"description"
>
<%=
course
.
description
%>
</p>
<%
unless
course
.
prereqs
.
nil?
||
course
.
prereqs
.
empty?
%>
<%
first
,
rest
=
course
.
prereqs
.
split
(
':'
)
%>
<%
prereqs
,
note
=
rest
.
split
(
'.'
)
%>
<p><strong>
<%=
first
%>
:
</strong>
<%=
prereqs
%>
<sub>
<%=
note
%>
</sub></p>
<%
end
%>
<div
class=
"d-block"
style=
"text-align: center"
>
<p
style=
"margin-bottom:-4px; font-size: 10px;"
>
Expand
</p>
<i
class=
"fas fa-chevron-down"
></i>
</div>
</h4>
</div>
<div
class=
"card-body"
>
<div
class=
"attr-list"
>
<div
class=
"attr"
><i
class=
"fa fa-book"
></i>
3 credits
</div>
<div
class=
"attr"
><i
class=
"fa fa-bars"
></i>
3 sections
</div>
</div>
<p
class=
"description"
>
<%=
course
.
description
%>
</p>