Commit ed38fd6c authored by Zac Wood's avatar Zac Wood

Now handles multiple semesters correctly

parent 3918e500
Pipeline #2987 failed with stage
in 2 minutes and 15 seconds
# Configures the application.
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
before_action :set_cookies, :set_cart
before_action :set_cookies, :set_cart, :set_semester
def set_cart
@cart = cookies[:ids].split(',').map do |crn|
......@@ -12,4 +12,12 @@ class ApplicationController < ActionController::Base
def set_cookies
cookies[:ids] = "" if cookies[:ids].nil?
end
def set_semester
@semester = if params.key?(:semester_id)
Semester.find_by(id: params[:semester_id])
else
Semester.find_by(season: 'Fall', year: '2018')
end
end
end
class SearchController < ApplicationController
def index
@courses = Course.where(subject: params[:q]).select do |course|
@courses = Course.where(subject: params[:q], semester: @semester).select do |course|
course.course_sections.count.positive?
end
end
def update
cookies[:ids] = params[:ids]
head :ok
end
end
......@@ -10,10 +10,4 @@ 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
end
# Contains logic belonging to the +CourseSection+ model.
#
# TODO: Add more docs
class CourseSection < ApplicationRecord
# Each +CourseSection+ belongs to a +Course+ and an +Instructor+.
belongs_to :course
......
require 'icalendar'
require 'time'
# Creates a iCal object given a list of CRNs
class Schedule
def initialize(crns)
@cal = Icalendar::Calendar.new
......
......@@ -3,12 +3,9 @@
# A +Semester+ is a simple model that consists of a +year+ and a +season+, e.g. "Fall 2018".
class Semester < ApplicationRecord
has_many :courses
has_many :closures
# Ensure necessary fields are present.
validates :year, presence: true
validates :season, presence: true
def courses
Course.where semester_id: id
end
end
# Registers all routes for the app.
Rails.application.routes.draw do
get 'search', to: 'search#index'
get 'search/update', to: 'search#update'
get 'search/update', to: 'search#update', as: 'update_cookie'
resources :instructors, only: [:index, :show]
......
......@@ -16,9 +16,9 @@ module PatriotWeb
})
end
def fetch_courses_in_subject(subject)
def fetch_courses_in_subject(semester_val, subject)
HTTParty.post('https://patriotweb.gmu.edu/pls/prod/bwckschd.p_get_crse_unsec',
body: "term_in=201870&sel_subj=dummy&sel_day=dummy&sel_schd=dummy&sel_insm=dummy&sel_camp=dummy&sel_levl=dummy&sel_sess=dummy&sel_instr=dummy&sel_ptrm=dummy&sel_attr=dummy&sel_subj=#{subject}&sel_crse=&sel_title=&sel_schd=%25&sel_from_cred=&sel_to_cred=&sel_camp=%25&sel_levl=%25&sel_ptrm=%25&sel_instr=%25&begin_hh=0&begin_mi=0&begin_ap=x&end_hh=0&end_mi=0&end_ap=x",
body: "term_in=#{semester_val}&sel_subj=dummy&sel_day=dummy&sel_schd=dummy&sel_insm=dummy&sel_camp=dummy&sel_levl=dummy&sel_sess=dummy&sel_instr=dummy&sel_ptrm=dummy&sel_attr=dummy&sel_subj=#{subject}&sel_crse=&sel_title=&sel_schd=%25&sel_from_cred=&sel_to_cred=&sel_camp=%25&sel_levl=%25&sel_ptrm=%25&sel_instr=%25&begin_hh=0&begin_mi=0&begin_ap=x&end_hh=0&end_mi=0&end_ap=x",
headers: {
'Content-Type' => 'application/x-www-form-urlencoded',
'charset' => 'utf-8'
......
......@@ -19,7 +19,6 @@ module PatriotWeb
def parse_semesters
response = @networker.fetch_page_containing_semester_data
document = Nokogiri::HTML(response) # parse the document from the HTTP response
get_semesters_from_option_values(document).compact
end
......@@ -33,8 +32,8 @@ module PatriotWeb
# Parses all courses belonging to a given subject
# @param subject [String]
def parse_courses_in_subject(subject)
response = @networker.fetch_courses_in_subject(subject)
def parse_courses_in_subject(semester, subject)
response = @networker.fetch_courses_in_subject(semester, subject)
document = Nokogiri::HTML(response)
get_courses(document, subject)
end
......@@ -46,9 +45,10 @@ module PatriotWeb
# @param document [Nokogiri::HTML::Document]
def get_semesters_from_option_values(document)
document.css('option').map do |opt| # for each option value
if opt.attr('value').start_with? '20' # ensure it is a semester value
opt.attr('value') # return the value
end
next unless opt.attr('value').start_with? '20' # ensure it is a semester value
text = opt.text.gsub(/ *\(.*\).*/, "").gsub(/ +/, " ")
season, year = text.split
{ value: opt.attr('value'), season: season, year: year }
end
end
......@@ -57,9 +57,7 @@ module PatriotWeb
# @param document [Nokogiri::HTML::Document]
def get_subject_codes_from_option_values(document)
document.xpath('//*[@id="subj_id"]/option').map do |opt| # for each option value under "subj_id"
if opt.attr('value').strip.alpha? # if the value is alphanumeric
opt.attr('value') # return the value
end
opt.attr('value') if opt.attr('value').strip.alpha? # return the value if the value is alphanumeric
end
end
......@@ -69,10 +67,6 @@ module PatriotWeb
def get_courses(document, _subject)
table = document.css('html body div.pagebodydiv table.datadisplaytable')
rows = table.css('tr')
# rows[100..110].each_with_index do |row, i|
# puts i
# puts row
# end
data_from rows
end
......@@ -95,7 +89,7 @@ module PatriotWeb
data[:section] = title_elements[3].strip
details = rows[i + 2].css('td table tr td')
unless !details.empty?
if details.empty?
# puts "#{full_name.join(' ')} is fake news"
i += 1
next
......
......@@ -23,23 +23,27 @@ def parse_courses(subjects)
end
def load_courses(courses, semester)
courses.each do |course|
Course.create!(subject: course[:subject],
title: course[:title],
course_number: course[:course_number],
credits: course[:credits],
description: course[:description],
semester: semester)
insert_hashes = courses.map do |course|
{
subject: course[:subject],
title: course[:title],
course_number: course[:course_number],
credits: course[:credits],
description: course[:description],
semester: semester
}
end
Course.create!(insert_hashes)
end
def parse_sections(subjects)
def parse_sections(semester, subjects)
parser = PatriotWeb::Parser.new
sections_in = {}
threads = subjects.map do |subject|
Thread.new do
sections_in[subject] = parser.parse_courses_in_subject(subject)
sections_in[subject] = parser.parse_courses_in_subject(semester, subject)
end
end
......@@ -91,39 +95,44 @@ def wipe_db
Semester.delete_all
end
def load_closures(semester)
def load_closures
# create closures for the days there will be no classes
# see: https://registrar.gmu.edu/calendars/fall-2018/
Closure.create! date: Date.new(2018, 9, 3), semester: semester
Closure.create! date: Date.new(2018, 10, 8), semester: semester
(21..25).each { |n| Closure.create! date: Date.new(2018, 11, n), semester: semester }
(10..19).each { |n| Closure.create! date: Date.new(2018, 12, n), semester: semester }
fall2018 = Semester.find_by(season: 'Fall', year: '2018')
Closure.create! date: Date.new(2018, 9, 3), semester: fall2018
Closure.create! date: Date.new(2018, 10, 8), semester: fall2018
(21..25).each { |n| Closure.create! date: Date.new(2018, 11, n), semester: fall2018 }
(10..19).each { |n| Closure.create! date: Date.new(2018, 12, n), semester: fall2018 }
end
def main
wipe_db
parser = PatriotWeb::Parser.new
semesters = [parser.parse_semesters.first] # expand to include however many semesters you want
courses = nil
puts "Parsing subjects..."
semester = parser.parse_semesters.first
subjects = parser.parse_subjects(semester)
semesters.each do |semester|
puts "#{semester[:season]} #{semester[:year]}"
db_semester = Semester.create!(season: semester[:season], year: semester[:year])
puts "Parsing courses from catalog.gmu.edu..."
courses = parse_courses(subjects)
puts "\tParsing subjects..."
subjects = parser.parse_subjects(semester[:value])
db_semester = Semester.create! season: 'Fall', year: 2018
puts "\tParsing courses from catalog.gmu.edu..."
courses = parse_courses(subjects) if courses.nil?
puts "Loading courses..."
load_courses(courses, db_semester)
puts "\tLoading courses..."
load_courses(courses, db_semester)
puts "Parsing sections from Patriot Web..."
sections_in = parse_sections(subjects)
puts "\tParsing sections from Patriot Web..."
sections_in = parse_sections(semester[:value], subjects)
puts "Loading sections..."
load_sections(sections_in, db_semester)
puts "\tLoading sections..."
load_sections(sections_in, db_semester)
end
load_closures(db_semester)
load_closures
end
main
require 'test_helper'
class SearchControllerTest < ActionDispatch::IntegrationTest
# test "should get index" do
# get search_index_url
# assert_response :success
# end
test "should get index" do
get search_url
assert_response :success
end
test "should update cookie" do
get update_cookie_url crns: '71926,71924'
assert_response :success
end
end
......@@ -36,5 +36,31 @@ cs211001:
start_time: 2:30 pm
end_time: 3:00 pm
location: ENGR 200
course: cs211
course: cs211spring
instructor: otten
cs112001spring:
name: CS 112 001
crn: 70192
title: Introduction to Computing
start_date: 2018-05-21
end_date: 2018-06-04
days: MWF
start_time: 12:00 pm
end_time: 1:00 pm
location: Innovation Hall 204
course: cs112spring
instructor: kinga
acct110001:
name: ACCT 110 001
crn: 71926
title: Business
start_date: 2018-05-21
end_date: 2018-06-04
days: MWF
start_time: 12:00 pm
end_time: 1:00 pm
location: Innovation Hall 204
course: acct110spring
instructor: business_man
......@@ -10,12 +10,17 @@ cs211:
course_number: 211
semester: fall2018
acct110:
acct110spring:
subject: ACCT
course_number: 110
semester: spring2018
cs110:
cs110spring:
subject: CS
course_number: 110
semester: spring2018
cs112spring:
subject: CS
course_number: 112
semester: spring2018
......@@ -8,3 +8,6 @@ otten:
kinga:
name: Kinga
business_man:
name: Bob
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment