engrade 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ spec/fixtures
19
+ config/environment.rb
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use default@engrade
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in engrade.gemspec
4
+ gemspec
@@ -0,0 +1,7 @@
1
+ # More info at https://github.com/guard/guard#readme
2
+
3
+ guard 'rspec', :cli => "--color -fd" do
4
+ watch(%r{^spec/.+_spec\.rb$})
5
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
6
+ watch('spec/spec_helper.rb') { "spec" }
7
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 TODO: Write your name
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.
@@ -0,0 +1,29 @@
1
+ # Engrade::Ruby
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'engrade-ruby'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install engrade-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 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec) do |spec|
6
+ spec.pattern = 'spec/*_spec.rb'
7
+ end
8
+
9
+ task :default => :spec
@@ -0,0 +1,4 @@
1
+ ENV['ENG_USER'] = 'username
2
+ ENV['ENG_PASS'] = 'password'
3
+ ENV['ENG_API'] = 'apikey'
4
+ ENV['ENG_SES'] = 'sample ses token'
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'engrade/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "engrade"
8
+ gem.version = Engrade::VERSION
9
+ gem.authors = ["Zachary Thompson"]
10
+ gem.email = ["zgthompson@gmail.com"]
11
+ gem.description = %q{Basic ruby wrapper for the Engrade API}
12
+ gem.summary = %q{Utilize the Engrade API easily through Ruby}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_runtime_dependency 'rest-client'
21
+ gem.add_runtime_dependency 'mechanize'
22
+ gem.add_development_dependency 'webmock'
23
+ gem.add_development_dependency 'vcr'
24
+ gem.add_development_dependency 'rake'
25
+ gem.add_development_dependency 'simplecov'
26
+ gem.add_development_dependency 'guard'
27
+ gem.add_development_dependency 'guard-rspec'
28
+ gem.add_development_dependency 'factory_girl'
29
+ gem.add_development_dependency 'rb-inotify'
30
+ end
@@ -0,0 +1,141 @@
1
+ require 'rest_client'
2
+ require 'json'
3
+ require_relative 'engrade/version'
4
+ require_relative 'engrade/exceptions'
5
+ require_relative 'engrade/return_types'
6
+ require_relative 'engrade/browser'
7
+
8
+ module Engrade
9
+
10
+ ###########################
11
+ # VARIABLES AND CONSTANTS #
12
+ ###########################
13
+ #
14
+ @default_params = { :api => 'json' }
15
+ @browser = Engrade::Browser.new
16
+
17
+ def self.base_uri; 'https://api.engrade.com/api/' end
18
+
19
+ ###########################
20
+ # GETTERS AND (RE)SETTERS #
21
+ ###########################
22
+
23
+ # default_params are included in all post requests
24
+ def self.default_params; @default_params end
25
+
26
+
27
+ # browser is used to extend the engrade api to allow for the removal of
28
+ # assignment comments
29
+ def self.browser; @browser end
30
+
31
+
32
+ def self.reset!
33
+ @default_params = { :api => 'json' }
34
+ end
35
+
36
+
37
+ def self.set_ses(ses)
38
+ @default_params.store :ses, ses
39
+ end
40
+
41
+ ######################
42
+ # CORE FUNCTIONALITY #
43
+ ######################
44
+
45
+ # .post takes merges the default_params with query, a hash of input fields,
46
+ # and posts them to the api
47
+ # Example:
48
+ # Engrade.post({:apitask => 'login', :usr => 'myusername', :pwd => 'secret'})
49
+ def self.post(query={})
50
+ response = RestClient.post base_uri, query.merge(@default_params)
51
+ raise InvalidKey, 'Invalid apikey' if response.match("Invalid apikey")
52
+ raise MissingTask, 'No apitask specified' if response.match("No apitask specified")
53
+ raise InvalidTask, 'Invalid apitask' if response.match("Invalid apitask")
54
+ raise InvalidSession, 'Not logged in' if response.match("Not logged in")
55
+ response
56
+ end
57
+
58
+
59
+ def self.set_apikey(key)
60
+ @default_params.store :apikey, key
61
+ end
62
+
63
+
64
+ # .login posts a valid username and password to the api, and stores the
65
+ # responding session token in default_params
66
+ def self.login(u, p)
67
+ response = post :apitask => 'login', :usr => u, :pwd => p
68
+ raise InvalidLogin, 'Invalid username/password' if response.
69
+ match(/Invalid (username|password)/)
70
+ @browser.login u, p
71
+ @default_params.store :ses, JSON(response)['values']['ses']
72
+ end
73
+
74
+
75
+ # .classes returns an array of Classroom objects(data structure to represent
76
+ # classes). if called with a regexp, it will only return the classes whose
77
+ # name matches that regexp. if called with no argument, it will return all
78
+ # classes.
79
+ def self.classes(regexp=nil)
80
+ classes = teacher_classes
81
+ classes.select! { |cl| cl['name'].match regexp } if regexp
82
+ classes.map { |cl| Classroom.new(cl) }
83
+ end
84
+
85
+
86
+ # .assignments returns an array(of Assignment objects) of every assignment
87
+ # from the input classes. the classes parameter can be the class id, a
88
+ # Classroom object, an array of class ids, or an array of Classroom objects.
89
+ def self.assignments(classes)
90
+ assignments = []
91
+ clid_array(classes).each do |clid|
92
+ (Engrade.gradebook :clid => clid).
93
+ map { |assn| assignments << Assignment.new(assn) }
94
+ end
95
+ assignments
96
+ end
97
+
98
+
99
+ # .delete takes in an array of Assignment object, or a single Assignment
100
+ # object and deletes those assignments from the Engrade webpage. it also
101
+ # removes the comments from those assignments before deleting, because of
102
+ # a bug in the Engrade system where comments from deleted assignments
103
+ # reappear when assignment ids are recycled.
104
+ def self.delete(assignments)
105
+ assignments = array(assignments)
106
+ assignments.each do |assn|
107
+ @browser.remove_comments assn.clid, assn.assnid
108
+ post :apitask => 'assignment-edit', :clid => assn.clid, :assnid => assn.assnid, :delete => '1'
109
+ end
110
+ end
111
+
112
+ ##################
113
+ # HELPER METHODS #
114
+ ##################
115
+
116
+ def self.teacher_classes(query={})
117
+ JSON(post :apitask => 'teacher-classes')['classes']
118
+ end
119
+
120
+
121
+ def self.gradebook(query={})
122
+ response = post({ :apitask => 'gradebook' }.merge query)
123
+ raise InvalidClassId, 'Invalid class' if response.
124
+ match("You are not authorized to view this class")
125
+ JSON(response)['assignments']
126
+ end
127
+
128
+
129
+ def self.array(input)
130
+ input = input.instance_of?(Array) ? input : [input]
131
+ end
132
+
133
+
134
+ def self.clid_array(classes)
135
+ classes = array(classes)
136
+ classes.map! { |cl| cl.clid } if classes.first.instance_of? Classroom
137
+ classes
138
+ end
139
+
140
+ end
141
+
@@ -0,0 +1,28 @@
1
+ require 'mechanize'
2
+
3
+ module Engrade
4
+
5
+ class Browser
6
+
7
+ def initialize
8
+ @agent
9
+ end
10
+
11
+ def login(u, p)
12
+ @agent = Mechanize.new
13
+ form = @agent.get('https://www.engrade.com/user/login.php').forms.first
14
+ form.usr = u
15
+ form.pwd = p
16
+ @agent.submit(form)
17
+ end
18
+
19
+ def remove_comments(clid, assnid)
20
+ page = @agent.get "https://www.engrade.com/class/assignments/edit.php?assnid=#{assnid}&clid=#{clid}"
21
+ scores = page.form.fields_with(:name => /^score/)
22
+ scores.each { |score| score.value = "" }
23
+ @agent.submit(page.form)
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,11 @@
1
+ module Engrade
2
+
3
+ class InvalidKey < StandardError; end
4
+ class InvalidLogin < StandardError; end
5
+ class InvalidSession < StandardError; end
6
+ class MissingTask < StandardError; end
7
+ class InvalidTask < StandardError; end
8
+ class InvalidClassId < StandardError; end
9
+
10
+ end
11
+
@@ -0,0 +1,8 @@
1
+ require 'ostruct'
2
+
3
+ module Engrade
4
+
5
+ class Classroom < OpenStruct; end
6
+ class Assignment < OpenStruct; end
7
+
8
+ end
@@ -0,0 +1,3 @@
1
+ module Engrade
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,359 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe Engrade do
4
+
5
+ after :each do
6
+ Engrade.reset!
7
+ end
8
+
9
+ it "should have base_uri set to engrade api endpoint" do
10
+ Engrade.base_uri.should eql 'https://api.engrade.com/api/'
11
+ end
12
+
13
+ describe "public method" do
14
+
15
+ describe ".default_params" do
16
+
17
+ it "should be a Hash" do
18
+ Engrade.default_params.should be_an_instance_of Hash
19
+ end
20
+
21
+ it "should have :api set to json" do
22
+ Engrade.default_params[:api].should eql 'json'
23
+ end
24
+
25
+ end
26
+
27
+ describe ".set_apikey" do
28
+
29
+ it "should set :apikey in default_params" do
30
+ Engrade.set_apikey 'test'
31
+ Engrade.default_params[:apikey].should eql 'test'
32
+ end
33
+
34
+ end
35
+
36
+ describe ".set_ses" do
37
+
38
+ it "should set :ses in default_params" do
39
+ Engrade.set_ses 'test'
40
+ Engrade.default_params[:ses].should eql 'test'
41
+ end
42
+
43
+ end
44
+
45
+ describe ".reset!" do
46
+
47
+ it "should reset default_params to original value" do
48
+ orig = Engrade.default_params.dup
49
+ Engrade.set_apikey "12345"
50
+ Engrade.reset!
51
+ Engrade.default_params.should eql orig
52
+ end
53
+
54
+ end
55
+
56
+ describe ".post" do
57
+
58
+ before :each do
59
+ Engrade.set_apikey ENV['ENG_API']
60
+ end
61
+
62
+ it "should call RestClient.post with query and default_params" do
63
+ response = build :login
64
+ RestClient.should_receive(:post).with(Engrade.base_uri, response[:payload]).
65
+ and_return response[:body]
66
+ Engrade.post response[:query]
67
+ end
68
+
69
+ context "with valid call" do
70
+
71
+ it "should return the result of RestClient.post" do
72
+ response = build :login
73
+ RestClient.stub(:post).and_return response[:body]
74
+ Engrade.post(response[:query]).should eql response[:body]
75
+ end
76
+
77
+ end
78
+
79
+ context "when not logged in" do
80
+
81
+ it "should raise an InvalidSession error" do
82
+ RestClient.stub(:post).and_return build(:bad_ses)[:body]
83
+ expect { Engrade.post :apitask => 'teacher-classes' }.
84
+ to raise_error Engrade::InvalidSession
85
+ end
86
+
87
+ end
88
+
89
+ context "with bad apikey" do
90
+
91
+ it "should raise an InvalidKey error" do
92
+ Engrade.set_apikey 'badapi'
93
+ RestClient.stub(:post).and_return build(:bad_api)[:body]
94
+ expect { Engrade.post }.to raise_error Engrade::InvalidKey
95
+ end
96
+
97
+ end
98
+
99
+ context "with no apitask specified" do
100
+
101
+ it "should raise a MissingTask error" do
102
+ RestClient.stub(:post).and_return build(:no_task)[:body]
103
+ expect { Engrade.post }.to raise_error Engrade::MissingTask
104
+ end
105
+
106
+ end
107
+
108
+ context "with invalid apitask specified" do
109
+
110
+ it "should raise an InvalidTask error" do
111
+ RestClient.stub(:post).and_return build(:bad_task)[:body]
112
+ expect { Engrade.post :apitask => 'badtask' }.
113
+ to raise_error Engrade::InvalidTask
114
+ end
115
+
116
+ end
117
+
118
+ end
119
+
120
+ describe ".login" do
121
+
122
+ before :each do
123
+ Engrade.browser.stub :login
124
+ end
125
+
126
+ it "should call .post with query Hash" do
127
+ response = build :login
128
+ Engrade.should_receive(:post).with(response[:query]).
129
+ and_return response[:body]
130
+ Engrade.login ENV['ENG_USER'], ENV['ENG_PASS']
131
+ end
132
+
133
+ context "with valid credentials" do
134
+
135
+ before :each do
136
+ @response = build(:login)
137
+ Engrade.stub(:post).and_return @response[:body]
138
+ end
139
+
140
+ it "should return the session id" do
141
+ Engrade.login(ENV['ENG_USER'], ENV['ENG_PASS']).
142
+ should eql @response[:json]['values']['ses']
143
+ end
144
+
145
+ it "should set :ses in the default params" do
146
+ ses = Engrade.login ENV['ENG_USER'], ENV['ENG_PASS']
147
+ Engrade.default_params[:ses].should eql ses
148
+ end
149
+
150
+ end
151
+
152
+ context "with invalid username" do
153
+
154
+ it "should raise InvalidLogin error" do
155
+ Engrade.stub(:post).and_return build(:login_baduser)[:body]
156
+ expect { Engrade.login 'baduser', 'anypass' }.to raise_error Engrade::InvalidLogin
157
+ end
158
+
159
+ end
160
+
161
+ context "with invalid password" do
162
+
163
+ it "should raise InvalidLogin error" do
164
+ Engrade.stub(:post).and_return build(:login_badpass)[:body]
165
+ expect { Engrade.login ENV['ENG_USER'], 'badpass' }.to raise_error Engrade::InvalidLogin
166
+ end
167
+
168
+ end
169
+
170
+ end
171
+
172
+ describe ".classes" do
173
+
174
+ before :each do
175
+ @classes = build(:teacher_classes)[:classes]
176
+ Engrade.stub(:teacher_classes).and_return @classes
177
+ end
178
+
179
+
180
+ it "should call .teacher_classes with the proper query" do
181
+ Engrade.should_receive(:teacher_classes).with(no_args).
182
+ and_return @classes
183
+ Engrade.classes
184
+ end
185
+
186
+
187
+ describe "return value" do
188
+
189
+ it "should be an Array" do
190
+ Engrade.classes.should be_an_instance_of Array
191
+ end
192
+
193
+ it "should contain Classroom objects" do
194
+ Engrade.classes.first.should be_an_instance_of Engrade::Classroom
195
+ end
196
+
197
+ it "should be filtered when given a string argument" do
198
+ Engrade.classes.size.should be > Engrade.classes("Sem1").size
199
+ end
200
+
201
+ it "should contain classes that match the string argument" do
202
+ Engrade.classes("Sem1").first.name.should match "Sem1"
203
+ end
204
+
205
+ end
206
+
207
+ end
208
+
209
+ describe ".assignments" do
210
+
211
+ before :each do
212
+ @response = build :gradebook
213
+ Engrade.stub(:gradebook).and_return @response[:assignments]
214
+ end
215
+
216
+ it "should take a class id as input" do
217
+ expect { Engrade.assignments((build :classroom).clid) }.to_not raise_error
218
+ end
219
+
220
+ it "should take a classroom object as input" do
221
+ expect { Engrade.assignments(build :classroom) }.to_not raise_error
222
+ end
223
+
224
+ it "should take an array of classroom objects as input" do
225
+ expect { Engrade.assignments [build(:classroom), build(:classroom)] }.
226
+ to_not raise_error
227
+ end
228
+
229
+ it "should take an array of clids as input" do
230
+ expect { Engrade.assignments [(build :classroom).clid, (build :classroom).clid] }.
231
+ to_not raise_error
232
+ end
233
+
234
+ it "should call .gradebook for each class from input array" do
235
+ classes = [build(:classroom), build(:classroom)]
236
+ Engrade.should_receive(:gradebook).exactly(2).times
237
+ Engrade.assignments classes
238
+ end
239
+
240
+
241
+ it "should call .gradebook with the appropriate query" do
242
+ Engrade.should_receive(:gradebook).with @response[:public_query]
243
+ Engrade.assignments build(:classroom)
244
+ end
245
+
246
+
247
+ describe "return value" do
248
+
249
+ it "should be an Array" do
250
+ Engrade.assignments(build :classroom).should be_an_instance_of Array
251
+ end
252
+
253
+ it "should contain Assignment objects" do
254
+ Engrade.assignments(build :classroom).first.
255
+ should be_an_instance_of Engrade::Assignment
256
+ end
257
+
258
+ it "should contain assigments from all input classes" do
259
+ Engrade.assignments(build :classroom).size.should be < Engrade.assignments([build(:classroom), build(:classroom)]).size
260
+ end
261
+
262
+ end
263
+
264
+ end
265
+
266
+ describe ".delete" do
267
+
268
+ before :each do
269
+ Engrade.set_ses ENV['ENG_SES']
270
+ Engrade.stub :post
271
+ Engrade.browser.stub :remove_comments
272
+ end
273
+
274
+ it "should accept an array of Assignments as input" do
275
+ assignments = [build(:assignment), build(:assignment)]
276
+ expect { Engrade.delete assignments }.to_not raise_error
277
+ end
278
+
279
+ it "should accept a single Assignment as input" do
280
+ assignment = build :assignment
281
+ expect { Engrade.delete assignment }.to_not raise_error
282
+ end
283
+
284
+ it "should remove comments of every assignment" do
285
+ assignments = [build(:assignment), build(:assignment)]
286
+ Engrade.browser.should_receive(:remove_comments).exactly(2).times
287
+ Engrade.delete assignments
288
+ end
289
+
290
+ it "should call .post with proper query" do
291
+ Engrade.should_receive(:post).with build(:delete)[:query]
292
+ Engrade.delete [build(:assignment)]
293
+ end
294
+
295
+ end
296
+
297
+ end
298
+
299
+ describe "helper method" do
300
+
301
+
302
+ describe ".teacher_classes" do
303
+
304
+ before :each do
305
+ Engrade.set_apikey ENV['ENG_API']
306
+ Engrade.set_ses ENV['ENG_SES']
307
+ @response = build :teacher_classes
308
+ Engrade.stub :classes
309
+ end
310
+
311
+ it "should call .post with the proper query" do
312
+ Engrade.should_receive(:post).with(@response[:helper_query]).
313
+ and_return @response[:body]
314
+ Engrade.teacher_classes
315
+ end
316
+
317
+ it "should return an array of classes" do
318
+ Engrade.stub(:post).and_return @response[:body]
319
+ Engrade.teacher_classes.should eql @response[:classes]
320
+ end
321
+
322
+ end
323
+
324
+ describe ".gradebook" do
325
+
326
+ before :each do
327
+ Engrade.set_apikey ENV['ENG_API']
328
+ Engrade.set_ses ENV['ENG_SES']
329
+ @response = build :gradebook
330
+ Engrade.stub :assignments
331
+ end
332
+
333
+ it "should call .post with appropriate query" do
334
+ Engrade.should_receive(:post).with(@response[:helper_query]).
335
+ and_return @response[:body]
336
+ Engrade.gradebook :clid => build(:classroom).clid
337
+ end
338
+
339
+ it "should return an array of assignments" do
340
+ Engrade.stub(:post).and_return @response[:body]
341
+ Engrade.gradebook(:clid => build(:classroom).clid).
342
+ should eql @response[:assignments]
343
+ end
344
+
345
+ context "with bad class id" do
346
+
347
+ it "should raise a InvalidClassId error" do
348
+ Engrade.stub(:post).and_return build(:bad_class)[:body]
349
+ expect { Engrade.gradebook(:clid => build(:bad_classroom).clid) }.
350
+ to raise_error Engrade::InvalidClassId
351
+ end
352
+
353
+ end
354
+
355
+ end
356
+
357
+ end
358
+
359
+ end
@@ -0,0 +1,10 @@
1
+ require 'factory_girl'
2
+
3
+ FactoryGirl.define do
4
+
5
+ factory :assignment, class:Engrade::Assignment do
6
+ clid "101"
7
+ assnid "1"
8
+ end
9
+
10
+ end
@@ -0,0 +1,13 @@
1
+ require 'factory_girl'
2
+
3
+ FactoryGirl.define do
4
+
5
+ factory :classroom, class:Engrade::Classroom do
6
+ clid "101"
7
+ end
8
+
9
+ factory :bad_classroom, class:Engrade::Classroom do
10
+ clid "123456"
11
+ end
12
+
13
+ end
@@ -0,0 +1,96 @@
1
+ require 'factory_girl'
2
+
3
+ FactoryGirl.define do
4
+
5
+ factory :login, class:Hash do
6
+ body "{\"fields\":\"usr pwd remember go\",\"success\":true,\"values\":{\"usr\":\"zgthompson\",\"pwd\":\"secretpass\",\"remember\":null,\"go\":null,\"ses\":\"9426016804785795792n8k42bbalb40m8e58j4n0faa98d03o0958kfmn98019pa\"},\"time\":1363650224}"
7
+ json { JSON(body) }
8
+ query { { :usr => ENV['ENG_USER'], :pwd => ENV['ENG_PASS'], :apitask => 'login' } }
9
+ payload { { :api => 'json', :apikey => ENV['ENG_API'] }.merge query }
10
+
11
+ initialize_with { attributes }
12
+ end
13
+
14
+ factory :login_baduser, class:Hash do
15
+ body "{\"fields\":\"usr pwd remember go\",\"success\":false,\"values\":{\"usr\":\"baduser\",\"pwd\":\"anypass\",\"remember\":null,\"go\":null},\"errors\":{\"usr\":\"Invalid username\"},\"time\":1363653753}"
16
+ json { JSON(body) }
17
+ query { { :usr => 'baduser', :pwd => 'anypass', :apitask => 'login' } }
18
+ payload { { :api => 'json', :apikey => ENV['ENG_API'] }.merge query }
19
+
20
+ initialize_with { attributes }
21
+ end
22
+
23
+ factory :login_badpass, class:Hash do
24
+ body "{\"fields\":\"usr pwd remember go\",\"success\":false,\"values\":{\"usr\":\"zgthompson\",\"pwd\":\"badpass\",\"remember\":null,\"go\":null},\"errors\":{\"pwd\":\"Invalid password\"},\"time\":1363654455}"
25
+ json { JSON(body) }
26
+ query { { :usr => 'zgthompson', :pwd => 'badpass', :apitask => 'login' } }
27
+ payload { { :api => 'json', :apikey => ENV['ENG_API'] }.merge query }
28
+
29
+ initialize_with { attributes }
30
+ end
31
+
32
+ factory :bad_api, class:Hash do
33
+ body "Invalid apikey, please contact Engrade to receive your API key.badapi"
34
+ query { {} }
35
+ payload { { :api => 'json', :apikey => 'badapi' } }
36
+
37
+ initialize_with { attributes }
38
+ end
39
+
40
+ factory :teacher_classes, class:Hash do
41
+ body "{\"success\":true,\"classes\":[{\"superid\":\"1000008059883\",\"subid\":\"101\",\"supertype\":\"teacher\",\"subtype\":\"class\",\"auth\":6,\"joined\":1361653042,\"role\":\"Teacher\",\"clid\":\"101\",\"created\":1361653042,\"folder\":3,\"syr\":\"2013-2014\",\"gp\":1,\"gscale\":9,\"weighted\":0,\"showper\":1,\"stusort\":1,\"assnsort\":1,\"round\":1,\"stuview\":1,\"stumail\":0,\"gradelevel\":7,\"subject\":5,\"bb\":0,\"teachmail\":1,\"priteach\":\"1000008059883\",\"name\":\"Biology\",\"code\":null,\"stucount\":6,\"average\":68.3,\"missing\":0,\"failing\":1,\"registered\":0,\"acount\":1,\"stdset\":0,\"updated\":1362535927},{\"superid\":\"1000008059883\",\"subid\":\"5000006291841\",\"supertype\":\"teacher\",\"subtype\":\"class\",\"auth\":6,\"joined\":1363580907,\"role\":\"Teacher\",\"clid\":\"5000006291841\",\"created\":1363580907,\"folder\":3,\"syr\":\"2013-2014\",\"gp\":1,\"gscale\":9,\"weighted\":0,\"showper\":1,\"stusort\":1,\"assnsort\":1,\"round\":1,\"stuview\":1,\"stumail\":0,\"gradelevel\":99,\"subject\":99,\"bb\":0,\"teachmail\":1,\"priteach\":\"1000008059883\",\"name\":\"Chemistry - Sem1\",\"code\":null,\"stucount\":2,\"average\":0,\"missing\":0,\"failing\":0,\"registered\":0,\"acount\":0,\"stdset\":0,\"updated\":1363580917},{\"superid\":\"1000008059883\",\"subid\":\"5000006291845\",\"supertype\":\"teacher\",\"subtype\":\"class\",\"auth\":6,\"joined\":1363580934,\"role\":\"Teacher\",\"clid\":\"5000006291845\",\"created\":1363580934,\"folder\":3,\"syr\":\"2013-2014\",\"gp\":1,\"gscale\":9,\"weighted\":0,\"showper\":1,\"stusort\":1,\"assnsort\":1,\"round\":1,\"stuview\":1,\"stumail\":0,\"gradelevel\":99,\"subject\":99,\"bb\":0,\"teachmail\":1,\"priteach\":\"1000008059883\",\"name\":\"Math - Sem2\",\"code\":null,\"stucount\":2,\"average\":0,\"missing\":0,\"failing\":0,\"registered\":0,\"acount\":0,\"stdset\":0,\"updated\":1363580941}],\"time\":1363655149}"
42
+ json { JSON(body) }
43
+ classes { json['classes'] }
44
+ helper_query { { :apitask => 'teacher-classes' } }
45
+ payload { { :api => 'json', :apikey => ENV['ENG_API'], :ses => ENV['ENG_SES'] }.merge helper_query }
46
+
47
+ initialize_with { attributes }
48
+ end
49
+
50
+ factory :bad_ses, class:Hash do
51
+ body "{\"success\":false,\"errors\":{\"system\":\"Not logged in\"},\"time\":1363655473}"
52
+ query { { :apitask => 'teacher-classes' } }
53
+ payload { { :api => 'json', :apikey => ENV['ENG_API'] }.merge query }
54
+ initialize_with { attributes }
55
+ end
56
+
57
+ factory :no_task, class:Hash do
58
+ body "No apitask specified"
59
+ query { {} }
60
+ payload { { :api => 'json', :apikey => ENV['ENG_API'] } }
61
+ initialize_with { attributes }
62
+ end
63
+
64
+ factory :bad_task, class:Hash do
65
+ body "Invalid apitask"
66
+ query { { :apitask => 'badtask' } }
67
+ payload { { :api => 'json', :apikey => ENV['ENG_API'] }.merge query }
68
+ initialize_with { attributes }
69
+ end
70
+
71
+ factory :gradebook, class:Hash do
72
+ body "{\"clid\":\"101\",\"created\":1361653042,\"folder\":3,\"syr\":\"2013-2014\",\"gp\":1,\"gscale\":9,\"weighted\":0,\"showper\":1,\"stusort\":1,\"assnsort\":1,\"round\":1,\"stuview\":1,\"stumail\":0,\"gradelevel\":7,\"subject\":5,\"bb\":0,\"teachmail\":1,\"priteach\":\"1000008059883\",\"name\":\"Biology\",\"code\":null,\"stucount\":6,\"average\":69.3,\"missing\":0,\"failing\":1,\"registered\":0,\"acount\":2,\"stdset\":0,\"updated\":1363837018,\"cgs\":[{\"num\":1,\"low\":90,\"grade\":\"A\"},{\"num\":2,\"low\":80,\"grade\":\"B\"},{\"num\":3,\"low\":70,\"grade\":\"C\"},{\"num\":4,\"low\":60,\"grade\":\"D\"},{\"num\":5,\"low\":0,\"grade\":\"F\"}],\"cat\":[{\"num\":1,\"name\":\"Assignments\",\"weight\":null,\"dls\":0}],\"assignments\":[{\"clid\":\"101\",\"assnid\":2,\"created\":1363837018,\"creator\":\"1000008059883\",\"type\":1,\"cat\":1,\"cal\":1,\"bb\":1,\"turnin\":0,\"lastreplier\":0,\"lastreply\":0,\"replies\":0,\"attachments\":0,\"due\":20130320,\"points\":2,\"title\":\"Quiz\",\"gb\":1,\"showstu\":1,\"rubric\":0,\"itemid\":null,\"name\":\"Zachary Thompson\",\"usr\":\"zgthompson\",\"pic\":0},{\"clid\":\"101\",\"assnid\":1,\"created\":1362534970,\"creator\":\"1000008059883\",\"type\":1,\"cat\":1,\"cal\":1,\"bb\":1,\"turnin\":0,\"lastreplier\":0,\"lastreply\":0,\"replies\":0,\"attachments\":0,\"due\":20130305,\"points\":10,\"title\":\"Pop Quiz\",\"gb\":1,\"showstu\":1,\"rubric\":0,\"itemid\":null,\"name\":\"Zachary Thompson\",\"usr\":\"zgthompson\",\"pic\":0}],\"students\":[{\"stuid\":95956,\"num\":1,\"first\":\"George\",\"last\":\"Washington\",\"grade\":\"F\",\"percent\":17,\"missing\":0,\"acount\":2,\"status\":3},{\"stuid\":17406,\"num\":2,\"first\":\"John\",\"last\":\"Adams\",\"grade\":\"B\",\"percent\":83,\"missing\":0,\"acount\":2,\"status\":3},{\"stuid\":12345,\"num\":3,\"first\":\"Bob\",\"last\":\"Barker\",\"grade\":\"B\",\"percent\":83,\"missing\":0,\"acount\":2,\"status\":3},{\"stuid\":13371,\"num\":4,\"first\":\"Mary\",\"last\":\"Popper\",\"grade\":\"B\",\"percent\":83,\"missing\":0,\"acount\":2,\"status\":3},{\"stuid\":12567,\"num\":5,\"first\":\"Mark\",\"last\":\"Zuck\",\"grade\":\"C\",\"percent\":75,\"missing\":0,\"acount\":2,\"status\":3},{\"stuid\":44245,\"num\":6,\"first\":\"Frank\",\"last\":\"Cull\",\"grade\":\"C\",\"percent\":75,\"missing\":0,\"acount\":2,\"status\":3}],\"scores\":[{\"assnid\":1,\"stuid\":12345,\"score\":8},{\"assnid\":1,\"stuid\":12567,\"score\":8},{\"assnid\":1,\"stuid\":13371,\"score\":8},{\"assnid\":1,\"stuid\":17406,\"score\":8},{\"assnid\":1,\"stuid\":44245,\"score\":8},{\"assnid\":1,\"stuid\":95956,\"score\":1},{\"assnid\":2,\"stuid\":12345,\"score\":2},{\"assnid\":2,\"stuid\":12567,\"score\":1},{\"assnid\":2,\"stuid\":13371,\"score\":2},{\"assnid\":2,\"stuid\":17406,\"score\":2},{\"assnid\":2,\"stuid\":44245,\"score\":1},{\"assnid\":2,\"stuid\":95956,\"score\":1}],\"success\":true,\"time\":1363840611}"
73
+ json { JSON(body) }
74
+ assignments { json['assignments'] }
75
+ public_query { { :clid => '101' } }
76
+ helper_query { { :apitask => 'gradebook', :clid => '101' } }
77
+ payload { { :api => 'json', :apikey => ENV['ENG_API'], :ses => ENV['ENG_SES'] }.merge helper_query }
78
+ initialize_with { attributes }
79
+ end
80
+
81
+ factory :bad_class, class:Hash do
82
+ body "{\"success\":false,\"errors\":{\"system\":\"You are not authorized to view this class\"},\"time\":1363843636}"
83
+ query { { :apitask => 'gradebook', :clid => '123456' } }
84
+ payload { { :api => 'json', :apikey => ENV['ENG_API'], :ses => ENV['ENG_SES'] }.merge query }
85
+ initialize_with { attributes }
86
+ end
87
+
88
+ factory :delete, class:Hash do
89
+ query { { :apitask => 'assignment-edit', :delete => '1',
90
+ :clid => '101', :assnid => '1' } }
91
+
92
+ initialize_with { attributes }
93
+ end
94
+
95
+ end
96
+
@@ -0,0 +1,22 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe Engrade do
4
+
5
+ it "should allow me to delete all my assignments" do
6
+ Engrade.set_apikey ENV['ENG_API']
7
+
8
+ VCR.use_cassette('login') { Engrade.login ENV['ENG_USER'], ENV['ENG_PASS'] }
9
+
10
+ VCR.use_cassette('classes') { @classes = Engrade.classes }
11
+
12
+ VCR.use_cassette('assignments') { @assignments = Engrade.assignments @classes }
13
+
14
+ VCR.use_cassette('delete') { Engrade.delete @assignments }
15
+
16
+ VCR.use_cassette('result') { @result = Engrade.assignments @classes }
17
+
18
+ @result.should be_empty
19
+ end
20
+
21
+ end
22
+
@@ -0,0 +1,26 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ #we need the actual library file
5
+ require_relative '../lib/engrade'
6
+ require_relative '../config/environment'
7
+ require 'rspec'
8
+ require 'factory_girl'
9
+ require 'webmock/rspec'
10
+ require 'vcr'
11
+
12
+ FactoryGirl.find_definitions
13
+
14
+ #RSpec config
15
+ RSpec.configure do |spec|
16
+ spec.color_enabled = true
17
+ spec.tty = true
18
+ spec.include FactoryGirl::Syntax::Methods
19
+ end
20
+
21
+ #VCR config
22
+ VCR.configure do |c|
23
+ c.default_cassette_options = { :serialize_with => :json, :preserve_exact_body_bytes => true }
24
+ c.cassette_library_dir = 'spec/fixtures/engrade_cassettes'
25
+ c.hook_into :webmock
26
+ end
metadata ADDED
@@ -0,0 +1,237 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: engrade
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Zachary Thompson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rest-client
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
+ - !ruby/object:Gem::Dependency
31
+ name: mechanize
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: webmock
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: vcr
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: simplecov
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: guard
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: guard-rspec
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: factory_girl
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: rb-inotify
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ description: Basic ruby wrapper for the Engrade API
175
+ email:
176
+ - zgthompson@gmail.com
177
+ executables: []
178
+ extensions: []
179
+ extra_rdoc_files: []
180
+ files:
181
+ - .gitignore
182
+ - .rvmrc
183
+ - Gemfile
184
+ - Guardfile
185
+ - LICENSE.txt
186
+ - README.md
187
+ - Rakefile
188
+ - config/environment.rb.template
189
+ - engrade.gemspec
190
+ - lib/engrade.rb
191
+ - lib/engrade/browser.rb
192
+ - lib/engrade/exceptions.rb
193
+ - lib/engrade/return_types.rb
194
+ - lib/engrade/version.rb
195
+ - spec/engrade_spec.rb
196
+ - spec/factories/assignment_factory.rb
197
+ - spec/factories/classroom_factory.rb
198
+ - spec/factories/response_factory.rb
199
+ - spec/integration_spec.rb
200
+ - spec/spec_helper.rb
201
+ homepage: ''
202
+ licenses: []
203
+ post_install_message:
204
+ rdoc_options: []
205
+ require_paths:
206
+ - lib
207
+ required_ruby_version: !ruby/object:Gem::Requirement
208
+ none: false
209
+ requirements:
210
+ - - ! '>='
211
+ - !ruby/object:Gem::Version
212
+ version: '0'
213
+ segments:
214
+ - 0
215
+ hash: -813889549
216
+ required_rubygems_version: !ruby/object:Gem::Requirement
217
+ none: false
218
+ requirements:
219
+ - - ! '>='
220
+ - !ruby/object:Gem::Version
221
+ version: '0'
222
+ segments:
223
+ - 0
224
+ hash: -813889549
225
+ requirements: []
226
+ rubyforge_project:
227
+ rubygems_version: 1.8.25
228
+ signing_key:
229
+ specification_version: 3
230
+ summary: Utilize the Engrade API easily through Ruby
231
+ test_files:
232
+ - spec/engrade_spec.rb
233
+ - spec/factories/assignment_factory.rb
234
+ - spec/factories/classroom_factory.rb
235
+ - spec/factories/response_factory.rb
236
+ - spec/integration_spec.rb
237
+ - spec/spec_helper.rb