rubyuw 0.99.2 → 0.99.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|