engrade 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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