mitchellh-rubyuw 0.7.2 → 0.99.0
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/.gitignore +3 -1
- data/{README.markdown → README.md} +6 -11
- data/Rakefile +12 -2
- data/VERSION +1 -1
- data/lib/rubyuw.rb +8 -0
- data/lib/rubyuw/base.rb +122 -0
- data/lib/rubyuw/connection.rb +145 -0
- data/lib/rubyuw/curriculum_enrollment.rb +75 -0
- data/lib/rubyuw/errors.rb +16 -0
- data/lib/rubyuw/sln.rb +108 -0
- data/test/live/curriculum_enrollment_test.rb +47 -0
- data/test/live/sln_test.rb +16 -26
- data/test/live/test_helper.rb +15 -2
- data/test/mocked/base_test.rb +171 -0
- data/test/mocked/connection_test.rb +178 -0
- data/test/mocked/curriculum_enrollment_test.rb +132 -61
- data/test/mocked/sln_test.rb +106 -142
- data/test/mocked/test_helper.rb +57 -1
- metadata +20 -29
- data/lib/myuw.rb +0 -67
- data/lib/myuw/curriculum_enrollment.rb +0 -110
- data/lib/myuw/errors.rb +0 -18
- data/lib/myuw/registration.rb +0 -98
- data/lib/myuw/schedule.rb +0 -74
- data/lib/myuw/session.rb +0 -91
- data/lib/myuw/sln.rb +0 -131
- data/rubyuw.gemspec +0 -102
- data/test/live/registration_test.rb +0 -46
- data/test/live/schedule_test.rb +0 -28
- data/test/live/session_test.rb +0 -34
- data/test/mocked/myuw_test.rb +0 -39
- data/test/mocked/registration_test.rb +0 -105
- data/test/mocked/schedule_test.rb +0 -132
- data/test/mocked/session_test.rb +0 -96
data/lib/myuw/schedule.rb
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
class MyUW
|
2
|
-
# = Synopsis
|
3
|
-
# The Schdule class is responsible for querying a
|
4
|
-
# student's currently registered schedule.
|
5
|
-
class Schedule
|
6
|
-
attr_accessor :myuw
|
7
|
-
attr_reader :courses
|
8
|
-
|
9
|
-
def initialize(myuw=nil)
|
10
|
-
@myuw ||= myuw
|
11
|
-
@courses = nil
|
12
|
-
end
|
13
|
-
|
14
|
-
def credits(force=false)
|
15
|
-
return @credits if @credits && !force
|
16
|
-
schedule(true)
|
17
|
-
@credits
|
18
|
-
end
|
19
|
-
|
20
|
-
def schedule(force=false)
|
21
|
-
raise NotLoggedInError.new("User must be logged in to fetch SLN data") unless @myuw.logged_in?
|
22
|
-
|
23
|
-
# Return the schedule if we precached it at some point
|
24
|
-
# unless we're forcing it
|
25
|
-
return @courses if @courses && !force
|
26
|
-
|
27
|
-
# Grab the schedule page
|
28
|
-
schedule_page = @myuw.browser.get("https://sdb.admin.washington.edu/students/uwnetid/schedule.asp")
|
29
|
-
|
30
|
-
# Grab the info out of it
|
31
|
-
get_courses schedule_page
|
32
|
-
|
33
|
-
# Grab the credits
|
34
|
-
get_credits schedule_page
|
35
|
-
|
36
|
-
# Return the courses
|
37
|
-
@courses
|
38
|
-
end
|
39
|
-
|
40
|
-
def get_courses(schedule)
|
41
|
-
courses = schedule.search("//table[@border=1 and @cellpadding=3]//tr[@bgcolor='#d0d0d0'][2]/following-sibling::*//td[@align='center']/parent::*")
|
42
|
-
raise InvalidPageError.new(schedule, "No courses were found") if courses.empty?
|
43
|
-
|
44
|
-
data_order = [:sln, :course, :type, :credits, :title, :days, :time, :location, :instructor]
|
45
|
-
|
46
|
-
@courses = []
|
47
|
-
courses.each do |course|
|
48
|
-
data = course.search('td')
|
49
|
-
raise InvalidPageError(schedule, "Something strange happening, found courses but no course data") if data.empty?
|
50
|
-
|
51
|
-
course_info = {}
|
52
|
-
i = 0
|
53
|
-
data.each do |datum|
|
54
|
-
break if data_order[i].nil?
|
55
|
-
|
56
|
-
course_info[data_order[i]] = datum.inner_text.strip.gsub("\302\240", "")
|
57
|
-
i += 1
|
58
|
-
end
|
59
|
-
|
60
|
-
@courses.push(course_info)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def get_credits(schedule)
|
65
|
-
credits = schedule.search("//table[@border=1 and @cellpadding=3]//tr[@bgcolor='#d0d0d0'][2]/following-sibling::*//td[@colspan=4]")
|
66
|
-
raise InvalidPageError.new(schedule, "No credit row was found") if credits.empty?
|
67
|
-
|
68
|
-
credits_text = credits[0].inner_text.strip.gsub("\302\240", "")
|
69
|
-
raise InvalidPageError.new(schedule, "Credit row in different syntax") unless credits_text =~ /^Total credits: (.+?)$/
|
70
|
-
|
71
|
-
@credits = $1.to_f
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
data/lib/myuw/session.rb
DELETED
@@ -1,91 +0,0 @@
|
|
1
|
-
class MyUW
|
2
|
-
# = Synopsis
|
3
|
-
# This class encapsulates a MyUW session. It handles
|
4
|
-
# the login/logout of the MyUW portal.
|
5
|
-
class Session
|
6
|
-
attr_accessor :myuw
|
7
|
-
attr_accessor :email
|
8
|
-
attr_accessor :password
|
9
|
-
|
10
|
-
def initialize(myuw=nil)
|
11
|
-
@myuw ||= myuw
|
12
|
-
|
13
|
-
@email = @password = nil
|
14
|
-
end
|
15
|
-
|
16
|
-
# Checks if a user is logged in. This method is actually
|
17
|
-
# pretty expensive in terms of time since it actually
|
18
|
-
# verifies by going to the MyUW homepage and seeing if the
|
19
|
-
# dashboard loads.
|
20
|
-
def logged_in?
|
21
|
-
home = get_bare_login_page
|
22
|
-
relay_page = home.form_with(:name => 'f').submit()
|
23
|
-
!relay_page.search("//div[@class='main_search']").empty?
|
24
|
-
end
|
25
|
-
|
26
|
-
# Logs a user in using the given email and password
|
27
|
-
def login
|
28
|
-
# Make sure that email and password are filled out or this is
|
29
|
-
# useless
|
30
|
-
if @email.nil? || @password.nil?
|
31
|
-
raise ArgumentError.new("Email and password weren't specified for MyUW session")
|
32
|
-
end
|
33
|
-
|
34
|
-
# Log out first
|
35
|
-
logout
|
36
|
-
|
37
|
-
# Go to the MyUW page and get to the login form
|
38
|
-
login_page = get_login_page
|
39
|
-
login_form = login_page.form_with(:name => 'query')
|
40
|
-
raise InvalidPageError.new(login_page, "Login form was not found on the MyUW page") if login_form.nil?
|
41
|
-
login_form.user = @email
|
42
|
-
login_form.pass = @password
|
43
|
-
relay_page = login_form.submit()
|
44
|
-
|
45
|
-
# Check if the login failed
|
46
|
-
unless relay_page.form_with(:name => 'query').nil?
|
47
|
-
return false
|
48
|
-
end
|
49
|
-
|
50
|
-
# Follow the relay
|
51
|
-
follow_relay_page(relay_page)
|
52
|
-
true
|
53
|
-
end
|
54
|
-
|
55
|
-
# Log out of the MyUW site
|
56
|
-
def logout
|
57
|
-
@myuw.browser.cookie_jar.clear!
|
58
|
-
end
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
# Gets the myuw.washington.edu homepage. It raises an error
|
63
|
-
# if it cannot find the login button (meaning something
|
64
|
-
# weird is going on)
|
65
|
-
def get_bare_login_page
|
66
|
-
home = @myuw.browser.get("http://myuw.washington.edu")
|
67
|
-
|
68
|
-
# Click the login button to get to the forum
|
69
|
-
entry_button = home.search("//input[@type='submit' and @value='Log in with your UW NetID']")
|
70
|
-
raise InvalidPageError.new(home, "Failed to find the log in button") if entry_button.empty?
|
71
|
-
|
72
|
-
home
|
73
|
-
end
|
74
|
-
|
75
|
-
# Gets the actual login page by pressing the "Log In"
|
76
|
-
# button and following the relay
|
77
|
-
def get_login_page
|
78
|
-
home = get_bare_login_page
|
79
|
-
entry_form = home.form_with(:name => 'f')
|
80
|
-
relay_page = entry_form.submit()
|
81
|
-
|
82
|
-
return follow_relay_page(relay_page)
|
83
|
-
end
|
84
|
-
|
85
|
-
# Follows a relay form
|
86
|
-
def follow_relay_page(relay_page)
|
87
|
-
relay_form = relay_page.form_with(:name => 'relay')
|
88
|
-
relay_form.submit()
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
data/lib/myuw/sln.rb
DELETED
@@ -1,131 +0,0 @@
|
|
1
|
-
class MyUW
|
2
|
-
# = Synopsis
|
3
|
-
# Gets information regarding a specific SLN, returning
|
4
|
-
# information in a SLN object.
|
5
|
-
class SLNInfo
|
6
|
-
attr_reader :term
|
7
|
-
attr_reader :sln
|
8
|
-
attr_accessor :myuw
|
9
|
-
attr_reader :data
|
10
|
-
|
11
|
-
def initialize(myuw)
|
12
|
-
@myuw = myuw
|
13
|
-
@term = nil
|
14
|
-
@data = nil
|
15
|
-
@sln = nil
|
16
|
-
end
|
17
|
-
|
18
|
-
# Custom setter for SLN to reset data
|
19
|
-
def sln=(value)
|
20
|
-
@sln = value
|
21
|
-
@data = nil
|
22
|
-
end
|
23
|
-
|
24
|
-
# Custom setter for term to reset data
|
25
|
-
def term=(value)
|
26
|
-
@term = value
|
27
|
-
@data = nil
|
28
|
-
end
|
29
|
-
|
30
|
-
# Fetches the information for the given SLN.
|
31
|
-
def fetch_data
|
32
|
-
raise ArgumentError.new("SLN not set.") if @sln.nil?
|
33
|
-
raise ArgumentError.new("Term not set.") if @term.nil?
|
34
|
-
raise NotLoggedInError.new("User must be logged in to fetch SLN data") unless @myuw.logged_in?
|
35
|
-
|
36
|
-
# Request the actual page
|
37
|
-
page = get_sln_page
|
38
|
-
|
39
|
-
# Check if the time is not available
|
40
|
-
check_time_schedule_closed page
|
41
|
-
|
42
|
-
# Get the actual course information
|
43
|
-
get_course_info page
|
44
|
-
get_enrollment_info page
|
45
|
-
get_course_notes page
|
46
|
-
end
|
47
|
-
|
48
|
-
# The methods to extract various info out of the SLN. While
|
49
|
-
# I tend to try to avoid metaprogramming, this case seemed
|
50
|
-
# simple enough. Nothing tricky happening here!
|
51
|
-
[:course, :section, :type, :credits, :title, :current_enrollment,
|
52
|
-
:limit_enrollment, :room_capacity, :space_available, :status,
|
53
|
-
:notes].each do |info|
|
54
|
-
eval(<<-eomethod)
|
55
|
-
def #{info}
|
56
|
-
fetch_data if @data.nil?
|
57
|
-
@data[:#{info}]
|
58
|
-
end
|
59
|
-
eomethod
|
60
|
-
end
|
61
|
-
|
62
|
-
# Gets the SLN info page for the SLN and term, raising
|
63
|
-
# an exception if it is requested too soon
|
64
|
-
def get_sln_page
|
65
|
-
page = @myuw.browser.get("https://sdb.admin.washington.edu/timeschd/uwnetid/sln.asp?QTRYR=#{@term}&SLN=#{@sln}")
|
66
|
-
if page.uri.to_s == "http://www.washington.edu/students/timeschd/badrequest.html"
|
67
|
-
raise RequestSLNTooSoonError.new("Attempted to fetch SLN data too soon.")
|
68
|
-
elsif page.body.to_s =~ /SLN: #{@sln} does not exist./
|
69
|
-
raise InvalidSLNError.new("SLN does not exist.")
|
70
|
-
end
|
71
|
-
|
72
|
-
page
|
73
|
-
end
|
74
|
-
|
75
|
-
# Gets the basic SLN info (the top table in the
|
76
|
-
# current status page)
|
77
|
-
def get_course_info(page)
|
78
|
-
data_keys = [nil, :course, :section, :type, :credits, :title]
|
79
|
-
info_nodes = page.search("//table[@border=1 and @cellpadding=3]//tr[@rowspan=1]//td")
|
80
|
-
raise InvalidPageError.new(page, "Could not extract course info for SLN.") unless extract_info(data_keys, info_nodes)
|
81
|
-
end
|
82
|
-
|
83
|
-
# Gets the enrollment information for an SLN such
|
84
|
-
# as current enrollment, space available, etc.
|
85
|
-
def get_enrollment_info(page)
|
86
|
-
data_keys = [:current_enrollment, :limit_enrollment, :room_capacity, :space_available, :status]
|
87
|
-
info_nodes = page.search("//table[@border=1 and @cellpadding=3]//tr[count(td)=5]//td")
|
88
|
-
raise InvalidPageError.new(page, "Could not extract enrollment info for SLN.") unless extract_info(data_keys, info_nodes)
|
89
|
-
end
|
90
|
-
|
91
|
-
# Gets the notes for a course
|
92
|
-
def get_course_notes(page)
|
93
|
-
data_keys = [:notes]
|
94
|
-
info_nodes = page.search("//table[@border=1 and @cellpadding=3]//tr[count(td)=1]//preceding-sibling::tr[count(th)=1]//following-sibling::tr//td")
|
95
|
-
raise InvalidPageError.new(page, "Could not find course notes information.") unless extract_info(data_keys, info_nodes)
|
96
|
-
end
|
97
|
-
|
98
|
-
# Extracts course information from each node and
|
99
|
-
# stores it as the proper key in the @data variable
|
100
|
-
def extract_info(keys, nodes)
|
101
|
-
return false if nodes.empty?
|
102
|
-
|
103
|
-
nodes.each_with_index do |node, i|
|
104
|
-
# Make sure we have keys left still
|
105
|
-
if i < keys.length then
|
106
|
-
data_key = keys[i]
|
107
|
-
|
108
|
-
# If the key is nil, we skip this node
|
109
|
-
unless data_key.nil?
|
110
|
-
@data ||= {}
|
111
|
-
@data[data_key] = node.inner_text.strip.gsub("\302\240", "")
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
true
|
117
|
-
end
|
118
|
-
|
119
|
-
# Checks if the time schedule system is closed and if
|
120
|
-
# so, raises an exception
|
121
|
-
def check_time_schedule_closed(page)
|
122
|
-
raise TimeScheduleClosedError.new if page.uri.to_s == "http://www.washington.edu/students/timeschd/nots.html"
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
#
|
127
|
-
# Exceptions
|
128
|
-
class InvalidSLNError < StandardError; end
|
129
|
-
class RequestSLNTooSoonError < StandardError; end
|
130
|
-
class TimeScheduleClosedError < StandardError; end
|
131
|
-
end
|
data/rubyuw.gemspec
DELETED
@@ -1,102 +0,0 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
-
# -*- encoding: utf-8 -*-
|
5
|
-
|
6
|
-
Gem::Specification.new do |s|
|
7
|
-
s.name = %q{rubyuw}
|
8
|
-
s.version = "0.7.2"
|
9
|
-
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["Mitchell Hashimoto"]
|
12
|
-
s.date = %q{2009-09-09}
|
13
|
-
s.description = %q{TODO}
|
14
|
-
s.email = %q{mitchell.hashimoto@gmail.com}
|
15
|
-
s.extra_rdoc_files = [
|
16
|
-
"README.markdown"
|
17
|
-
]
|
18
|
-
s.files = [
|
19
|
-
".gitignore",
|
20
|
-
"README.markdown",
|
21
|
-
"Rakefile",
|
22
|
-
"VERSION",
|
23
|
-
"lib/myuw.rb",
|
24
|
-
"lib/myuw/curriculum_enrollment.rb",
|
25
|
-
"lib/myuw/errors.rb",
|
26
|
-
"lib/myuw/registration.rb",
|
27
|
-
"lib/myuw/schedule.rb",
|
28
|
-
"lib/myuw/session.rb",
|
29
|
-
"lib/myuw/sln.rb",
|
30
|
-
"rubyuw.gemspec",
|
31
|
-
"test/live/registration_test.rb",
|
32
|
-
"test/live/schedule_test.rb",
|
33
|
-
"test/live/session_test.rb",
|
34
|
-
"test/live/sln_test.rb",
|
35
|
-
"test/live/test_helper.rb",
|
36
|
-
"test/mocked/curriculum_enrollment_test.rb",
|
37
|
-
"test/mocked/fixture_pages/bad_request.html",
|
38
|
-
"test/mocked/fixture_pages/curric_courses.html",
|
39
|
-
"test/mocked/fixture_pages/curric_no_courses.html",
|
40
|
-
"test/mocked/fixture_pages/curric_no_curric_found.html",
|
41
|
-
"test/mocked/fixture_pages/logged_in_relay.html",
|
42
|
-
"test/mocked/fixture_pages/login.html",
|
43
|
-
"test/mocked/fixture_pages/no_form_login.html",
|
44
|
-
"test/mocked/fixture_pages/no_login_button.html",
|
45
|
-
"test/mocked/fixture_pages/not_logged_in_relay.html",
|
46
|
-
"test/mocked/fixture_pages/registration.html",
|
47
|
-
"test/mocked/fixture_pages/registration_error.html",
|
48
|
-
"test/mocked/fixture_pages/registration_error_no_rows.html",
|
49
|
-
"test/mocked/fixture_pages/registration_no_form.html",
|
50
|
-
"test/mocked/fixture_pages/registration_success.html",
|
51
|
-
"test/mocked/fixture_pages/registration_unknown.html",
|
52
|
-
"test/mocked/fixture_pages/schedule.html",
|
53
|
-
"test/mocked/fixture_pages/schedule_diff_credit_row.html",
|
54
|
-
"test/mocked/fixture_pages/schedule_empty.html",
|
55
|
-
"test/mocked/fixture_pages/schedule_no_credit_row.html",
|
56
|
-
"test/mocked/fixture_pages/sln_does_not_exist.html",
|
57
|
-
"test/mocked/fixture_pages/sln_no_course_info.html",
|
58
|
-
"test/mocked/fixture_pages/sln_no_course_notes.html",
|
59
|
-
"test/mocked/fixture_pages/sln_no_enrollment_info.html",
|
60
|
-
"test/mocked/fixture_pages/sln_status.html",
|
61
|
-
"test/mocked/fixture_pages/welcome.html",
|
62
|
-
"test/mocked/myuw_test.rb",
|
63
|
-
"test/mocked/registration_test.rb",
|
64
|
-
"test/mocked/schedule_test.rb",
|
65
|
-
"test/mocked/session_test.rb",
|
66
|
-
"test/mocked/sln_test.rb",
|
67
|
-
"test/mocked/test_helper.rb",
|
68
|
-
"test/password.rb.sample"
|
69
|
-
]
|
70
|
-
s.homepage = %q{http://github.com/mitchellh/rubyuw}
|
71
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
72
|
-
s.require_paths = ["lib"]
|
73
|
-
s.rubygems_version = %q{1.3.4}
|
74
|
-
s.summary = %q{Library which provides a ruby interface to the University of Washington student portal.}
|
75
|
-
s.test_files = [
|
76
|
-
"test/live/registration_test.rb",
|
77
|
-
"test/live/schedule_test.rb",
|
78
|
-
"test/live/session_test.rb",
|
79
|
-
"test/live/sln_test.rb",
|
80
|
-
"test/live/test_helper.rb",
|
81
|
-
"test/mocked/curriculum_enrollment_test.rb",
|
82
|
-
"test/mocked/myuw_test.rb",
|
83
|
-
"test/mocked/registration_test.rb",
|
84
|
-
"test/mocked/schedule_test.rb",
|
85
|
-
"test/mocked/session_test.rb",
|
86
|
-
"test/mocked/sln_test.rb",
|
87
|
-
"test/mocked/test_helper.rb"
|
88
|
-
]
|
89
|
-
|
90
|
-
if s.respond_to? :specification_version then
|
91
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
92
|
-
s.specification_version = 3
|
93
|
-
|
94
|
-
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
95
|
-
s.add_runtime_dependency(%q<tenderlove-mechanize>, [">= 0.9.2.20090428085858"])
|
96
|
-
else
|
97
|
-
s.add_dependency(%q<tenderlove-mechanize>, [">= 0.9.2.20090428085858"])
|
98
|
-
end
|
99
|
-
else
|
100
|
-
s.add_dependency(%q<tenderlove-mechanize>, [">= 0.9.2.20090428085858"])
|
101
|
-
end
|
102
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
if $0 != __FILE__
|
2
|
-
puts <<-WORDS
|
3
|
-
Note: Registration tests can only be run by directly
|
4
|
-
invoking the registration_test.rb file in the
|
5
|
-
test/live directory.
|
6
|
-
WORDS
|
7
|
-
end
|
8
|
-
|
9
|
-
if $0 == __FILE__
|
10
|
-
# Only run tests in this file if we're executing it
|
11
|
-
# on its own
|
12
|
-
require File.join(File.dirname(__FILE__), 'test_helper')
|
13
|
-
|
14
|
-
class LiveRegistrationTest < Test::Unit::TestCase
|
15
|
-
context "registering for a course" do
|
16
|
-
setup do
|
17
|
-
@myuw = MyUW.new
|
18
|
-
@session = @myuw.session
|
19
|
-
@session.email = MYUW_ID
|
20
|
-
@session.password = MYUW_PASSWORD
|
21
|
-
assert @session.login
|
22
|
-
|
23
|
-
@schedule = @myuw.schedule
|
24
|
-
@registration = @myuw.registration
|
25
|
-
|
26
|
-
@good_course = "17697"
|
27
|
-
@bad_courses = ["17778", "17779"]
|
28
|
-
end
|
29
|
-
|
30
|
-
should "successfully register for a course" do
|
31
|
-
assert !@schedule.schedule.empty?
|
32
|
-
@schedule.schedule.each { |course| assert_not_equal @good_course, course[:sln] }
|
33
|
-
|
34
|
-
assert @registration.register([@good_course])
|
35
|
-
|
36
|
-
@schedule.schedule(true)
|
37
|
-
assert !@schedule.schedule.select { |course| course[:sln].to_i == @good_course.to_i }.empty?
|
38
|
-
end
|
39
|
-
|
40
|
-
should "successfully read erroneous registration for multiple courses" do
|
41
|
-
assert !@registration.register(@bad_courses)
|
42
|
-
assert !@registration.errors["17779"].empty?
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|