pcr-ruby 0.0.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 +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/lib/pcr-ruby.rb +522 -0
- data/lib/pcr-ruby/version.rb +5 -0
- data/pcr-ruby.gemspec +19 -0
- metadata +68 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 parm289
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Pcr::Ruby
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'pcr-ruby'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install pcr-ruby
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/pcr-ruby.rb
ADDED
@@ -0,0 +1,522 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'time'
|
4
|
+
require 'csv'
|
5
|
+
|
6
|
+
module PCR
|
7
|
+
|
8
|
+
#Add some useful String methods
|
9
|
+
class ::String
|
10
|
+
#Checks if String is valid Penn course code format
|
11
|
+
def isValidCourseCode?
|
12
|
+
test = self.split('-')
|
13
|
+
if test[0].length == 4 and test[1].length == 3
|
14
|
+
true
|
15
|
+
else
|
16
|
+
false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
#Methods to convert strings to titlecase.
|
21
|
+
#Thanks https://github.com/samsouder/titlecase
|
22
|
+
def titlecase
|
23
|
+
small_words = %w(a an and as at but by en for if in of on or the to v v. via vs vs.)
|
24
|
+
|
25
|
+
x = split(" ").map do |word|
|
26
|
+
# note: word could contain non-word characters!
|
27
|
+
# downcase all small_words, capitalize the rest
|
28
|
+
small_words.include?(word.gsub(/\W/, "").downcase) ? word.downcase! : word.smart_capitalize!
|
29
|
+
word
|
30
|
+
end
|
31
|
+
# capitalize first and last words
|
32
|
+
x.first.smart_capitalize!
|
33
|
+
x.last.smart_capitalize!
|
34
|
+
# small words after colons are capitalized
|
35
|
+
x.join(" ").gsub(/:\s?(\W*#{small_words.join("|")}\W*)\s/) { ": #{$1.smart_capitalize} " }
|
36
|
+
end
|
37
|
+
|
38
|
+
def smart_capitalize
|
39
|
+
# ignore any leading crazy characters and capitalize the first real character
|
40
|
+
if self =~ /^['"\(\[']*([a-z])/
|
41
|
+
i = index($1)
|
42
|
+
x = self[i,self.length]
|
43
|
+
# word with capitals and periods mid-word are left alone
|
44
|
+
self[i,1] = self[i,1].upcase unless x =~ /[A-Z]/ or x =~ /\.\w+/
|
45
|
+
end
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def smart_capitalize!
|
50
|
+
replace(smart_capitalize)
|
51
|
+
end
|
52
|
+
|
53
|
+
#Method to compare semesters. Returns true if self is later, false if self is before, 0 if same
|
54
|
+
#s should be a string like "2009A"
|
55
|
+
def compareSemester(s)
|
56
|
+
year = self[0..3]
|
57
|
+
season = self[4]
|
58
|
+
compYear = s[0..3]
|
59
|
+
compSeason = s[4]
|
60
|
+
|
61
|
+
if year.to_i > compYear.to_i #Later year
|
62
|
+
return true
|
63
|
+
elsif year.to_i < compYear.to_i #Earlier year
|
64
|
+
return false
|
65
|
+
elsif year.to_i == compYear.to_i #Same year, so test season
|
66
|
+
if season > compSeason #Season is later
|
67
|
+
return true
|
68
|
+
elsif season = compSeason #Exact same time
|
69
|
+
return 0
|
70
|
+
elsif season < compSeason #compSeason is later
|
71
|
+
return false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
#Add useful array methods
|
78
|
+
class Array
|
79
|
+
def binary_search(target)
|
80
|
+
self.search_iter(0, self.length-1, target)
|
81
|
+
end
|
82
|
+
|
83
|
+
def search_iter(lower, upper, target)
|
84
|
+
return -1 if lower > upper
|
85
|
+
mid = (lower+upper)/2
|
86
|
+
if (self[mid] == target)
|
87
|
+
mid
|
88
|
+
elsif (target < self[mid])
|
89
|
+
self.search_iter(lower, mid-1, target)
|
90
|
+
else
|
91
|
+
self.search_iter(mid+1, upper, target)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
#API class handles token and api url, so both are easily changed
|
97
|
+
class API
|
98
|
+
attr_accessor :token, :api_endpt
|
99
|
+
def initialize()
|
100
|
+
@token = File.open('token.dat', &:readline)
|
101
|
+
@api_endpt = "http://api.penncoursereview.com/v1/"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
#These errors serve as more specific exceptions so we know where exactly errors are coming from.
|
106
|
+
class CourseError < StandardError
|
107
|
+
end
|
108
|
+
|
109
|
+
class InstructorError < StandardError
|
110
|
+
end
|
111
|
+
|
112
|
+
#Course object matches up with the coursehistory request of the pcr api.
|
113
|
+
#A Course essentially is a signle curriculum and course code, and includes all Sections across time (semesters).
|
114
|
+
class Course
|
115
|
+
attr_accessor :course_code, :sections, :id, :name, :path, :reviews
|
116
|
+
|
117
|
+
def initialize(args)
|
118
|
+
#Set indifferent access for args hash
|
119
|
+
args.default_proc = proc do |h, k|
|
120
|
+
case k
|
121
|
+
when String then sym = k.to_sym; h[sym] if h.key?(sym)
|
122
|
+
when Symbol then str = k.to_s; h[str] if h.key?(str)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
#Initialization actions
|
127
|
+
if args[:course_code].is_a? String and args[:course_code].isValidCourseCode?
|
128
|
+
@course_code = args[:course_code]
|
129
|
+
|
130
|
+
#Read JSON from the PCR API
|
131
|
+
pcr = PCR::API.new()
|
132
|
+
api_url = pcr.api_endpt + "coursehistories/" + self.course_code + "/?token=" + pcr.token
|
133
|
+
json = JSON.parse(open(api_url).read)
|
134
|
+
|
135
|
+
#Create array of Section objects, containing all Sections found in the API JSON for the Course
|
136
|
+
@sections = []
|
137
|
+
json["result"]["courses"].each do |c|
|
138
|
+
@sections << Section.new(:aliases => c["aliases"], :id => c["id"], :name => c["name"], :path => c["path"], :semester => c["semester"], :hit_api => true)
|
139
|
+
end
|
140
|
+
|
141
|
+
#Set variables according to Course JSON data
|
142
|
+
@id = json["result"]["id"]
|
143
|
+
@name = json["result"]["name"]
|
144
|
+
@path = json["result"]["path"]
|
145
|
+
|
146
|
+
#Get reviews for the Course -- unfortunately this has to be a separate query
|
147
|
+
api_url_reviews = pcr.api_endpt + "coursehistories/" + self.id.to_s + "/reviews?token=" + pcr.token
|
148
|
+
json_reviews = JSON.parse(open(api_url_reviews).read)
|
149
|
+
@reviews = json_reviews["result"]["values"]
|
150
|
+
|
151
|
+
else
|
152
|
+
raise CourseError, "Invalid course code specified. Use format [DEPT-###]."
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def average(metric)
|
157
|
+
#Ensure that we know argument type
|
158
|
+
if metric.is_a? Symbol
|
159
|
+
metric = metric.to_s
|
160
|
+
end
|
161
|
+
|
162
|
+
if metric.is_a? String
|
163
|
+
#Loop vars
|
164
|
+
total = 0
|
165
|
+
n = 0
|
166
|
+
|
167
|
+
#For each section, check if ratings include metric arg -- if so, add metric rating to total and increment counting variable
|
168
|
+
self.reviews.each do |review|
|
169
|
+
ratings = review["ratings"]
|
170
|
+
if ratings.include? metric
|
171
|
+
total = total + review["ratings"][metric].to_f
|
172
|
+
n = n + 1
|
173
|
+
else
|
174
|
+
raise CourseError, "No ratings found for \"#{metric}\" in #{self.name}."
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
#Return average score as a float
|
179
|
+
return (total/n)
|
180
|
+
|
181
|
+
else
|
182
|
+
raise CourseError, "Invalid metric format. Metric must be a string or symbol."
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def recent(metric)
|
187
|
+
#Ensure that we know argument type
|
188
|
+
if metric.is_a? Symbol
|
189
|
+
metric = metric.to_s
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
if metric.is_a? String
|
194
|
+
#Get the most recent section
|
195
|
+
section = self.sections[-1]
|
196
|
+
|
197
|
+
#Iterate through all the section reviews, and if the section review id matches the id of the most recent section, return that rating
|
198
|
+
self.reviews.each do |review|
|
199
|
+
if review["section"]["id"].to_s[0..4].to_i == section.id
|
200
|
+
return review["ratings"][metric]
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
raise CourseError, "No ratings found for #{metric} in #{section.semester}."
|
205
|
+
|
206
|
+
else
|
207
|
+
raise CourseError, "Invalid metric format. Metric must be a string or symbol."
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
#Section is an individual class under the umbrella of a general Course
|
213
|
+
class Section
|
214
|
+
attr_accessor :aliases, :id, :name, :path, :semester, :description, :comments, :ratings, :instructor
|
215
|
+
|
216
|
+
def initialize(args)
|
217
|
+
#Set indifferent access for args
|
218
|
+
args.default_proc = proc do |h, k|
|
219
|
+
case k
|
220
|
+
when String then sym = k.to_sym; h[sym] if h.key?(sym)
|
221
|
+
when Symbol then str = k.to_s; h[str] if h.key?(str)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
pcr = PCR::API.new()
|
226
|
+
@aliases = args[:aliases] if args[:aliases].is_a? Array
|
227
|
+
@id = args[:id] if args[:id].is_a? Integer
|
228
|
+
@name = args[:name] if args[:name].is_a? String
|
229
|
+
@path = args[:path] if args[:path].is_a? String
|
230
|
+
@semester = args[:semester] if args[:semester].is_a? String
|
231
|
+
@comments = ""
|
232
|
+
@ratings = {}
|
233
|
+
@instructor = {}
|
234
|
+
|
235
|
+
if args[:hit_api]
|
236
|
+
if args[:get_reviews]
|
237
|
+
self.hit_api(:get_reviews => true)
|
238
|
+
else
|
239
|
+
self.hit_api(:get_reviews => false)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def hit_api(args)
|
245
|
+
data = ["aliases", "name", "path", "semester", "description"]
|
246
|
+
pcr = PCR::API.new()
|
247
|
+
api_url = pcr.api_endpt + "courses/" + self.id.to_s + "?token=" + pcr.token
|
248
|
+
json = JSON.parse(open(api_url).read)
|
249
|
+
|
250
|
+
data.each do |d|
|
251
|
+
case d
|
252
|
+
when "aliases"
|
253
|
+
self.instance_variable_set("@#{d}", json["result"]["aliases"])
|
254
|
+
when "name"
|
255
|
+
self.instance_variable_set("@#{d}", json["result"]["name"])
|
256
|
+
when "path"
|
257
|
+
self.instance_variable_set("@#{d}", json["result"]["path"])
|
258
|
+
when "semester"
|
259
|
+
self.instance_variable_set("@#{d}", json["result"]["semester"])
|
260
|
+
when "description"
|
261
|
+
self.instance_variable_set("@#{d}", json["result"]["description"])
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
if args[:get_reviews]
|
266
|
+
self.reviews()
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def reviews()
|
271
|
+
pcr = PCR::API.new()
|
272
|
+
api_url = pcr.api_endpt + "courses/" + self.id.to_s + "/reviews?token=" + pcr.token
|
273
|
+
json = JSON.parse(open(api_url).read)
|
274
|
+
@comments = []
|
275
|
+
@ratings = []
|
276
|
+
@instructors = []
|
277
|
+
json["result"]["values"].each do |a|
|
278
|
+
@comments << {a["instructor"]["id"] => a["comments"]}
|
279
|
+
@ratings << {a["instructor"]["id"] => a["ratings"]}
|
280
|
+
@instructors << a["instructor"]
|
281
|
+
end
|
282
|
+
# @comments = json["result"]["values"][0]["comments"]
|
283
|
+
# @ratings = json["result"]["values"][0]["ratings"]
|
284
|
+
# @instructor = json["result"]["values"][0]["instructor"]
|
285
|
+
|
286
|
+
return {:comments => @comments, :ratings => @ratings}
|
287
|
+
end
|
288
|
+
|
289
|
+
def after(s)
|
290
|
+
if s.is_a? Section
|
291
|
+
self.semester.compareSemester(s.semester)
|
292
|
+
elsif s.is_a? String
|
293
|
+
self.semester.compareSemester(s)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
#Instructor is a professor. Instructors are not tied to a course or section, but will have to be referenced from Sections.
|
299
|
+
class Instructor
|
300
|
+
attr_accessor :id, :name, :path, :sections, :reviews
|
301
|
+
|
302
|
+
def initialize(id, args)
|
303
|
+
#Set indifferent access for args
|
304
|
+
args.default_proc = proc do |h, k|
|
305
|
+
case k
|
306
|
+
when String then sym = k.to_sym; h[sym] if h.key?(sym)
|
307
|
+
when Symbol then str = k.to_s; h[str] if h.key?(str)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
#Assign args. ID is necessary because that's how we look up Instructors in the PCR API.
|
312
|
+
if id.is_a? String
|
313
|
+
@id = id
|
314
|
+
else
|
315
|
+
raise InstructorError("Invalid Instructor ID specified.")
|
316
|
+
end
|
317
|
+
|
318
|
+
@name = args[:name].downcase.titlecase if args[:name].is_a? String
|
319
|
+
@path = args[:path] if args[:path].is_a? String
|
320
|
+
@sections = args[:sections] if args[:sections].is_a? Hash
|
321
|
+
|
322
|
+
#Hit PCR API to get missing info, if requested
|
323
|
+
if args[:hit_api] == true
|
324
|
+
self.getInfo
|
325
|
+
self.getReviews
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
#Hit the PCR API to get all missing info
|
330
|
+
#Separate method in case we want to conduct it separately from a class init
|
331
|
+
def getInfo
|
332
|
+
pcr = PCR::API.new()
|
333
|
+
api_url = pcr.api_endpt + "instructors/" + self.id + "?token=" + pcr.token
|
334
|
+
json = JSON.parse(open(api_url).read)
|
335
|
+
|
336
|
+
@name = json["result"]["name"].downcase.titlecase unless @name
|
337
|
+
@path = json["result"]["path"] unless @path
|
338
|
+
@sections = json["result"]["reviews"]["values"] unless @sections #Mislabeled reviews in PCR API
|
339
|
+
end
|
340
|
+
|
341
|
+
#Separate method for getting review data in case we don't want to make an extra API hit each init
|
342
|
+
def getReviews
|
343
|
+
if not self.reviews #make sure we don't already have reviews
|
344
|
+
pcr = PCR::API.new()
|
345
|
+
api_url = pcr.api_endpt + "instructors/" + self.id + "/reviews?token=" + pcr.token
|
346
|
+
json = JSON.parse(open(api_url).read)
|
347
|
+
|
348
|
+
@reviews = json["result"]["values"] #gets array
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
#Get average value of a certain rating for Instructor
|
353
|
+
def average(metric)
|
354
|
+
#Ensure that we know argument type
|
355
|
+
if metric.is_a? Symbol
|
356
|
+
metric = metric.to_s
|
357
|
+
end
|
358
|
+
|
359
|
+
if metric.is_a? String
|
360
|
+
#Loop vars
|
361
|
+
total = 0
|
362
|
+
n = 0
|
363
|
+
|
364
|
+
#For each section, check if ratings include metric arg -- if so, add metric rating to total and increment counting variable
|
365
|
+
self.getReviews
|
366
|
+
self.reviews.each do |review|
|
367
|
+
ratings = review["ratings"]
|
368
|
+
if ratings.include? metric
|
369
|
+
total = total + review["ratings"][metric].to_f
|
370
|
+
n = n + 1
|
371
|
+
else
|
372
|
+
raise CourseError, "No ratings found for \"#{metric}\" for #{self.name}."
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
#Return average score as a float
|
377
|
+
return (total/n)
|
378
|
+
|
379
|
+
else
|
380
|
+
raise CourseError, "Invalid metric format. Metric must be a string or symbol."
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
#Get most recent value of a certain rating for Instructor
|
385
|
+
def recent(metric)
|
386
|
+
#Ensure that we know argument type
|
387
|
+
if metric.is_a? Symbol
|
388
|
+
metric = metric.to_s
|
389
|
+
end
|
390
|
+
|
391
|
+
if metric.is_a? String
|
392
|
+
#Iterate through reviews and create Section for each section reviewed, presented in an array
|
393
|
+
sections = []
|
394
|
+
section_ids = []
|
395
|
+
self.getReviews
|
396
|
+
self.reviews.each do |review|
|
397
|
+
if section_ids.index(review["section"]["id"].to_i).nil?
|
398
|
+
s = PCR::Section.new(:id => review["section"]["id"].to_i, :hit_api => false)
|
399
|
+
sections << s
|
400
|
+
section_ids << s.id
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
#Get only most recent Section(s) in the array
|
405
|
+
sections.reverse! #Newest first
|
406
|
+
targets = []
|
407
|
+
sections.each do |s|
|
408
|
+
s.hit_api(:get_reviews => true)
|
409
|
+
if sections.index(s) == 0
|
410
|
+
targets << s
|
411
|
+
elsif s.semester == sections[0].semester and s.id != sections[0].id
|
412
|
+
targets << s
|
413
|
+
else
|
414
|
+
break
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
#Calculate recent rating
|
419
|
+
total = 0
|
420
|
+
num = 0
|
421
|
+
targets.each do |section|
|
422
|
+
#Make sure we get the rating for the right Instructor
|
423
|
+
section.ratings.each do |rating|
|
424
|
+
if rating.key?(self.id)
|
425
|
+
if rating[self.id][metric].nil?
|
426
|
+
raise InstructorError, "No ratings found for #{metric} for #{self.name}."
|
427
|
+
else
|
428
|
+
total = total + rating[self.id][metric].to_f
|
429
|
+
num += 1
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
return total / num
|
436
|
+
|
437
|
+
else
|
438
|
+
raise CourseError, "Invalid metric format. Metric must be a string or symbol."
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
end #module
|
444
|
+
|
445
|
+
#Some instance methods to handle instructor searching
|
446
|
+
def downloadInstructors(instructors_db)
|
447
|
+
pcr = PCR::API.new()
|
448
|
+
api_url = pcr.api_endpt + "instructors/" + "?token=" + pcr.token
|
449
|
+
#puts "Downloading instructors json..."
|
450
|
+
json = JSON.parse(open(api_url).read)
|
451
|
+
|
452
|
+
#Parse api data, writing to file
|
453
|
+
begin
|
454
|
+
File.open(instructors_db, 'w') do |f|
|
455
|
+
instructor_hashes = json["result"]["values"]
|
456
|
+
file_lines = []
|
457
|
+
#puts "Constructing instructor file_lines"
|
458
|
+
instructor_hashes.each do |instructor|
|
459
|
+
n = instructor["name"].split(" ")
|
460
|
+
file_lines << ["#{n[2]} #{n[1]} #{n[0]}",instructor["id"]] if n.length == 3 #if instructor has middle name
|
461
|
+
file_lines << ["#{n[1]} #{n[0]}",instructor["id"]] if n.length == 2 #if instructor does not have middle name
|
462
|
+
end
|
463
|
+
|
464
|
+
#Sort lines alphabetically
|
465
|
+
#puts "sorting file lines alphabetically..."
|
466
|
+
file_lines.sort! { |a,b| a[0] <=> b[0] }
|
467
|
+
|
468
|
+
#Write lines to csv file
|
469
|
+
#puts "writing file lines to csv file..."
|
470
|
+
file_lines.each { |line| f.write("#{line[0]},#{line[1]}\n") }
|
471
|
+
end
|
472
|
+
rescue IOError => e
|
473
|
+
puts "Could not write to instructors file"
|
474
|
+
rescue Errno::ENOENT => e
|
475
|
+
puts "Could not open instructors file"
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
def instructorSearch(args)
|
480
|
+
#Set indifferent access for args
|
481
|
+
args.default_proc = proc do |h, k|
|
482
|
+
case k
|
483
|
+
when String then sym = k.to_sym; h[sym] if h.key?(sym)
|
484
|
+
when Symbol then str = k.to_s; h[str] if h.key?(str)
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
#Set args
|
489
|
+
first_name = args[:first_name]
|
490
|
+
middle_initial = args[:middle_initial]
|
491
|
+
last_name = args[:last_name]
|
492
|
+
|
493
|
+
#Check if we've downloaded instructors in last week
|
494
|
+
begin
|
495
|
+
last_dl_time = Time.local(File.mtime("instructors.txt").tv_sec).tv_sec
|
496
|
+
#puts last_dl_time
|
497
|
+
rescue Errno::ENOENT => e
|
498
|
+
downloadInstructors("instructors.txt") #instructors file doesn't exist, so download
|
499
|
+
else
|
500
|
+
current_time = Time.local(Time.now().tv_sec).tv_sec
|
501
|
+
#puts current_time
|
502
|
+
if current_time - last_dl_time <= 604800 #1 week in seconds
|
503
|
+
downloadInstructors("instructors.txt")
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
#Check if instructors file exists
|
508
|
+
# begin
|
509
|
+
# f = File.open("instructors.txt", "rb")
|
510
|
+
# rescue Errno::ENOENT => e
|
511
|
+
# downloadInstructors("instructors.txt")
|
512
|
+
# end
|
513
|
+
|
514
|
+
#Search for instructor name in instructors file and get corresponding ids, in an array
|
515
|
+
#puts "searching instructors file..."
|
516
|
+
results = []
|
517
|
+
CSV.foreach("instructors.txt") do |line|
|
518
|
+
results << {line[0] => line[1]} if line[0].include? last_name.upcase
|
519
|
+
end
|
520
|
+
|
521
|
+
return results
|
522
|
+
end
|
data/pcr-ruby.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/pcr-ruby/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = "Matt Parmett"
|
6
|
+
gem.email = "parm289@yahoo.com"
|
7
|
+
gem.description = %q{Ruby wrapper for the Penn Course Review API}
|
8
|
+
gem.summary = %q{Ruby wrapper for the Penn Course Review API}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "pcr-ruby"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Pcr::Ruby::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency "json"
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pcr-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Matt Parmett
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: json
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: Ruby wrapper for the Penn Course Review API
|
31
|
+
email: parm289@yahoo.com
|
32
|
+
executables: []
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- .gitignore
|
37
|
+
- Gemfile
|
38
|
+
- LICENSE
|
39
|
+
- README.md
|
40
|
+
- Rakefile
|
41
|
+
- lib/pcr-ruby.rb
|
42
|
+
- lib/pcr-ruby/version.rb
|
43
|
+
- pcr-ruby.gemspec
|
44
|
+
homepage: ''
|
45
|
+
licenses: []
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirements: []
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.8.23
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: Ruby wrapper for the Penn Course Review API
|
68
|
+
test_files: []
|