pcr-ruby 0.2.1 → 0.5
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 +2 -1
- data/README.md +84 -56
- data/lib/classes/course.rb +55 -66
- data/lib/classes/coursehistory.rb +70 -0
- data/lib/classes/review.rb +16 -0
- data/lib/classes/section.rb +28 -42
- data/lib/classes/string.rb +4 -4
- data/lib/pcr-ruby.rb +12 -12
- data/lib/pcr-ruby/version.rb +1 -1
- metadata +4 -4
- data/lib/classes/array.rb +0 -18
- data/lib/classes/instructor.rb +0 -116
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -15,76 +15,97 @@ gem install pcr-ruby
|
|
15
15
|
|
16
16
|
pcr-ruby follows the structure of the PCR API, with a few name changes to make object identities and roles clearer in your code. (Before using pcr-ruby, you should most definitely read the PCR API documentation, the link to which you should recieve upon being granted your API token.)
|
17
17
|
|
18
|
-
The PCR API essentially consists of four types of objects: 'Courses', 'Sections', 'Instructors', and 'Course Histories'. pcr-ruby aims to provide intuitive access to the data contained in these four object types while abstracting you and your user from background processing and unnecessary data. To that end, pcr-ruby (thus far) consists of
|
18
|
+
The PCR API essentially consists of four types of objects: 'Courses', 'Sections', 'Instructors', and 'Course Histories'. pcr-ruby aims to provide intuitive access to the data contained in these four object types while abstracting you and your user from background processing and unnecessary data. To that end, pcr-ruby (thus far) consists of the same four types of objects: 'Course Histories', 'Courses', 'Sections', and 'Instructors' (coming soon).
|
19
19
|
|
20
|
-
###
|
21
|
-
|
22
|
-
Course objects in the PCR API are essentially a group of that Course's Sections which were offered in a certain semester. Courses in pcr-ruby are different, and match up most directly with 'Course History' objects of the PCR API. It is my belief that when students think of a "course," they think of the entire history of the course and *not* the course offering for a specific semester. Therefore, pcr-ruby does not associate Courses with specific semesters -- rather, Courses exist across time and represent a single curriculum and course code.
|
20
|
+
### CourseHistories in pcr-ruby ###
|
21
|
+
Course Histories represent a course through time, and contain Course objects that represent the course offering in each semester.
|
23
22
|
|
24
|
-
To create a Course:
|
23
|
+
To create a Course History (the first step in getting PCR data):
|
25
24
|
```ruby
|
26
25
|
require 'pcr-ruby'
|
27
|
-
pcr = PCR.new(
|
28
|
-
|
26
|
+
pcr = PCR.new(API_TOKEN)
|
27
|
+
course_history = pcr.coursehistory(COURSE_CODE)
|
29
28
|
```
|
30
|
-
All other instance variables will auto-populate based on data from the PCR API.
|
31
29
|
|
32
|
-
|
33
|
-
* **course_code** -- a string in the format "DEPT-###", where "DEPT" is the four-letter department code and "###" is the three-digit course code.
|
34
|
-
* **sections** -- an array of Section objects for the Course across all time. Useful for calculating average ratings and other cumulative statistics.
|
35
|
-
* **id** -- the Course's PCR API id. (Integer)
|
36
|
-
* **name** -- the Course's plain-English name. (String)
|
37
|
-
* **path** -- the PCR API sub-path leading to the Course (or, more accurately, the Course History). For example, "/coursehistories/1794/". Or, more generally: "/coursehistories/[id]/". (String)
|
38
|
-
* **reviews** -- an array of Hashes that contain review data for each of the Course's sections.
|
30
|
+
All other attributes will auto-populate based on data from the PCR API.
|
39
31
|
|
40
|
-
|
41
|
-
*
|
42
|
-
*
|
32
|
+
Course Histories have the following attributes:
|
33
|
+
* **course_code** -- the course code entered by the user at initialization (String)
|
34
|
+
* **courses** -- an array of Courses associated with the Course History (Array)
|
35
|
+
* **id** -- the Course History's PCR API ID (String)
|
36
|
+
* **path** -- the Course History's PCR API URL path (String)
|
37
|
+
* **retrieved** -- the date the Course History was retreived (String)
|
38
|
+
* **valid** -- true/false whether or not the query was valid (String)
|
39
|
+
* **version** -- version of the PCR API hit (String)
|
43
40
|
|
44
|
-
|
41
|
+
The most useful way to think about a Course History is as a collection of Course objects.
|
45
42
|
|
46
|
-
|
43
|
+
Course Histories have the following instance methods:
|
44
|
+
* **average(metric)** -- returns the average value, across all Courses, of "metric" as a Float. "Metric" must be a recognized rating in the PCR API. (Currently the names of these ratings are not intuitive, so I may provide plain-English access to rating names in the future.)
|
45
|
+
* **recent(metric)** -- returns the most recent value of "metric" as a Float. (If there are multiple Sections offered in the most recent semester, the average across those Sections is returned.) "Metric" must be a recognized rating in the PCR API. (Currently the names of these ratings are not intuitive, so I may provide plain-English access to rating names in the future.)
|
47
46
|
|
48
|
-
|
47
|
+
### 'Courses' in pcr-ruby ###
|
48
|
+
|
49
|
+
Courses in the PCR API represent a collection of Sections of a course code during a given semester, and are treated similarly in pcr-ruby.
|
50
|
+
|
51
|
+
Courses are accessed from within their "parent" Course History:
|
49
52
|
```ruby
|
50
53
|
require 'pcr-ruby'
|
51
|
-
pcr = PCR.new(
|
52
|
-
|
54
|
+
pcr = PCR.new(API_TOKEN)
|
55
|
+
course_history = pcr.coursehistory(course_code)
|
56
|
+
courses = course_history.courses
|
57
|
+
earliest_course = courses.first
|
58
|
+
most_recent_course = courses.last
|
53
59
|
```
|
54
60
|
|
55
|
-
|
56
|
-
*
|
57
|
-
*
|
58
|
-
*
|
59
|
-
*
|
60
|
-
*
|
61
|
-
*
|
62
|
-
*
|
63
|
-
*
|
64
|
-
*
|
65
|
-
*
|
61
|
+
pcr-ruby's Course objects have the following instance variables:
|
62
|
+
* **aliases** -- an array of crosslistings (Array)
|
63
|
+
* **credits** -- the number of credits awarded for the course (String)
|
64
|
+
* **description** -- the PCR course description (String)
|
65
|
+
* **history** -- the PCR API path to the course's history (String)
|
66
|
+
* **id** -- the PCR API ID of the course (String)
|
67
|
+
* **name** -- the plain-English name of the course, taken from the most recent Semester (String)
|
68
|
+
* **path** -- the PCR API URL path of the course (String)
|
69
|
+
* **reviews** -- a hash that usually contains one key, the path to the course's reviews (String)
|
70
|
+
* **sections** -- an array of Sections associated with the course (Array)
|
71
|
+
* **semester** -- the semester in which the course was offered (String)
|
72
|
+
* **retrieved** -- the date/time the course was retrieved (String)
|
73
|
+
* **valid** -- true/false if valid/invalid request (String)
|
74
|
+
* **version** -- PCR API version (String)
|
75
|
+
|
76
|
+
The most useful way to think about Courses is as a collection of Section objects.
|
66
77
|
|
67
|
-
### '
|
78
|
+
### 'Sections' in pcr-ruby ###
|
68
79
|
|
69
|
-
|
80
|
+
In pcr-ruby, Sections are single offerings of a Course. Each Section is associated with a certain Course -- think of a Section as the individual classes under the umbrella of the Course. Sections in the PCR API are treated similarly.
|
70
81
|
|
71
|
-
To
|
82
|
+
To retrieve a Section:
|
72
83
|
```ruby
|
73
84
|
require 'pcr-ruby'
|
74
|
-
pcr = PCR.new(
|
75
|
-
|
85
|
+
pcr = PCR.new(API_TOKEN)
|
86
|
+
course_history = pcr.coursehistory(course_code)
|
87
|
+
recent_sections = course_history.courses.last.sections
|
88
|
+
single_recent_section = recent_sections.first
|
76
89
|
```
|
77
90
|
|
78
|
-
|
79
|
-
*
|
80
|
-
*
|
81
|
-
*
|
82
|
-
*
|
83
|
-
*
|
91
|
+
Sections have the following instance variables:
|
92
|
+
* **aliases** -- crosslistings of the Section (Array)
|
93
|
+
* **course** -- a hash containing info of the parent course (Hash)
|
94
|
+
* **group** --
|
95
|
+
* **id** -- the PCR API ID of the section (String)
|
96
|
+
* **instructors** -- a hash of info on each of the section's instructors (Hash)
|
97
|
+
* **meetingtimes** -- an array of hashes that contain info on each of the meeting times of the section (Array)
|
98
|
+
* **name** -- the plain-English name of the section (String)
|
99
|
+
* **path** -- the PCR API URL path of the section (String)
|
100
|
+
* **reviews** -- an array of hashes which each contain the review data for the section (usually only one review hash) (Array)
|
101
|
+
* **sectionnum** -- the number of the section (e.g. "001") (String)
|
102
|
+
* **retrieved** -- date/time retrieved (String)
|
103
|
+
* **valid** -- true/false if query valid/invalid (String)
|
104
|
+
* **version** -- PCR API version (String)
|
105
|
+
|
106
|
+
### 'Instructors' in pcr-ruby ###
|
84
107
|
|
85
|
-
|
86
|
-
* **average(metric)** -- returns the average value, across all Sections taught by Instructor, of "metric" as a Float. "Metric" must be a recognized rating in the PCR API. (Currently the names of these ratings are not intuitive, so I may provide plain-English access to rating names in the future.)
|
87
|
-
* **recent(metric)** -- returns the average value of "metric" for the most recent semester in which the Instructor taught as a float. (For example, if the professor taught 3 classes in the most recent semester, this would return the average of "metric" over the three classes.) "Metric" must be a recognized rating in the PCR API. (Currently the names of these ratings are not intuitive, so I may provide plain-English access to rating names in the future.)
|
108
|
+
(New version of instructors coming soon)
|
88
109
|
|
89
110
|
## pcr-ruby Usage Examples ##
|
90
111
|
|
@@ -97,8 +118,8 @@ Let's say we want to find the average course quality rating for Introduction to
|
|
97
118
|
require 'pcr-ruby'
|
98
119
|
course_code = "PSCI-150"
|
99
120
|
pcr = PCR.new(API_TOKEN)
|
100
|
-
|
101
|
-
puts
|
121
|
+
psci150 = pcr.coursehistory(course_code)
|
122
|
+
puts psci150.average("rCourseQuality") #=> 3.041
|
102
123
|
```
|
103
124
|
|
104
125
|
Or, even more briefly:
|
@@ -106,7 +127,7 @@ Or, even more briefly:
|
|
106
127
|
```ruby
|
107
128
|
require 'pcr-ruby'
|
108
129
|
pcr = PCR.new(API_TOKEN)
|
109
|
-
puts pcr.
|
130
|
+
puts pcr.coursehistory("PSCI-150").average("rCourseQuality")
|
110
131
|
#=> 3.041
|
111
132
|
```
|
112
133
|
|
@@ -115,19 +136,26 @@ Finding the most recent section's course difficulty rating is just as easy:
|
|
115
136
|
|
116
137
|
```ruby
|
117
138
|
require 'pcr-ruby'
|
139
|
+
course_code = "PSCI-150"
|
118
140
|
pcr = PCR.new(API_TOKEN)
|
119
|
-
|
120
|
-
puts
|
141
|
+
psci150 = pcr.coursehistory(course_code)
|
142
|
+
puts psci150.recent("rDifficulty") #=> 2.59
|
121
143
|
```
|
122
144
|
|
123
|
-
### Get
|
145
|
+
### Get the course quality rating for a specific course/semester ###
|
124
146
|
```ruby
|
125
147
|
require 'pcr-ruby'
|
148
|
+
course_code = "PSCI-150"
|
126
149
|
pcr = PCR.new(API_TOKEN)
|
127
|
-
|
128
|
-
|
150
|
+
psci150 = pcr.coursehistory(course_code)
|
151
|
+
psci150_course = psci150.courses[3]
|
152
|
+
puts psci150_course.semester #=> "2003C"
|
153
|
+
puts psci150_course.average("rDifficulty") #=> 2.98
|
129
154
|
```
|
130
155
|
|
156
|
+
|
131
157
|
## TODO ##
|
158
|
+
* Make the coursehistory init quicker
|
159
|
+
* Implement refactored Instructor object
|
132
160
|
* Implement stricter checks on course code arguments
|
133
161
|
* Implement search by professor last/first name rather than by ID. ID is unintuitive. Will probably need to see if I can make a lookup method, or simply pull down a database of all instructors and do a search on that database.
|
data/lib/classes/course.rb
CHANGED
@@ -1,79 +1,68 @@
|
|
1
1
|
class Course < PCR
|
2
|
-
attr_accessor :
|
2
|
+
attr_accessor :aliases, :credits, :description, :history, :id,
|
3
|
+
:name, :path, :reviews, :sections, :semester,
|
4
|
+
:retrieved, :valid, :version
|
3
5
|
|
4
|
-
def initialize(
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
6
|
+
def initialize(path, semester, api_endpt, token)
|
7
|
+
@path, @semester = path, semester
|
8
|
+
@api_endpt, @token = api_endpt, token
|
9
|
+
|
10
|
+
# Hit api
|
11
|
+
api_url = makeURL(self.path)
|
12
|
+
json = JSON.parse(open(api_url).read)
|
13
|
+
|
14
|
+
# List of sections
|
15
|
+
section_list = json['result']['sections']['values']
|
16
|
+
@sections = []
|
17
|
+
section_list.each do |section|
|
18
|
+
@sections << Section.new(section['path'], @api_endpt, @token)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Assign attrs
|
22
|
+
attrs = %w(aliases credits description history id name reviews
|
23
|
+
retrieved valid version)
|
24
|
+
attrs.each do |attr|
|
25
|
+
if json['result'][attr]
|
26
|
+
self.instance_variable_set("@#{attr}", json['result'][attr])
|
27
|
+
else
|
28
|
+
self.instance_variable_set("@#{attr}", json[attr])
|
29
|
+
end
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
33
|
+
def compareSemester(other)
|
34
|
+
year = self.semester[0..3]
|
35
|
+
season = self.semester[4]
|
36
|
+
compYear = other.semester[0..3]
|
37
|
+
compSeason = other.semester[4]
|
38
|
+
|
39
|
+
if year.to_i > compYear.to_i #Later year
|
40
|
+
return 1
|
41
|
+
elsif year.to_i < compYear.to_i #Earlier year
|
42
|
+
return -1
|
43
|
+
elsif year.to_i == compYear.to_i #Same year, so test season
|
44
|
+
if season > compSeason #Season is later
|
45
|
+
return 1
|
46
|
+
elsif season = compSeason #Exact same time
|
47
|
+
return 0
|
48
|
+
elsif season < compSeason #compSeason is later
|
49
|
+
return -1
|
48
50
|
end
|
49
|
-
|
50
|
-
#Return average score as a float
|
51
|
-
(total / n)
|
52
|
-
else
|
53
|
-
raise CourseError, "Invalid metric format. Metric must be a string or symbol."
|
54
51
|
end
|
55
52
|
end
|
56
53
|
|
57
|
-
def
|
58
|
-
#
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
#Iterate through all the section reviews, and if the section review id matches
|
66
|
-
#the id of the most recent section, return that rating
|
67
|
-
self.reviews.each do |review|
|
68
|
-
if review["section"]["id"].to_s[0..4].to_i == section.id
|
69
|
-
return review["ratings"][metric]
|
70
|
-
end
|
54
|
+
def average(metric)
|
55
|
+
# Aggregate ratings across all sections
|
56
|
+
total, num = 0, 0
|
57
|
+
self.sections.each do |section|
|
58
|
+
section.reviews.each do |review|
|
59
|
+
total += review.send(metric).to_f
|
60
|
+
num += 1
|
71
61
|
end
|
72
|
-
|
73
|
-
#Else, metric hasn't been found
|
74
|
-
raise CourseError, "No ratings found for #{metric} in #{section.semester}."
|
75
|
-
else
|
76
|
-
raise CourseError, "Invalid metric format. Metric must be a string or symbol."
|
77
62
|
end
|
63
|
+
|
64
|
+
# Return average value across all sections
|
65
|
+
(total / num)
|
78
66
|
end
|
67
|
+
|
79
68
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
class CourseHistory < PCR
|
2
|
+
attr_accessor :course_code, :courses, :id, :path, :retrieved, :valid, :version
|
3
|
+
|
4
|
+
def initialize(course_code, api_endpt, token)
|
5
|
+
@course_code = course_code
|
6
|
+
@api_endpt = api_endpt
|
7
|
+
@token = token
|
8
|
+
|
9
|
+
# Read JSON from PCR API
|
10
|
+
api_url = makeURL("coursehistories/#{self.course_code}")
|
11
|
+
json = JSON.parse(open(api_url).read)
|
12
|
+
|
13
|
+
# List of courses in coursehistory
|
14
|
+
course_list = json['result']['courses']
|
15
|
+
@courses = []
|
16
|
+
course_list.each do |course|
|
17
|
+
@courses << Course.new(course['path'], course['semester'], @api_endpt, @token)
|
18
|
+
end
|
19
|
+
# Sort course list by semester
|
20
|
+
@courses.sort! { |a,b| a.compareSemester(b) }
|
21
|
+
|
22
|
+
# Assign rest of attrs
|
23
|
+
attrs = %w(id path reviews retrieved valid version)
|
24
|
+
attrs.each do |attr|
|
25
|
+
if json['result'][attr]
|
26
|
+
self.instance_variable_set("@#{attr}", json['result'][attr])
|
27
|
+
else
|
28
|
+
self.instance_variable_set("@#{attr}", json[attr])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def recent(metric)
|
34
|
+
# Select most recent course
|
35
|
+
course = @courses[-1]
|
36
|
+
|
37
|
+
# Aggregate ratings for metric
|
38
|
+
total, num = 0, 0
|
39
|
+
course.sections.each do |section|
|
40
|
+
section.reviews.each do |review|
|
41
|
+
total += review.send(metric).to_f
|
42
|
+
num += 1
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return average value across most recent sections
|
47
|
+
(total / num)
|
48
|
+
end
|
49
|
+
|
50
|
+
def average(metric)
|
51
|
+
# Aggregate ratings across all sections
|
52
|
+
total, num = 0, 0
|
53
|
+
courses.each do |course|
|
54
|
+
course.sections.each do |section|
|
55
|
+
section.reviews.each do |review|
|
56
|
+
total += review.send(metric).to_f
|
57
|
+
num += 1
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Return average value across all sections
|
63
|
+
(total / num)
|
64
|
+
end
|
65
|
+
|
66
|
+
def name
|
67
|
+
self.courses.last.name
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#review_new.rb
|
2
|
+
|
3
|
+
class Review
|
4
|
+
#attr_accessor
|
5
|
+
|
6
|
+
def initialize(review_hash)
|
7
|
+
# Assign ratings
|
8
|
+
ratings = review_hash['ratings']
|
9
|
+
ratings.each do |name, val|
|
10
|
+
self.instance_variable_set("@#{name}", val)
|
11
|
+
self.class.send(:attr_accessor, name)
|
12
|
+
#self.class_eval("def #{name};@#{name};end")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
data/lib/classes/section.rb
CHANGED
@@ -1,50 +1,36 @@
|
|
1
|
-
#Section is an individual class under the umbrella of a general Course
|
2
1
|
class Section < PCR
|
3
|
-
attr_accessor :aliases, :
|
2
|
+
attr_accessor :aliases, :course, :group, :id, :instructors,
|
3
|
+
:meetingtimes, :name, :path, :reviews,
|
4
|
+
:sectionnum, :retrieved, :valid, :version
|
4
5
|
|
5
|
-
def initialize(
|
6
|
-
|
7
|
-
@
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def hit_api
|
14
|
-
data = ["aliases", "name", "path", "semester", "description"]
|
15
|
-
api_url = @@api_endpt + "courses/" + self.id.to_s + "?token=" + @@token
|
6
|
+
def initialize(path, api_endpt, token)
|
7
|
+
@path = path
|
8
|
+
@api_endpt = api_endpt
|
9
|
+
@token = token
|
10
|
+
|
11
|
+
# Hit api
|
12
|
+
api_url = makeURL(self.path)
|
16
13
|
json = JSON.parse(open(api_url).read)
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
when "semester"
|
26
|
-
self.instance_variable_set("@#{d}", json["result"]["semester"])
|
27
|
-
when "description"
|
28
|
-
self.instance_variable_set("@#{d}", json["result"]["description"])
|
29
|
-
end
|
14
|
+
|
15
|
+
# Get reviews
|
16
|
+
# Usually one, but may be > 1
|
17
|
+
@reviews = []
|
18
|
+
reviews_url = makeURL(json['result']['reviews']['path'])
|
19
|
+
reviews_json = JSON.parse(open(reviews_url).read)
|
20
|
+
reviews_json['result']['values'].each do |review|
|
21
|
+
@reviews << Review.new(review)
|
30
22
|
end
|
31
23
|
|
32
|
-
#
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
@instructors = []
|
42
|
-
json["result"]["values"].each do |a|
|
43
|
-
@comments << {a["instructor"]["id"] => a["comments"]}
|
44
|
-
@ratings << {a["instructor"]["id"] => a["ratings"]}
|
45
|
-
@instructors << a["instructor"]
|
24
|
+
# Assign attrs
|
25
|
+
attrs = %w(aliases course group id instructors meetingtimes name
|
26
|
+
sectionnum retrieved valid version)
|
27
|
+
attrs.each do |attr|
|
28
|
+
if json['result'][attr]
|
29
|
+
self.instance_variable_set("@#{attr}", json['result'][attr])
|
30
|
+
else
|
31
|
+
self.instance_variable_set("@#{attr}", json[attr])
|
32
|
+
end
|
46
33
|
end
|
47
|
-
@reviews = {"comments" => @comments, "ratings" => @ratings}
|
48
34
|
end
|
49
|
-
|
35
|
+
|
50
36
|
end
|
data/lib/classes/string.rb
CHANGED
@@ -52,16 +52,16 @@ class ::String
|
|
52
52
|
compSeason = s[4]
|
53
53
|
|
54
54
|
if year.to_i > compYear.to_i #Later year
|
55
|
-
return
|
55
|
+
return 1
|
56
56
|
elsif year.to_i < compYear.to_i #Earlier year
|
57
|
-
return
|
57
|
+
return -1
|
58
58
|
elsif year.to_i == compYear.to_i #Same year, so test season
|
59
59
|
if season > compSeason #Season is later
|
60
|
-
return
|
60
|
+
return 1
|
61
61
|
elsif season = compSeason #Exact same time
|
62
62
|
return 0
|
63
63
|
elsif season < compSeason #compSeason is later
|
64
|
-
return
|
64
|
+
return -1
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
data/lib/pcr-ruby.rb
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'open-uri'
|
3
|
-
require 'time'
|
4
|
-
require 'csv'
|
5
|
-
|
6
3
|
|
7
4
|
#PCR class handles token and api url, so both are easily changed
|
8
5
|
class PCR
|
6
|
+
attr_accessor :token, :api_endpt
|
7
|
+
|
9
8
|
def initialize(token, api_endpt = "http://api.penncoursereview.com/v1/")
|
10
|
-
|
11
|
-
|
9
|
+
@token = token
|
10
|
+
@api_endpt = api_endpt
|
12
11
|
end
|
13
12
|
|
14
|
-
def
|
15
|
-
|
13
|
+
def coursehistory(course_code)
|
14
|
+
CourseHistory.new(course_code, self.api_endpt, self.token)
|
16
15
|
end
|
17
16
|
|
18
|
-
def
|
19
|
-
|
20
|
-
end
|
17
|
+
# def instructor(id)
|
18
|
+
# Instructor.new(id)
|
19
|
+
# end
|
21
20
|
|
22
|
-
def
|
23
|
-
|
21
|
+
def makeURL(path)
|
22
|
+
"#{self.api_endpt + path}?token=#{self.token}"
|
24
23
|
end
|
24
|
+
|
25
25
|
end
|
26
26
|
|
27
27
|
# Load classes
|
data/lib/pcr-ruby/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pcr-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.5'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-11-
|
12
|
+
date: 2012-11-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|
@@ -38,10 +38,10 @@ files:
|
|
38
38
|
- LICENSE
|
39
39
|
- README.md
|
40
40
|
- Rakefile
|
41
|
-
- lib/classes/array.rb
|
42
41
|
- lib/classes/course.rb
|
42
|
+
- lib/classes/coursehistory.rb
|
43
43
|
- lib/classes/errors.rb
|
44
|
-
- lib/classes/
|
44
|
+
- lib/classes/review.rb
|
45
45
|
- lib/classes/section.rb
|
46
46
|
- lib/classes/string.rb
|
47
47
|
- lib/pcr-ruby.rb
|
data/lib/classes/array.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
#Add useful array methods
|
2
|
-
class Array
|
3
|
-
def binary_search(target)
|
4
|
-
self.search_iter(0, self.length-1, target)
|
5
|
-
end
|
6
|
-
|
7
|
-
def search_iter(lower, upper, target)
|
8
|
-
return -1 if lower > upper
|
9
|
-
mid = (lower+upper)/2
|
10
|
-
if (self[mid] == target)
|
11
|
-
mid
|
12
|
-
elsif (target < self[mid])
|
13
|
-
self.search_iter(lower, mid-1, target)
|
14
|
-
else
|
15
|
-
self.search_iter(mid+1, upper, target)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
data/lib/classes/instructor.rb
DELETED
@@ -1,116 +0,0 @@
|
|
1
|
-
#Instructor is a professor. Instructors are not tied to a course or section, but will have to be referenced from Sections.
|
2
|
-
class Instructor < PCR
|
3
|
-
attr_accessor :id, :name, :path, :sections, :reviews
|
4
|
-
|
5
|
-
def initialize(id)
|
6
|
-
#Assign args
|
7
|
-
if id.is_a? String
|
8
|
-
@id = id
|
9
|
-
else
|
10
|
-
raise InstructorError("Invalid Instructor ID specified.")
|
11
|
-
end
|
12
|
-
|
13
|
-
#Hit PCR API to get missing info based on id
|
14
|
-
self.getInfo
|
15
|
-
self.getReviews
|
16
|
-
end
|
17
|
-
|
18
|
-
#Hit the PCR API to get all missing info
|
19
|
-
def getInfo
|
20
|
-
api_url = @@api_endpt + "instructors/" + self.id + "?token=" + @@token
|
21
|
-
json = JSON.parse(open(api_url).read)
|
22
|
-
@name = json["result"]["name"].downcase.titlecase unless @name
|
23
|
-
@path = json["result"]["path"] unless @path
|
24
|
-
@sections = json["result"]["reviews"]["values"] unless @sections #Mislabeled reviews in PCR API
|
25
|
-
end
|
26
|
-
|
27
|
-
#Separate method for getting review data
|
28
|
-
def getReviews
|
29
|
-
api_url = @@api_endpt + "instructors/" + self.id + "/reviews?token=" + @@token
|
30
|
-
json = JSON.parse(open(api_url).read)
|
31
|
-
@reviews = json["result"]["values"] #array
|
32
|
-
end
|
33
|
-
|
34
|
-
#Get average value of a certain rating for Instructor
|
35
|
-
def average(metric)
|
36
|
-
#Ensure that we know argument type
|
37
|
-
metric = metric.to_s if metric.is_a? Symbol
|
38
|
-
|
39
|
-
if metric.is_a? String
|
40
|
-
#Loop vars
|
41
|
-
total, n = 0, 0
|
42
|
-
|
43
|
-
#For each section, check if ratings include metric arg
|
44
|
-
#if so, add metric rating to total and increment counting variable
|
45
|
-
self.getReviews
|
46
|
-
self.reviews.each do |review|
|
47
|
-
if review["ratings"].include? metric
|
48
|
-
total = total + review["ratings"][metric].to_f
|
49
|
-
n = n + 1
|
50
|
-
else
|
51
|
-
raise CourseError, "No ratings found for \"#{metric}\" for #{self.name}."
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
#Return average score as a float
|
56
|
-
(total / n)
|
57
|
-
else
|
58
|
-
raise CourseError, "Invalid metric format. Metric must be a string or symbol."
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
#Get most recent value of a certain rating for Instructor
|
63
|
-
def recent(metric)
|
64
|
-
#Ensure that we know argument type
|
65
|
-
metric = metric.to_s if metric.is_a? Symbol
|
66
|
-
|
67
|
-
if metric.is_a? String
|
68
|
-
#Iterate through reviews and create Section for each section reviewed, presented in an array
|
69
|
-
sections = []
|
70
|
-
section_ids = []
|
71
|
-
self.getReviews
|
72
|
-
self.reviews.each do |review|
|
73
|
-
if section_ids.index(review["section"]["id"].to_i).nil?
|
74
|
-
s = PCR::Section.new(review["section"]["id"].to_i, false)
|
75
|
-
sections << s
|
76
|
-
section_ids << s.id
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
#Get only most recent Section(s) in the array
|
81
|
-
sections.reverse! #Newest first
|
82
|
-
targets = []
|
83
|
-
sections.each do |s|
|
84
|
-
s.hit_api
|
85
|
-
if sections.index(s) == 0
|
86
|
-
targets << s
|
87
|
-
elsif s.semester == sections[0].semester && s.id != sections[0].id
|
88
|
-
targets << s
|
89
|
-
else
|
90
|
-
break
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
#Calculate recent rating
|
95
|
-
total, num = 0, 0
|
96
|
-
targets.each do |section|
|
97
|
-
#Make sure we get the rating for the right Instructor
|
98
|
-
section.ratings.each do |rating|
|
99
|
-
if rating.key?(self.id)
|
100
|
-
if rating[self.id][metric].nil?
|
101
|
-
raise InstructorError, "No ratings found for #{metric} for #{self.name}."
|
102
|
-
else
|
103
|
-
total = total + rating[self.id][metric].to_f
|
104
|
-
num += 1
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
# Return recent rating
|
111
|
-
(total / num)
|
112
|
-
else
|
113
|
-
raise CourseError, "Invalid metric format. Metric must be a string or symbol."
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|