rubyuw 0.99.2 → 0.99.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +2 -2
- data/VERSION +1 -1
- data/lib/rubyuw/curriculum.rb +192 -0
- data/lib/rubyuw.rb +1 -0
- data/rubyuw.gemspec +9 -2
- data/test/live/curriculum_test.rb +103 -0
- data/test/mocked/curriculum_test.rb +256 -0
- data/test/mocked/fixture_pages/quarter_time_schedule.html +711 -0
- data/test/mocked/fixture_pages/time_schedule_chem.html +1896 -0
- metadata +9 -2
data/README.md
CHANGED
@@ -52,9 +52,9 @@ The following is a quick and simple example:
|
|
52
52
|
|
53
53
|
## Documentation
|
54
54
|
|
55
|
-
You may view all documentation [at the following URL](http://mitchellh.github.com/
|
55
|
+
You may view all documentation [at the following URL](http://mitchellh.github.com/rubyuw/):
|
56
56
|
|
57
|
-
http://mitchellh.github.com/
|
57
|
+
[http://mitchellh.github.com/rubyuw/](http://mitchellh.github.com/rubyuw/)
|
58
58
|
|
59
59
|
## Testing
|
60
60
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.99.
|
1
|
+
0.99.3
|
@@ -0,0 +1,192 @@
|
|
1
|
+
module RubyUW
|
2
|
+
# RubyUW::Curriculum is used to gather a list of courses offered by
|
3
|
+
# a specific curriculum within the University of Washington. This
|
4
|
+
# is opposite of {RubyUW::SLN} which pulls data for a single SLN.
|
5
|
+
# With RubyUW::Curriculum, you can answer the following sorts of
|
6
|
+
# questions:
|
7
|
+
#
|
8
|
+
# * What courses are offered within the "Chemistry" curriculum?
|
9
|
+
# * What courses are offered within the "BIO A" curriculum?
|
10
|
+
# * What courses are offered for "CHEM 142?" This would return
|
11
|
+
# all the lectures, labs, and quiz sections associated with a
|
12
|
+
# specific course.
|
13
|
+
#
|
14
|
+
# <b>Does _not_ require authentication with {RubyUW::Base} for use!</b>
|
15
|
+
class Curriculum
|
16
|
+
class <<self
|
17
|
+
# Find all courses within a specific curriculum or course.
|
18
|
+
# The class itself determines, based on the format of the
|
19
|
+
# curriculum parameter, whether the user intends to search
|
20
|
+
# for an entire curriculum or specific course. This search
|
21
|
+
# can be verified in the resulting hash's <pre>:type</pre>
|
22
|
+
# value.
|
23
|
+
#
|
24
|
+
# = Examples:
|
25
|
+
#
|
26
|
+
# RubyUW::Curriculum.find("Chemistry", "WIN2010")
|
27
|
+
#
|
28
|
+
# RubyUW::Curriculum.find("BIO A", "WIN2010")
|
29
|
+
#
|
30
|
+
# RubyUW::Curriculum.find("CHEM 142", "WIN2010")
|
31
|
+
#
|
32
|
+
# = Return Format
|
33
|
+
#
|
34
|
+
# The method returns a hash which depending on your search is
|
35
|
+
# returned in one of two formats. Specific documentation for this
|
36
|
+
# will come soon. For now I recommend simply inspecting the hash
|
37
|
+
# for yourself, as it is fairly simple.
|
38
|
+
#
|
39
|
+
# @param [String] curriculum The curriculum or course to search
|
40
|
+
# @param [String] term The term (quarter) to search in.
|
41
|
+
# @return [Hash]
|
42
|
+
def find(curriculum, term)
|
43
|
+
curriculum = curriculum.strip # So we don't modify the original variable
|
44
|
+
type = !!(curriculum =~ /^(.+?)(\d+)$/) ? :course : :curriculum
|
45
|
+
send("find_#{type}", curriculum, term)
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def find_curriculum(curriculum, term)
|
51
|
+
page = Base.connection.get("http://www.washington.edu/students/timeschd/#{term}/")
|
52
|
+
raise Errors::QuarterDoesNotExistError.new if !quarter_exists?(page)
|
53
|
+
|
54
|
+
curriculum_page = extract_curriculum_url(curriculum, page)
|
55
|
+
raise Errors::CurriculumDoesNotExistError.new if curriculum_page.nil?
|
56
|
+
|
57
|
+
curriculum_page = Base.connection.get("http://www.washington.edu/students/timeschd/#{term}/#{curriculum_page}")
|
58
|
+
raise Errors::CurriculumDoesNotExistError.new if !curriculum_exists?(page)
|
59
|
+
|
60
|
+
return {
|
61
|
+
:type => :curriculum,
|
62
|
+
:courses => extract_courses(curriculum_page)
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def find_course(raw_curriculum, term)
|
67
|
+
curriculum, course = parse_curriculum(raw_curriculum)
|
68
|
+
curriculum_data = find_curriculum(curriculum, term)
|
69
|
+
courses = find_course_in_data(course, curriculum_data)
|
70
|
+
raise Errors::CourseDoesNotExistError.new(curriculum, course, raw_curriculum) if courses.nil?
|
71
|
+
|
72
|
+
return {
|
73
|
+
:type => :course,
|
74
|
+
:courses => courses
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def find_course_in_data(course, data)
|
79
|
+
data[:courses].each do |course_data, listing|
|
80
|
+
next unless course_data[:name] =~ /#{course}$/
|
81
|
+
|
82
|
+
return listing
|
83
|
+
end
|
84
|
+
|
85
|
+
return nil
|
86
|
+
end
|
87
|
+
|
88
|
+
def extract_courses(page)
|
89
|
+
nodes = page.search("//table[@bgcolor=\"#d3d3d3\"][1]//following-sibling::table")
|
90
|
+
|
91
|
+
courses = {}
|
92
|
+
current_course = []
|
93
|
+
|
94
|
+
nodes.each do |node|
|
95
|
+
if node[:bgcolor] == "#99ccff"
|
96
|
+
# If its a course header, start putting course listings under that
|
97
|
+
# course header
|
98
|
+
current_course = []
|
99
|
+
courses[extract_course_header(node)] = current_course
|
100
|
+
else
|
101
|
+
# Otherwise we just extract the course listing and stuff it under
|
102
|
+
# the the current course
|
103
|
+
current_course.push(extract_course_listing(node))
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
courses
|
108
|
+
end
|
109
|
+
|
110
|
+
def extract_course_header(node)
|
111
|
+
Base.extract(node, ".//td[@width=\"50%\"]//a", [:name, :title])
|
112
|
+
end
|
113
|
+
|
114
|
+
def extract_course_listing(node)
|
115
|
+
pre_node = node.search(".//pre")
|
116
|
+
pre_text = pre_node.inner_text.gsub("\302\240", "").gsub("\n", "").strip.chomp
|
117
|
+
|
118
|
+
# Ugly regex to match the <pre> text to extract the proper information for the
|
119
|
+
# course.
|
120
|
+
raise Errors::CourseListingMatchError.new(pre_text) unless pre_text =~ /^((IS\s+)|(Restr\s+))?>?([0-9]+)\s([A-Z]{1,2})\s(.+?)\s(.+)/i #\s(([A-Z]+)\s(.+?))\s(([A-Z]+)\s([0-9]+))/i
|
121
|
+
|
122
|
+
{
|
123
|
+
:sln => $4.to_s,
|
124
|
+
:section => $5.to_s,
|
125
|
+
:type => ($6.to_i > 0) ? "LC" : $3.to_s
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
def quarter_exists?(page)
|
130
|
+
# Time Schedule - No Courses Offered
|
131
|
+
return !(page.body.to_s =~ /Time Schedule - No Courses Offered/i)
|
132
|
+
end
|
133
|
+
|
134
|
+
# For now, unless MyUW changes, these are equivalent
|
135
|
+
alias :curriculum_exists? :quarter_exists?
|
136
|
+
|
137
|
+
def extract_curriculum_url(curriculum, page)
|
138
|
+
links = page.search("a")
|
139
|
+
|
140
|
+
links.each do |link|
|
141
|
+
text = link.inner_text.gsub("\302\240", " ").to_s
|
142
|
+
|
143
|
+
return link[:href].to_s if text =~ /^#{curriculum} \((.+?)\)$/i ||
|
144
|
+
text =~ /^(.+?) \(#{curriculum}\)$/i
|
145
|
+
end
|
146
|
+
|
147
|
+
return nil
|
148
|
+
end
|
149
|
+
|
150
|
+
def parse_curriculum(curriculum)
|
151
|
+
return nil unless curriculum =~ /^(.+?)\s+(\d+)$/
|
152
|
+
return $1, $2
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
module Errors
|
158
|
+
# An error indicating that a quarter does not exist in the time schedule
|
159
|
+
class QuarterDoesNotExistError < Exception; end
|
160
|
+
|
161
|
+
# An error indicating that the curriculum requested does not exist
|
162
|
+
class CurriculumDoesNotExistError < Exception; end
|
163
|
+
|
164
|
+
# An error indicating that the course listing didn't match the format
|
165
|
+
class CourseListingMatchError < Exception
|
166
|
+
attr_reader :listing_text
|
167
|
+
|
168
|
+
def initialize(listing_text, message = nil)
|
169
|
+
@listing_text = listing_text
|
170
|
+
|
171
|
+
message = "Failed to match: #{@listing_text}" if message.nil?
|
172
|
+
super(message)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# An error indicating that a course does not exist
|
177
|
+
class CourseDoesNotExistError < Exception
|
178
|
+
attr_reader :curriculum
|
179
|
+
attr_reader :course
|
180
|
+
attr_reader :original
|
181
|
+
|
182
|
+
def initialize(curriculum, course, original, message = nil)
|
183
|
+
@curriculum = curriculum
|
184
|
+
@course = course
|
185
|
+
@original = original
|
186
|
+
|
187
|
+
message = "Course does not exist: (#{curriculum}, #{course}) from \"#{original}\""
|
188
|
+
super(message)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
data/lib/rubyuw.rb
CHANGED
data/rubyuw.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rubyuw}
|
8
|
-
s.version = "0.99.
|
8
|
+
s.version = "0.99.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Mitchell Hashimoto"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2010-01-17}
|
13
13
|
s.description = %q{Library which provides a ruby interface to the University of Washington student portal.}
|
14
14
|
s.email = %q{mitchell.hashimoto@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -23,17 +23,20 @@ Gem::Specification.new do |s|
|
|
23
23
|
"lib/rubyuw.rb",
|
24
24
|
"lib/rubyuw/base.rb",
|
25
25
|
"lib/rubyuw/connection.rb",
|
26
|
+
"lib/rubyuw/curriculum.rb",
|
26
27
|
"lib/rubyuw/curriculum_enrollment.rb",
|
27
28
|
"lib/rubyuw/errors.rb",
|
28
29
|
"lib/rubyuw/schedule.rb",
|
29
30
|
"lib/rubyuw/sln.rb",
|
30
31
|
"rubyuw.gemspec",
|
31
32
|
"test/live/curriculum_enrollment_test.rb",
|
33
|
+
"test/live/curriculum_test.rb",
|
32
34
|
"test/live/sln_test.rb",
|
33
35
|
"test/live/test_helper.rb",
|
34
36
|
"test/mocked/base_test.rb",
|
35
37
|
"test/mocked/connection_test.rb",
|
36
38
|
"test/mocked/curriculum_enrollment_test.rb",
|
39
|
+
"test/mocked/curriculum_test.rb",
|
37
40
|
"test/mocked/fixture_pages/bad_request.html",
|
38
41
|
"test/mocked/fixture_pages/curric_courses.html",
|
39
42
|
"test/mocked/fixture_pages/curric_no_courses.html",
|
@@ -43,6 +46,7 @@ Gem::Specification.new do |s|
|
|
43
46
|
"test/mocked/fixture_pages/no_form_login.html",
|
44
47
|
"test/mocked/fixture_pages/no_login_button.html",
|
45
48
|
"test/mocked/fixture_pages/not_logged_in_relay.html",
|
49
|
+
"test/mocked/fixture_pages/quarter_time_schedule.html",
|
46
50
|
"test/mocked/fixture_pages/registration.html",
|
47
51
|
"test/mocked/fixture_pages/registration_error.html",
|
48
52
|
"test/mocked/fixture_pages/registration_error_no_rows.html",
|
@@ -58,6 +62,7 @@ Gem::Specification.new do |s|
|
|
58
62
|
"test/mocked/fixture_pages/sln_no_course_notes.html",
|
59
63
|
"test/mocked/fixture_pages/sln_no_enrollment_info.html",
|
60
64
|
"test/mocked/fixture_pages/sln_status.html",
|
65
|
+
"test/mocked/fixture_pages/time_schedule_chem.html",
|
61
66
|
"test/mocked/fixture_pages/welcome.html",
|
62
67
|
"test/mocked/schedule_test.rb",
|
63
68
|
"test/mocked/sln_test.rb",
|
@@ -71,11 +76,13 @@ Gem::Specification.new do |s|
|
|
71
76
|
s.summary = %q{Library which provides a ruby interface to the University of Washington student portal.}
|
72
77
|
s.test_files = [
|
73
78
|
"test/live/curriculum_enrollment_test.rb",
|
79
|
+
"test/live/curriculum_test.rb",
|
74
80
|
"test/live/sln_test.rb",
|
75
81
|
"test/live/test_helper.rb",
|
76
82
|
"test/mocked/base_test.rb",
|
77
83
|
"test/mocked/connection_test.rb",
|
78
84
|
"test/mocked/curriculum_enrollment_test.rb",
|
85
|
+
"test/mocked/curriculum_test.rb",
|
79
86
|
"test/mocked/schedule_test.rb",
|
80
87
|
"test/mocked/sln_test.rb",
|
81
88
|
"test/mocked/test_helper.rb"
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class LiveCurriculumTest < Test::Unit::TestCase
|
4
|
+
setup do
|
5
|
+
@term = "WIN2010"
|
6
|
+
end
|
7
|
+
|
8
|
+
context "live curriculum listing grabbing" do
|
9
|
+
context "checking parsing of curriculums" do
|
10
|
+
setup do
|
11
|
+
@valid_curriculums = ["CHEM", "Chemistry", " BIO A "]
|
12
|
+
@invalid_curriculums = ["Underwater Weaving", " Chmeistry"]
|
13
|
+
end
|
14
|
+
|
15
|
+
should "not raise any errors on valid curriculums" do
|
16
|
+
@valid_curriculums.each do |curriculum|
|
17
|
+
assert_nothing_raised {
|
18
|
+
result = RubyUW::Curriculum.find(curriculum, @term)
|
19
|
+
assert result.is_a?(Hash)
|
20
|
+
assert_equal :curriculum, result[:type]
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
should "raise CurriculumDoesNotExistError on invalid curriculums" do
|
26
|
+
@invalid_curriculums.each do |curriculum|
|
27
|
+
assert_raises(RubyUW::Errors::CurriculumDoesNotExistError) {
|
28
|
+
RubyUW::Curriculum.find(curriculum, @term)
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "proper grabbing of courses" do
|
35
|
+
setup do
|
36
|
+
@valid_curriculums = {
|
37
|
+
"BIO A" => {
|
38
|
+
"BIO A 201" => 10943..10949
|
39
|
+
}
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
should "extract proper amount of courses from a curriculum" do
|
44
|
+
@valid_curriculums.each do |curriculum, courses|
|
45
|
+
result = RubyUW::Curriculum.find(curriculum, @term)
|
46
|
+
assert result.is_a?(Hash)
|
47
|
+
|
48
|
+
courses.each do |expected_course, sln_range|
|
49
|
+
found = false
|
50
|
+
slns = []
|
51
|
+
|
52
|
+
result[:courses].each do |course, data|
|
53
|
+
next unless course[:name] == expected_course
|
54
|
+
|
55
|
+
data.each do |listing|
|
56
|
+
slns.push(listing[:sln].to_i)
|
57
|
+
end
|
58
|
+
|
59
|
+
found = true
|
60
|
+
break
|
61
|
+
end
|
62
|
+
|
63
|
+
assert found
|
64
|
+
assert !slns.empty?
|
65
|
+
|
66
|
+
sln_range.each { |sln|
|
67
|
+
assert slns.include?(sln)
|
68
|
+
slns -= [sln]
|
69
|
+
}
|
70
|
+
assert slns.empty?
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "live course listing grabbing" do
|
78
|
+
context "checking parsing of courses" do
|
79
|
+
setup do
|
80
|
+
@valid_courses = ["Chemistry 142", "CHEM 142", "CHEM 152", " BIO A 466"]
|
81
|
+
@invalid_courses = [" Chemistry 111 ", " CHEM 269"]
|
82
|
+
end
|
83
|
+
|
84
|
+
should "not raise any errors on valid courses" do
|
85
|
+
@valid_courses.each do |course|
|
86
|
+
assert_nothing_raised {
|
87
|
+
result = RubyUW::Curriculum.find(course, @term)
|
88
|
+
assert result.is_a?(Hash)
|
89
|
+
assert_equal :course, result[:type]
|
90
|
+
}
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
should "raise a CourseDoesNotExistError on invalid courses" do
|
95
|
+
@invalid_courses.each do |course|
|
96
|
+
assert_raises(RubyUW::Errors::CourseDoesNotExistError) {
|
97
|
+
RubyUW::Curriculum.find(course, @term)
|
98
|
+
}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,256 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class CurriculumTest < Test::Unit::TestCase
|
4
|
+
setup do
|
5
|
+
RubyUW::Curriculum.publicize_singleton_methods
|
6
|
+
end
|
7
|
+
|
8
|
+
teardown do
|
9
|
+
RubyUW::Curriculum.protect_singleton_methods
|
10
|
+
end
|
11
|
+
|
12
|
+
context "properly routing to find_curriculum or find_course" do
|
13
|
+
setup do
|
14
|
+
@curriculums = ["Chemistry", "BIO A", "CSE", " CHEM ", "CHEMISTRY"]
|
15
|
+
@courses = ["CHEM 142", "BIO A 240", " CHEM 142 ", " BIO A 240 "]
|
16
|
+
@term = "WIN2010"
|
17
|
+
end
|
18
|
+
|
19
|
+
should "recognize each of the curriculums" do
|
20
|
+
RubyUW::Curriculum.expects(:find_curriculum).times(@curriculums.length)
|
21
|
+
RubyUW::Curriculum.expects(:find_course).never
|
22
|
+
|
23
|
+
@curriculums.each do |curriculum|
|
24
|
+
RubyUW::Curriculum.find(curriculum, @term)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
should "recognize each of the courses" do
|
29
|
+
RubyUW::Curriculum.expects(:find_curriculum).never
|
30
|
+
RubyUW::Curriculum.expects(:find_course).times(@courses.length)
|
31
|
+
|
32
|
+
@courses.each do |course|
|
33
|
+
RubyUW::Curriculum.find(course, @term)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "finding course information" do
|
39
|
+
setup do
|
40
|
+
@term = "WIN2010"
|
41
|
+
end
|
42
|
+
|
43
|
+
context "parsing course string" do
|
44
|
+
should "parse the proper curriculum from the course string" do
|
45
|
+
curriculums = {
|
46
|
+
"Chemistry 142" => ["Chemistry", "142"],
|
47
|
+
"CHEM 142" => ["CHEM", "142"],
|
48
|
+
"Chemistry 142" => ["Chemistry", "142"],
|
49
|
+
"BIO A 240" => ["BIO A", "240"],
|
50
|
+
"I'm invalid =]" => [nil, nil],
|
51
|
+
"CHEM INVALID" => [nil, nil]
|
52
|
+
}
|
53
|
+
|
54
|
+
curriculums.each do |course, curric|
|
55
|
+
parsed = RubyUW::Curriculum.parse_curriculum(course)
|
56
|
+
parsed_curric = parsed[0] unless parsed.nil?
|
57
|
+
parsed_number = parsed.nil? ? nil : parsed[1]
|
58
|
+
assert_equal curric[0], parsed_curric
|
59
|
+
assert_equal curric[1], parsed_number
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "finding course within returned curriculum information" do
|
65
|
+
setup do
|
66
|
+
@page = RubyUW::Base.connection.get(path_to_html("time_schedule_chem"))
|
67
|
+
@data = {
|
68
|
+
:type => :curriculum,
|
69
|
+
:courses => RubyUW::Curriculum.extract_courses(@page)
|
70
|
+
}
|
71
|
+
|
72
|
+
# Below pulled directly from Chem WIN2010 time schedule
|
73
|
+
@listings_count = {
|
74
|
+
"299" => 2,
|
75
|
+
"321" => 3,
|
76
|
+
"520" => 1,
|
77
|
+
"317" => 8
|
78
|
+
}
|
79
|
+
|
80
|
+
@nonexistent_course = "111"
|
81
|
+
end
|
82
|
+
|
83
|
+
should "return proper number of listings for a course" do
|
84
|
+
@listings_count.each do |course, amount|
|
85
|
+
listing = RubyUW::Curriculum.find_course_in_data(course, @data)
|
86
|
+
|
87
|
+
assert_not_nil listing
|
88
|
+
assert_equal amount, listing.length
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
should "return nil for a course which doesn't exist" do
|
93
|
+
assert_nil RubyUW::Curriculum.find_course_in_data(@nonexistent_course, @data)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context "the actual find_course method" do
|
98
|
+
setup do
|
99
|
+
@raw_curriculum = "CHEM 142"
|
100
|
+
@curriculum, @course = RubyUW::Curriculum.parse_curriculum(@raw_curriculum)
|
101
|
+
@page = RubyUW::Base.connection.get(path_to_html("time_schedule_chem"))
|
102
|
+
@data = {
|
103
|
+
:type => :curriculum,
|
104
|
+
:courses => RubyUW::Curriculum.extract_courses(@page)
|
105
|
+
}
|
106
|
+
|
107
|
+
@listing = RubyUW::Curriculum.find_course_in_data(@course, @data)
|
108
|
+
|
109
|
+
RubyUW::Curriculum.expects(:parse_curriculum).once.returns([@curriculum, @course])
|
110
|
+
RubyUW::Curriculum.expects(:find_curriculum).once.returns(@data)
|
111
|
+
end
|
112
|
+
|
113
|
+
should "raise a CourseDoesNotExistError if it can't be parsed" do
|
114
|
+
RubyUW::Curriculum.expects(:find_course_in_data).once.returns(nil)
|
115
|
+
assert_raises(RubyUW::Errors::CourseDoesNotExistError) {
|
116
|
+
RubyUW::Curriculum.find_course(@raw_curriculum, @term)
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
should "return a hash on a valid course" do
|
121
|
+
RubyUW::Curriculum.expects(:find_course_in_data).once.returns(@listing)
|
122
|
+
assert_nothing_raised {
|
123
|
+
returned = RubyUW::Curriculum.find_course(@raw_curriculum, @term)
|
124
|
+
assert returned.is_a?(Hash)
|
125
|
+
assert_equal :course, returned[:type]
|
126
|
+
assert returned[:courses].is_a?(Array)
|
127
|
+
}
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context "finding curriculum information" do
|
133
|
+
context "with checking existence of a quarter" do
|
134
|
+
setup do
|
135
|
+
@page = mock('page')
|
136
|
+
end
|
137
|
+
|
138
|
+
should "return false if page contains the string 'Time Schedule - No Courses Offered'" do
|
139
|
+
@page.stubs(:body).returns("Time Schedule - No Courses Offered")
|
140
|
+
assert !RubyUW::Curriculum.quarter_exists?(@page)
|
141
|
+
end
|
142
|
+
|
143
|
+
should "return true if page does not contain that string" do
|
144
|
+
@page.stubs(:body).returns("HI!")
|
145
|
+
assert RubyUW::Curriculum.quarter_exists?(@page)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context "with checking existence of a curriculum" do
|
150
|
+
setup do
|
151
|
+
@page = mock('page')
|
152
|
+
end
|
153
|
+
|
154
|
+
should "return false if page contains the string 'Time Schedule - No Courses Offered'" do
|
155
|
+
@page.stubs(:body).returns("Time Schedule - No Courses Offered")
|
156
|
+
assert !RubyUW::Curriculum.curriculum_exists?(@page)
|
157
|
+
end
|
158
|
+
|
159
|
+
should "return true if page does not contain that string" do
|
160
|
+
@page.stubs(:body).returns("HI!")
|
161
|
+
assert RubyUW::Curriculum.curriculum_exists?(@page)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context "with finding the link for a curriculum" do
|
166
|
+
setup do
|
167
|
+
@page = RubyUW::Base.connection.get(path_to_html("quarter_time_schedule"))
|
168
|
+
|
169
|
+
@curriculums = {
|
170
|
+
"Chemistry" => "chem.html",
|
171
|
+
"Biology" => "biology.html",
|
172
|
+
"JAPAN" => "japanese.html",
|
173
|
+
"BIO A" => "bioanth.html",
|
174
|
+
"Underwater Basket" => nil,
|
175
|
+
"UBASK" => nil
|
176
|
+
}
|
177
|
+
end
|
178
|
+
|
179
|
+
should "extract proper URLs for curriculums" do
|
180
|
+
@curriculums.each do |curric, url|
|
181
|
+
assert_equal url, RubyUW::Curriculum.extract_curriculum_url(curric, @page)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context "extracting course information" do
|
188
|
+
setup do
|
189
|
+
@page = RubyUW::Base.connection.get(path_to_html("time_schedule_chem"))
|
190
|
+
end
|
191
|
+
|
192
|
+
context "extracting course headers" do
|
193
|
+
setup do
|
194
|
+
@header = {
|
195
|
+
:name => "CHEM 142",
|
196
|
+
:title => "GENERAL CHEMISTRY"
|
197
|
+
}
|
198
|
+
end
|
199
|
+
|
200
|
+
should "extract proper information out of a header" do
|
201
|
+
header_node = @page.search("//table[@bgcolor=\"#99ccff\"]")[0]
|
202
|
+
|
203
|
+
returned_header = RubyUW::Curriculum.extract_course_header(header_node)
|
204
|
+
@header.each do |k,v|
|
205
|
+
assert_equal v, returned_header[k]
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
should "return nil for an invalid header" do
|
210
|
+
header_node = @page.search("//table[@bgcolor=\"#d3d3d3\"]")[0]
|
211
|
+
|
212
|
+
assert_nothing_raised {
|
213
|
+
assert_nil RubyUW::Curriculum.extract_course_header(header_node)
|
214
|
+
}
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
context "extracting a course listing" do
|
219
|
+
setup do
|
220
|
+
@listing = {
|
221
|
+
:sln => "11610",
|
222
|
+
:section => "A",
|
223
|
+
:type => "LC"
|
224
|
+
}
|
225
|
+
end
|
226
|
+
|
227
|
+
should "return the proper information for the first listing" do
|
228
|
+
node = @page.search("//table[@bgcolor=\"#99ccff\"]//following-sibling::table")[0]
|
229
|
+
|
230
|
+
returned = RubyUW::Curriculum.extract_course_listing(node)
|
231
|
+
@listing.each do |k,v|
|
232
|
+
assert_equal v, returned[k]
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
should "raise an exception if the listing does not match" do
|
237
|
+
pre_node = mock('pre_node')
|
238
|
+
pre_node.expects(:inner_text).once.returns("I don't match")
|
239
|
+
node = mock('node')
|
240
|
+
node.expects(:search).once.returns(pre_node)
|
241
|
+
|
242
|
+
assert_raises(RubyUW::Errors::CourseListingMatchError) {
|
243
|
+
RubyUW::Curriculum.extract_course_listing(node)
|
244
|
+
}
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
context "extracting the list of courses" do
|
249
|
+
should "not raise any errors on a valid page" do
|
250
|
+
assert_nothing_raised {
|
251
|
+
RubyUW::Curriculum.extract_courses(@page)
|
252
|
+
}
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|