pcr-ruby 0.2 → 0.2.1
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 +13 -7
- data/lib/classes/course.rb +17 -27
- data/lib/classes/instructor.rb +16 -30
- data/lib/classes/section.rb +3 -6
- data/lib/pcr-ruby/version.rb +1 -1
- metadata +2 -2
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -2,13 +2,20 @@
|
|
2
2
|
|
3
3
|
pcr-ruby is a simple, intuitive way to retrieve course data from the Penn Course Review API in Ruby. With pcr-ruby and a valid API token (which you can request [here](https://docs.google.com/spreadsheet/viewform?hl=en_US&formkey=dGZOZkJDaVkxdmc5QURUejAteFdBZGc6MQ#gid=0)), your Ruby project has access to reviews, ratings, and other information for all Penn courses.
|
4
4
|
|
5
|
+
## Installation ##
|
6
|
+
|
7
|
+
pcr-ruby is a gem hosted on rubygems, so installation is as simple as:
|
8
|
+
```
|
9
|
+
gem install pcr-ruby
|
10
|
+
```
|
11
|
+
|
5
12
|
## How to use pcr-ruby #
|
6
13
|
|
7
14
|
*This section may change a lot as pcr-ruby is developed. As such, this section may not be fully accurate, but I will try to keep the instructions as current as possible.*
|
8
15
|
|
9
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.)
|
10
17
|
|
11
|
-
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 three types of objects: 'Courses', 'Sections', and 'Instructors'.
|
12
19
|
|
13
20
|
### 'Courses' in pcr-ruby ###
|
14
21
|
|
@@ -44,7 +51,6 @@ require 'pcr-ruby'
|
|
44
51
|
pcr = PCR.new(api_token)
|
45
52
|
section = pcr.section(id)
|
46
53
|
```
|
47
|
-
Possible instance variables available for setting in the Section initialize method are: aliases, id, name, path, semester.
|
48
54
|
|
49
55
|
Sections have the following instance variables:
|
50
56
|
* **aliases** -- an array of the Section's course listings. Most of the time, a Section will only have one listing (the course code followed by a section code, like "-001"), but Sections that are cross-listed between departments may have multiple listings.
|
@@ -88,7 +94,7 @@ Here are some (hopefully very simple and intuitive) usage examples for pcr-ruby:
|
|
88
94
|
Let's say we want to find the average course quality rating for Introduction to International Relations, PSCI-150:
|
89
95
|
|
90
96
|
```ruby
|
91
|
-
require 'pcr
|
97
|
+
require 'pcr-ruby'
|
92
98
|
course_code = "PSCI-150"
|
93
99
|
pcr = PCR.new(API_TOKEN)
|
94
100
|
course = pcr.course(course_code)
|
@@ -98,9 +104,9 @@ puts course.average("rCourseQuality") #=> 3.041
|
|
98
104
|
Or, even more briefly:
|
99
105
|
|
100
106
|
```ruby
|
101
|
-
require 'pcr
|
107
|
+
require 'pcr-ruby'
|
102
108
|
pcr = PCR.new(API_TOKEN)
|
103
|
-
puts pcr.course("PSCI-150")
|
109
|
+
puts pcr.course("PSCI-150").average("rCourseQuality")
|
104
110
|
#=> 3.041
|
105
111
|
```
|
106
112
|
|
@@ -108,7 +114,7 @@ puts pcr.course("PSCI-150")course.average("rCourseQuality")
|
|
108
114
|
Finding the most recent section's course difficulty rating is just as easy:
|
109
115
|
|
110
116
|
```ruby
|
111
|
-
require 'pcr
|
117
|
+
require 'pcr-ruby'
|
112
118
|
pcr = PCR.new(API_TOKEN)
|
113
119
|
course = pcr.course("PSCI-150")
|
114
120
|
puts course.recent("rDifficulty") #=> 2.5
|
@@ -116,7 +122,7 @@ puts course.recent("rDifficulty") #=> 2.5
|
|
116
122
|
|
117
123
|
### Get professor's average "ability to stimulate student interest" rating ###
|
118
124
|
```ruby
|
119
|
-
require 'pcr
|
125
|
+
require 'pcr-ruby'
|
120
126
|
pcr = PCR.new(API_TOKEN)
|
121
127
|
instructor = pcr.instructor("1090-LINDA-H-ZHAO")
|
122
128
|
puts instructor.average("rStimulateInterest").round(2) #=> 1.7
|
data/lib/classes/course.rb
CHANGED
@@ -1,32 +1,28 @@
|
|
1
|
-
#Course object matches up with the coursehistory request of the pcr api.
|
2
|
-
#A Course essentially is a signle curriculum and course code, and includes all Sections across time (semesters).
|
3
1
|
class Course < PCR
|
4
2
|
attr_accessor :course_code, :sections, :id, :name, :path, :reviews
|
5
3
|
|
6
4
|
def initialize(course_code)
|
7
|
-
if course_code.is_a? String
|
5
|
+
if course_code.is_a? String && course_code.isValidCourseCode?
|
8
6
|
@course_code = course_code
|
9
7
|
|
10
8
|
#Read JSON from the PCR API
|
11
9
|
api_url = @@api_endpt + "coursehistories/" + self.course_code + "/?token=" + @@token
|
12
10
|
json = JSON.parse(open(api_url).read)
|
13
11
|
|
14
|
-
#Create array of Section objects
|
12
|
+
#Create array of Section objects
|
15
13
|
@sections = []
|
16
|
-
json["result"]["courses"].each
|
17
|
-
@sections << Section.new(c["id"])
|
18
|
-
end
|
14
|
+
json["result"]["courses"].each { |c| @sections << Section.new(c["id"]) }
|
19
15
|
|
20
16
|
#Set variables according to Course JSON data
|
21
17
|
@id = json["result"]["id"]
|
22
18
|
@name = json["result"]["name"]
|
23
19
|
@path = json["result"]["path"]
|
24
20
|
|
25
|
-
#Get reviews for the Course --
|
26
|
-
api_url_reviews = @@api_endpt + "coursehistories/" +
|
21
|
+
#Get reviews for the Course -- this has to be a separate query
|
22
|
+
api_url_reviews = @@api_endpt + "coursehistories/" +
|
23
|
+
self.id.to_s + "/reviews?token=" + @@token
|
27
24
|
json_reviews = JSON.parse(open(api_url_reviews).read)
|
28
25
|
@reviews = json_reviews["result"]["values"]
|
29
|
-
|
30
26
|
else
|
31
27
|
raise CourseError, "Invalid course code specified. Use format [DEPT-###]."
|
32
28
|
end
|
@@ -34,19 +30,16 @@ class Course < PCR
|
|
34
30
|
|
35
31
|
def average(metric)
|
36
32
|
#Ensure that we know argument type
|
37
|
-
if metric.is_a? Symbol
|
38
|
-
|
39
|
-
end
|
40
|
-
|
33
|
+
metric = metric.to_s if metric.is_a? Symbol
|
34
|
+
|
41
35
|
if metric.is_a? String
|
42
36
|
#Loop vars
|
43
|
-
total = 0
|
44
|
-
n = 0
|
37
|
+
total, n = 0, 0
|
45
38
|
|
46
|
-
#For each section, check if ratings include metric arg
|
39
|
+
#For each section, check if ratings include metric arg
|
40
|
+
#if so, add metric rating to total && increment counting variable
|
47
41
|
self.reviews.each do |review|
|
48
|
-
|
49
|
-
if ratings.include? metric
|
42
|
+
if review["ratings"].include? metric
|
50
43
|
total = total + review["ratings"][metric].to_f
|
51
44
|
n = n + 1
|
52
45
|
else
|
@@ -55,8 +48,7 @@ class Course < PCR
|
|
55
48
|
end
|
56
49
|
|
57
50
|
#Return average score as a float
|
58
|
-
(total/n)
|
59
|
-
|
51
|
+
(total / n)
|
60
52
|
else
|
61
53
|
raise CourseError, "Invalid metric format. Metric must be a string or symbol."
|
62
54
|
end
|
@@ -64,24 +56,22 @@ class Course < PCR
|
|
64
56
|
|
65
57
|
def recent(metric)
|
66
58
|
#Ensure that we know argument type
|
67
|
-
if metric.is_a? Symbol
|
68
|
-
metric = metric.to_s
|
69
|
-
end
|
70
|
-
|
59
|
+
metric = metric.to_s if metric.is_a? Symbol
|
71
60
|
|
72
61
|
if metric.is_a? String
|
73
62
|
#Get the most recent section
|
74
63
|
section = self.sections[-1]
|
75
64
|
|
76
|
-
#Iterate through all the section reviews, and if the section review id matches
|
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
|
77
67
|
self.reviews.each do |review|
|
78
68
|
if review["section"]["id"].to_s[0..4].to_i == section.id
|
79
69
|
return review["ratings"][metric]
|
80
70
|
end
|
81
71
|
end
|
82
72
|
|
73
|
+
#Else, metric hasn't been found
|
83
74
|
raise CourseError, "No ratings found for #{metric} in #{section.semester}."
|
84
|
-
|
85
75
|
else
|
86
76
|
raise CourseError, "Invalid metric format. Metric must be a string or symbol."
|
87
77
|
end
|
data/lib/classes/instructor.rb
CHANGED
@@ -3,7 +3,7 @@ class Instructor < PCR
|
|
3
3
|
attr_accessor :id, :name, :path, :sections, :reviews
|
4
4
|
|
5
5
|
def initialize(id)
|
6
|
-
#Assign args
|
6
|
+
#Assign args
|
7
7
|
if id.is_a? String
|
8
8
|
@id = id
|
9
9
|
else
|
@@ -13,47 +13,38 @@ class Instructor < PCR
|
|
13
13
|
#Hit PCR API to get missing info based on id
|
14
14
|
self.getInfo
|
15
15
|
self.getReviews
|
16
|
-
|
17
16
|
end
|
18
17
|
|
19
18
|
#Hit the PCR API to get all missing info
|
20
|
-
#Separate method in case we want to conduct it separately from a class init
|
21
19
|
def getInfo
|
22
20
|
api_url = @@api_endpt + "instructors/" + self.id + "?token=" + @@token
|
23
21
|
json = JSON.parse(open(api_url).read)
|
24
|
-
|
25
22
|
@name = json["result"]["name"].downcase.titlecase unless @name
|
26
23
|
@path = json["result"]["path"] unless @path
|
27
24
|
@sections = json["result"]["reviews"]["values"] unless @sections #Mislabeled reviews in PCR API
|
28
25
|
end
|
29
26
|
|
30
|
-
#Separate method for getting review data
|
27
|
+
#Separate method for getting review data
|
31
28
|
def getReviews
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
@reviews = json["result"]["values"] #gets array
|
37
|
-
end
|
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
|
38
32
|
end
|
39
33
|
|
40
34
|
#Get average value of a certain rating for Instructor
|
41
35
|
def average(metric)
|
42
36
|
#Ensure that we know argument type
|
43
|
-
if metric.is_a? Symbol
|
44
|
-
metric = metric.to_s
|
45
|
-
end
|
37
|
+
metric = metric.to_s if metric.is_a? Symbol
|
46
38
|
|
47
39
|
if metric.is_a? String
|
48
40
|
#Loop vars
|
49
|
-
total = 0
|
50
|
-
n = 0
|
41
|
+
total, n = 0, 0
|
51
42
|
|
52
|
-
#For each section, check if ratings include metric arg
|
43
|
+
#For each section, check if ratings include metric arg
|
44
|
+
#if so, add metric rating to total and increment counting variable
|
53
45
|
self.getReviews
|
54
46
|
self.reviews.each do |review|
|
55
|
-
|
56
|
-
if ratings.include? metric
|
47
|
+
if review["ratings"].include? metric
|
57
48
|
total = total + review["ratings"][metric].to_f
|
58
49
|
n = n + 1
|
59
50
|
else
|
@@ -62,8 +53,7 @@ class Instructor < PCR
|
|
62
53
|
end
|
63
54
|
|
64
55
|
#Return average score as a float
|
65
|
-
(total/n)
|
66
|
-
|
56
|
+
(total / n)
|
67
57
|
else
|
68
58
|
raise CourseError, "Invalid metric format. Metric must be a string or symbol."
|
69
59
|
end
|
@@ -72,9 +62,7 @@ class Instructor < PCR
|
|
72
62
|
#Get most recent value of a certain rating for Instructor
|
73
63
|
def recent(metric)
|
74
64
|
#Ensure that we know argument type
|
75
|
-
if metric.is_a? Symbol
|
76
|
-
metric = metric.to_s
|
77
|
-
end
|
65
|
+
metric = metric.to_s if metric.is_a? Symbol
|
78
66
|
|
79
67
|
if metric.is_a? String
|
80
68
|
#Iterate through reviews and create Section for each section reviewed, presented in an array
|
@@ -93,10 +81,10 @@ class Instructor < PCR
|
|
93
81
|
sections.reverse! #Newest first
|
94
82
|
targets = []
|
95
83
|
sections.each do |s|
|
96
|
-
s.hit_api
|
84
|
+
s.hit_api
|
97
85
|
if sections.index(s) == 0
|
98
86
|
targets << s
|
99
|
-
elsif s.semester == sections[0].semester
|
87
|
+
elsif s.semester == sections[0].semester && s.id != sections[0].id
|
100
88
|
targets << s
|
101
89
|
else
|
102
90
|
break
|
@@ -104,8 +92,7 @@ class Instructor < PCR
|
|
104
92
|
end
|
105
93
|
|
106
94
|
#Calculate recent rating
|
107
|
-
total = 0
|
108
|
-
num = 0
|
95
|
+
total, num = 0, 0
|
109
96
|
targets.each do |section|
|
110
97
|
#Make sure we get the rating for the right Instructor
|
111
98
|
section.ratings.each do |rating|
|
@@ -121,8 +108,7 @@ class Instructor < PCR
|
|
121
108
|
end
|
122
109
|
|
123
110
|
# Return recent rating
|
124
|
-
total / num
|
125
|
-
|
111
|
+
(total / num)
|
126
112
|
else
|
127
113
|
raise CourseError, "Invalid metric format. Metric must be a string or symbol."
|
128
114
|
end
|
data/lib/classes/section.rb
CHANGED
@@ -7,15 +7,13 @@ class Section < PCR
|
|
7
7
|
@id = id
|
8
8
|
|
9
9
|
# Hit api to fill additional info
|
10
|
-
self.hit_api
|
11
|
-
|
10
|
+
self.hit_api unless hit_api == false
|
12
11
|
end
|
13
12
|
|
14
|
-
def hit_api
|
13
|
+
def hit_api
|
15
14
|
data = ["aliases", "name", "path", "semester", "description"]
|
16
15
|
api_url = @@api_endpt + "courses/" + self.id.to_s + "?token=" + @@token
|
17
16
|
json = JSON.parse(open(api_url).read)
|
18
|
-
|
19
17
|
data.each do |d|
|
20
18
|
case d
|
21
19
|
when "aliases"
|
@@ -33,10 +31,9 @@ class Section < PCR
|
|
33
31
|
|
34
32
|
# Get review data
|
35
33
|
self.get_reviews
|
36
|
-
|
37
34
|
end
|
38
35
|
|
39
|
-
def get_reviews
|
36
|
+
def get_reviews
|
40
37
|
api_url = @@api_endpt + "courses/" + self.id.to_s + "/reviews?token=" + @@token
|
41
38
|
json = JSON.parse(open(api_url).read)
|
42
39
|
@comments = []
|
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:
|
4
|
+
version: 0.2.1
|
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-
|
12
|
+
date: 2012-11-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|