sakai-info 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +15 -0
- data/README.md +2 -2
- data/ROADMAP.md +7 -11
- data/lib/sakai-info.rb +1 -0
- data/lib/sakai-info/cli/help.rb +46 -3
- data/lib/sakai-info/cli/lookup.rb +35 -5
- data/lib/sakai-info/cli/query.rb +40 -1
- data/lib/sakai-info/content.rb +67 -1
- data/lib/sakai-info/forum.rb +9 -2
- data/lib/sakai-info/generic_message.rb +0 -6
- data/lib/sakai-info/hacks.rb +8 -1
- data/lib/sakai-info/private_message.rb +78 -0
- data/lib/sakai-info/quiz.rb +261 -2
- data/lib/sakai-info/sakai_object.rb +11 -1
- data/lib/sakai-info/site.rb +14 -1
- data/lib/sakai-info/user.rb +20 -2
- data/lib/sakai-info/version.rb +1 -1
- metadata +3 -2
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# sakai-info Change History #
|
2
2
|
|
3
|
+
### 0.5.1 ###
|
4
|
+
|
5
|
+
*Released 2012-10-07*
|
6
|
+
|
7
|
+
* Added new private-message lookup type
|
8
|
+
* Adjusted to new use of the 'author' field in PrivateMessage/ForumPost table
|
9
|
+
* Now including post body with forum-post
|
10
|
+
* Added new pending-quiz-access-control and published-quiz-access-control types
|
11
|
+
* Integrated access control objects with quiz object output
|
12
|
+
* New deleted-content-resource type
|
13
|
+
* New rake task to load test fixtures into database
|
14
|
+
* Added to_csv method with field selection to SakaiObject
|
15
|
+
* Added more use cases to POC query mode
|
16
|
+
* Added field selection to lookup via --fields=x,y,z
|
17
|
+
|
3
18
|
### 0.5.0 ###
|
4
19
|
|
5
20
|
*Released 2012-06-21*
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# sakai-info #
|
2
2
|
|
3
|
-
last updated: 2012-
|
3
|
+
last updated: 2012-10-07
|
4
4
|
author: David Adams (daveadams@gmail.com)
|
5
5
|
github url: https://github.com/daveadams/sakai-info
|
6
6
|
|
@@ -24,7 +24,7 @@ Use `rake` to test and build the gem:
|
|
24
24
|
$ rake gem:build
|
25
25
|
|
26
26
|
The resulting gem will be saved to the working directory as
|
27
|
-
`sakai-info-0.5.
|
27
|
+
`sakai-info-0.5.1.gem`.
|
28
28
|
|
29
29
|
Cleanup built gems using:
|
30
30
|
|
data/ROADMAP.md
CHANGED
@@ -1,27 +1,23 @@
|
|
1
1
|
# sakai-info Roadmap #
|
2
2
|
|
3
|
-
*Last updated 2012-
|
4
|
-
|
5
|
-
### 0.5.1 ###
|
6
|
-
|
7
|
-
* Test fixture generation for sites
|
8
|
-
* Text fixture loading into sqlite for sites and users
|
9
|
-
* Field restrictions for lookups
|
10
|
-
* Simple queries by name/title for user/site
|
11
|
-
* More details for quizzes including access control data
|
3
|
+
*Last updated 2012-10-07*
|
12
4
|
|
13
5
|
### 0.5.2 ###
|
14
6
|
|
15
|
-
*
|
16
|
-
*
|
7
|
+
* JSON output support
|
8
|
+
* Ability to specify alternate config file at the command line
|
9
|
+
* Better date formatting and proper time zone understanding
|
17
10
|
* More query functionality
|
18
11
|
* Proof-of-concept sin shell
|
19
12
|
* Chat channel/message support
|
13
|
+
* Alias support
|
14
|
+
* Add simple tests against user/site fixture data
|
20
15
|
|
21
16
|
### 0.5.3 ###
|
22
17
|
|
23
18
|
* RDS schema creation and data loading for MySQL and Oracle testing
|
24
19
|
* More query and shell functionality
|
20
|
+
* Test fixture generation for quizzes
|
25
21
|
|
26
22
|
### 0.5.4 ###
|
27
23
|
|
data/lib/sakai-info.rb
CHANGED
data/lib/sakai-info/cli/help.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# - sin command line help
|
3
3
|
#
|
4
4
|
# Created 2012-02-19 daveadams@gmail.com
|
5
|
-
# Last updated 2012-
|
5
|
+
# Last updated 2012-10-03 daveadams@gmail.com
|
6
6
|
#
|
7
7
|
# https://github.com/daveadams/sakai-info
|
8
8
|
#
|
@@ -24,11 +24,12 @@ Sakai INfo: sin #{VERSION}
|
|
24
24
|
Supported object types:
|
25
25
|
user, group, site, page, tool, pending-quiz, published-quiz,
|
26
26
|
pending-quiz-section, published-quiz-section, pending-quiz-item,
|
27
|
-
published-quiz-item,
|
27
|
+
published-quiz-item, pending-quiz-access-control,
|
28
|
+
published-quiz-access-control, quiz-attempt, quiz-attempt-item,
|
28
29
|
quiz-attempt-item-attachment, question-pool, assignment,
|
29
30
|
assignment-submission, forum, forum-thread, forum-post, content,
|
30
31
|
announcement, announcement-channel, gradebook, gradebook-item,
|
31
|
-
role, function, realm
|
32
|
+
role, function, realm, private-message
|
32
33
|
|
33
34
|
QUERY MODE
|
34
35
|
Query particular fields from certain objects given certain conditions.
|
@@ -78,6 +79,9 @@ Sakai Info: sin #{VERSION}
|
|
78
79
|
|
79
80
|
--all
|
80
81
|
Print all possible information (other than dbrow)
|
82
|
+
|
83
|
+
--fields=f1[,f2[,...]]
|
84
|
+
Include in output only those first-level hash keys given
|
81
85
|
EOF
|
82
86
|
|
83
87
|
"help" => <<EOF,
|
@@ -242,6 +246,24 @@ sin published-quiz-item
|
|
242
246
|
--mod Print creation/modification info
|
243
247
|
EOF
|
244
248
|
|
249
|
+
"pending-quiz-access-control" => <<EOF,
|
250
|
+
sin pending-quiz-access-control
|
251
|
+
|
252
|
+
Usage: sin pending-quiz-access-control <id> [<options>]
|
253
|
+
|
254
|
+
Prints information about the access control object attached to the
|
255
|
+
pending quiz ID specified.
|
256
|
+
EOF
|
257
|
+
|
258
|
+
"published-quiz-access-control" => <<EOF,
|
259
|
+
sin published-quiz-access-control
|
260
|
+
|
261
|
+
Usage: sin published-quiz-access-control <id> [<options>]
|
262
|
+
|
263
|
+
Prints information about the access control object attached to the
|
264
|
+
published quiz ID specified.
|
265
|
+
EOF
|
266
|
+
|
245
267
|
"question-pool" => <<EOF,
|
246
268
|
sin question-pool
|
247
269
|
|
@@ -364,6 +386,17 @@ sin content
|
|
364
386
|
--children Recursively print collection children
|
365
387
|
--full-children Print children with full IDs and file paths
|
366
388
|
--mod Print creation/modification info
|
389
|
+
EOF
|
390
|
+
"deleted-content-resource" => <<EOF,
|
391
|
+
sin content
|
392
|
+
|
393
|
+
Usage: sin deleted-content-resource <id> [<options>]
|
394
|
+
|
395
|
+
Prints information about the deleted content resource ID specified.
|
396
|
+
Additional options may be passed to include additional information:
|
397
|
+
|
398
|
+
--properties Print all properties
|
399
|
+
--mod Print creation/modification info
|
367
400
|
EOF
|
368
401
|
"announcement" => <<EOF,
|
369
402
|
sin announcement
|
@@ -427,6 +460,16 @@ sin realm
|
|
427
460
|
|
428
461
|
--roles List roles associated with this realm
|
429
462
|
--users List users in this realm
|
463
|
+
--mod Print creation/modification info
|
464
|
+
EOF
|
465
|
+
"private-message" => <<EOF,
|
466
|
+
sin private-message
|
467
|
+
|
468
|
+
Usage: sin private-message <id> [<options>]
|
469
|
+
|
470
|
+
Prints information about the private message ID specified. Additional options
|
471
|
+
may be passed to include additional information:
|
472
|
+
|
430
473
|
--mod Print creation/modification info
|
431
474
|
EOF
|
432
475
|
"query" => <<EOF,
|
@@ -15,21 +15,42 @@ module SakaiInfo
|
|
15
15
|
def self.process(args, flags)
|
16
16
|
object_type = args.shift
|
17
17
|
id = args.shift
|
18
|
+
fields = nil
|
19
|
+
|
20
|
+
flags.each do |flag|
|
21
|
+
if flag =~ /^--fields=(.+)$/
|
22
|
+
fields = $1.downcase.split(',')
|
23
|
+
flags.delete(flag)
|
24
|
+
end
|
25
|
+
end
|
18
26
|
|
19
27
|
if flags.include? "--all"
|
20
28
|
serials = CLI::LookupModes[object_type].all_serializations
|
21
|
-
elsif
|
22
|
-
flags.include? "--dbrow-only"
|
29
|
+
elsif flags.include? "--dbrow-only"
|
23
30
|
serials = [:dbrow_only]
|
24
31
|
else
|
25
32
|
serials = [:default] + flags.collect{|flag|flag.gsub(/^--/,'').gsub("-","_").to_sym}
|
26
33
|
end
|
34
|
+
|
35
|
+
object = nil
|
27
36
|
begin
|
28
|
-
|
29
|
-
rescue ObjectNotFoundException
|
30
|
-
STDERR.puts "ERROR: Could not find #{object_type} with an ID of '#{id}'"
|
37
|
+
object = CLI::LookupModes[object_type].find(id)
|
38
|
+
rescue ObjectNotFoundException => e
|
39
|
+
STDERR.puts "ERROR: Could not find #{object_type} with an ID of '#{id}':"
|
40
|
+
STDERR.puts " #{e}"
|
31
41
|
exit 1
|
32
42
|
end
|
43
|
+
|
44
|
+
if fields.nil? or fields.empty?
|
45
|
+
puts object.to_yaml(serials)
|
46
|
+
else
|
47
|
+
object_hash = object.serialize(serials).delete_if{|k,v| not fields.include? k.to_s}
|
48
|
+
if object_hash.empty?
|
49
|
+
STDERR.puts "ERROR: no requested fields were found"
|
50
|
+
exit 1
|
51
|
+
end
|
52
|
+
puts object_hash.to_yaml
|
53
|
+
end
|
33
54
|
end
|
34
55
|
end
|
35
56
|
|
@@ -57,6 +78,10 @@ module SakaiInfo
|
|
57
78
|
"nqi" => PendingQuizItem,
|
58
79
|
"penqi" => PendingQuizItem,
|
59
80
|
"pending-quiz-item" => PendingQuizItem,
|
81
|
+
"pqac" => PublishedQuizAccessControl,
|
82
|
+
"published-quiz-access-control" => PublishedQuizAccessControl,
|
83
|
+
"nqac" => PendingQuizAccessControl,
|
84
|
+
"pending-quiz-access-control" => PendingQuizAccessControl,
|
60
85
|
"qa" => QuizAttempt,
|
61
86
|
"quiz-attempt" => QuizAttempt,
|
62
87
|
"qai" => QuizAttemptItem,
|
@@ -77,6 +102,9 @@ module SakaiInfo
|
|
77
102
|
"fp" => ForumPost,
|
78
103
|
"forum-post" => ForumPost,
|
79
104
|
"content" => Content,
|
105
|
+
"deleted-content-resource" => DeletedContentResource,
|
106
|
+
"deleted-content" => DeletedContentResource,
|
107
|
+
"delcon" => DeletedContentResource,
|
80
108
|
"ann" => Announcement,
|
81
109
|
"announcement" => Announcement,
|
82
110
|
"annchan" => AnnouncementChannel,
|
@@ -90,6 +118,8 @@ module SakaiInfo
|
|
90
118
|
"func" => AuthzFunction,
|
91
119
|
"function" => AuthzFunction,
|
92
120
|
"realm" => AuthzRealm,
|
121
|
+
"private-message" => PrivateMessage,
|
122
|
+
"pm" => PrivateMessage,
|
93
123
|
}
|
94
124
|
end
|
95
125
|
end
|
data/lib/sakai-info/cli/query.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# class for handling "query" command line mode
|
3
3
|
#
|
4
4
|
# Created 2012-05-23 daveadams@gmail.com
|
5
|
-
# Last updated 2012-06
|
5
|
+
# Last updated 2012-10-06 daveadams@gmail.com
|
6
6
|
#
|
7
7
|
# https://github.com/daveadams/sakai-info
|
8
8
|
#
|
@@ -48,6 +48,45 @@ module SakaiInfo
|
|
48
48
|
exit 1
|
49
49
|
end
|
50
50
|
|
51
|
+
elsif object_type == "deleted-content"
|
52
|
+
userid = nil
|
53
|
+
while flags.length > 0
|
54
|
+
flag = flags.shift
|
55
|
+
if flag =~ /^--by=/
|
56
|
+
userid = flag.split('=')[1]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if userid.nil?
|
61
|
+
STDERR.puts "ERROR: you must specify --by=<userid>"
|
62
|
+
exit 1
|
63
|
+
end
|
64
|
+
|
65
|
+
begin
|
66
|
+
user = User.find(userid)
|
67
|
+
deleted_resources = DeletedContentResource.find_by_delete_userid(user.id)
|
68
|
+
deleted_resources.each do |dr|
|
69
|
+
puts dr.id
|
70
|
+
end
|
71
|
+
rescue ObjectNotFoundException => e
|
72
|
+
STDERR.puts "ERROR: #{e}"
|
73
|
+
exit 1
|
74
|
+
end
|
75
|
+
|
76
|
+
elsif object_type == "site"
|
77
|
+
title = args.shift
|
78
|
+
|
79
|
+
Site.find_by_title(title).each do |site|
|
80
|
+
puts site.to_csv(:id, :title)
|
81
|
+
end
|
82
|
+
|
83
|
+
elsif object_type == "user"
|
84
|
+
name = args.shift
|
85
|
+
|
86
|
+
User.find_by_name(name).each do |user|
|
87
|
+
puts user.to_csv(:eid, :name)
|
88
|
+
end
|
89
|
+
|
51
90
|
else
|
52
91
|
STDERR.puts "ERROR: Unrecognized object type"
|
53
92
|
exit 1
|
data/lib/sakai-info/content.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# SakaiInfo::Content library
|
3
3
|
#
|
4
4
|
# Created 2012-02-17 daveadams@gmail.com
|
5
|
-
# Last updated 2012-
|
5
|
+
# Last updated 2012-10-04 daveadams@gmail.com
|
6
6
|
#
|
7
7
|
# https://github.com/daveadams/sakai-info
|
8
8
|
#
|
@@ -219,6 +219,9 @@ module SakaiInfo
|
|
219
219
|
if result["uuid"].nil?
|
220
220
|
result.delete("uuid")
|
221
221
|
end
|
222
|
+
if result["context"].nil?
|
223
|
+
result.delete("context")
|
224
|
+
end
|
222
225
|
result
|
223
226
|
end
|
224
227
|
|
@@ -238,6 +241,69 @@ module SakaiInfo
|
|
238
241
|
end
|
239
242
|
end
|
240
243
|
|
244
|
+
class DeletedContentResource < ContentResource
|
245
|
+
attr_reader :file_path, :uuid, :context, :resource_type_id, :deleted_at, :dbrow
|
246
|
+
|
247
|
+
def self.clear_cache
|
248
|
+
@@cache = {}
|
249
|
+
end
|
250
|
+
clear_cache
|
251
|
+
|
252
|
+
def initialize(dbrow)
|
253
|
+
super(dbrow)
|
254
|
+
@table_name = "content_resource_delete"
|
255
|
+
@deleted_at = @dbrow[:delete_date].strftime("%Y-%m-%d %H:%M:%S")
|
256
|
+
end
|
257
|
+
|
258
|
+
def self.find(id)
|
259
|
+
if @@cache[id].nil?
|
260
|
+
row = DB.connect[:content_resource_delete].where(:resource_id => id).first
|
261
|
+
if row.nil?
|
262
|
+
raise ObjectNotFoundException.new(DeletedContentResource, id)
|
263
|
+
end
|
264
|
+
@@cache[id] = DeletedContentResource.new(row)
|
265
|
+
end
|
266
|
+
@@cache[id]
|
267
|
+
end
|
268
|
+
|
269
|
+
def deleted_by
|
270
|
+
@deleted_by ||= User.find(@dbrow[:delete_userid])
|
271
|
+
end
|
272
|
+
|
273
|
+
def self.find_by_delete_userid(user_id)
|
274
|
+
resources = []
|
275
|
+
DB.connect[:content_resource_delete].where(:delete_userid => user_id).all.each do |row|
|
276
|
+
@@cache[row[:resource_id]] = DeletedContentResource.new(row)
|
277
|
+
resources << @@cache[row[:resource_id]]
|
278
|
+
end
|
279
|
+
resources
|
280
|
+
end
|
281
|
+
|
282
|
+
def self.query_by_parent(parent_id)
|
283
|
+
DB.connect[:content_resource_delete].where(:in_collection => parent_id)
|
284
|
+
end
|
285
|
+
|
286
|
+
def self.find_by_parent(parent_id)
|
287
|
+
resources = []
|
288
|
+
DeletedContentResource.query_by_parent(parent_id).all.each do |row|
|
289
|
+
@@cache[row[:resource_id]] = DeletedContentResource.new(row)
|
290
|
+
resources << @@cache[row[:resource_id]]
|
291
|
+
end
|
292
|
+
resources
|
293
|
+
end
|
294
|
+
|
295
|
+
def self.count_by_parent(parent_id)
|
296
|
+
DeletedContentResource.query_by_parent(parent_id).count
|
297
|
+
end
|
298
|
+
|
299
|
+
def default_serialization
|
300
|
+
result = super
|
301
|
+
result["deleted_at"] = self.deleted_at
|
302
|
+
result["deleted_by"] = self.deleted_by.serialize(:summary)
|
303
|
+
result
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
241
307
|
class ContentCollection < Content
|
242
308
|
attr_reader :dbrow
|
243
309
|
|
data/lib/sakai-info/forum.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# SakaiInfo::Forum library
|
3
3
|
#
|
4
4
|
# Created 2012-04-01 daveadams@gmail.com
|
5
|
-
# Last updated 2012-
|
5
|
+
# Last updated 2012-09-25 daveadams@gmail.com
|
6
6
|
#
|
7
7
|
# https://github.com/daveadams/sakai-info
|
8
8
|
#
|
@@ -231,18 +231,24 @@ module SakaiInfo
|
|
231
231
|
def initialize(dbrow)
|
232
232
|
@dbrow = dbrow
|
233
233
|
|
234
|
+
@dbrow[:body] = dbrow[:body].read
|
235
|
+
|
234
236
|
@id = dbrow[:id].to_i
|
235
237
|
@title = dbrow[:title]
|
236
238
|
end
|
237
239
|
|
238
240
|
def author
|
239
|
-
@author ||= User.find(@dbrow[:
|
241
|
+
@author ||= User.find(@dbrow[:created_by])
|
240
242
|
end
|
241
243
|
|
242
244
|
def thread
|
243
245
|
@thread ||= ForumThread.find(@dbrow[:surrogatekey])
|
244
246
|
end
|
245
247
|
|
248
|
+
def body
|
249
|
+
@dbrow[:body]
|
250
|
+
end
|
251
|
+
|
246
252
|
def self.query_by_thread_id(thread_id)
|
247
253
|
DB.connect[:mfr_message_t].where(:surrogatekey => thread_id)
|
248
254
|
end
|
@@ -265,6 +271,7 @@ module SakaiInfo
|
|
265
271
|
"title" => self.title,
|
266
272
|
"author" => self.author.serialize(:summary),
|
267
273
|
"thread" => self.thread.serialize(:summary),
|
274
|
+
"body" => self.body,
|
268
275
|
}
|
269
276
|
end
|
270
277
|
|
data/lib/sakai-info/hacks.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# Hacks necessary to work around problems in external libraries
|
3
3
|
#
|
4
4
|
# Created 2012-05-20 daveadams@gmail.com
|
5
|
-
# Last updated 2012-05
|
5
|
+
# Last updated 2012-10-05 daveadams@gmail.com
|
6
6
|
#
|
7
7
|
# https://github.com/daveadams/sakai-info
|
8
8
|
#
|
@@ -47,3 +47,10 @@ module Sequel
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|
50
|
+
|
51
|
+
# hack to get clobs in sqlite to work correctly
|
52
|
+
class String
|
53
|
+
def read
|
54
|
+
self
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# sakai-info/private_message.rb
|
2
|
+
# SakaiInfo::PrivateMessage library
|
3
|
+
#
|
4
|
+
# Created 2012-09-25 daveadams@gmail.com
|
5
|
+
# Last updated 2012-09-25 daveadams@gmail.com
|
6
|
+
#
|
7
|
+
# https://github.com/daveadams/sakai-info
|
8
|
+
#
|
9
|
+
# This software is public domain.
|
10
|
+
#
|
11
|
+
|
12
|
+
module SakaiInfo
|
13
|
+
class PrivateMessage < GenericMessage
|
14
|
+
attr_reader :id, :title, :dbrow
|
15
|
+
|
16
|
+
include ModProps
|
17
|
+
created_by_key :created_by
|
18
|
+
created_at_key :created
|
19
|
+
modified_by_key :modified_by
|
20
|
+
modified_at_key :modified
|
21
|
+
|
22
|
+
def self.clear_cache
|
23
|
+
@@cache = {}
|
24
|
+
end
|
25
|
+
clear_cache
|
26
|
+
|
27
|
+
def self.find(id)
|
28
|
+
if @@cache[id.to_s].nil?
|
29
|
+
row = DB.connect[:mfr_message_t].where(:id => id, :message_dtype => "PM").first
|
30
|
+
if row.nil?
|
31
|
+
raise ObjectNotFoundException.new(PrivateMessage, id)
|
32
|
+
end
|
33
|
+
@@cache[id.to_s] = PrivateMessage.new(row)
|
34
|
+
end
|
35
|
+
@@cache[id.to_s]
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(dbrow)
|
39
|
+
@dbrow = dbrow
|
40
|
+
|
41
|
+
@dbrow[:body] = dbrow[:body].read
|
42
|
+
@dbrow[:recipients_as_text] = dbrow[:recipients_as_text].read
|
43
|
+
|
44
|
+
@id = dbrow[:id].to_i
|
45
|
+
@title = dbrow[:title]
|
46
|
+
end
|
47
|
+
|
48
|
+
def author
|
49
|
+
# apparently the 'author' field is just a display string?!?
|
50
|
+
# as of 2.8, perhaps?
|
51
|
+
@author ||= User.find(@dbrow[:created_by])
|
52
|
+
end
|
53
|
+
|
54
|
+
def body
|
55
|
+
@dbrow[:body]
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.count_by_date(d)
|
59
|
+
count_by_date_and_message_type(d, "PM")
|
60
|
+
end
|
61
|
+
|
62
|
+
def default_serialization
|
63
|
+
{
|
64
|
+
"id" => self.id,
|
65
|
+
"title" => self.title,
|
66
|
+
"author" => self.author.serialize(:summary),
|
67
|
+
"body" => self.body,
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def summary_serialization
|
72
|
+
{
|
73
|
+
"id" => self.id,
|
74
|
+
"title" => self.title,
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/sakai-info/quiz.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# SakaiInfo::Quiz library
|
3
3
|
#
|
4
4
|
# Created 2012-02-17 daveadams@gmail.com
|
5
|
-
# Last updated 2012-
|
5
|
+
# Last updated 2012-09-29 daveadams@gmail.com
|
6
6
|
#
|
7
7
|
# https://github.com/daveadams/sakai-info
|
8
8
|
#
|
@@ -128,6 +128,10 @@ module SakaiInfo
|
|
128
128
|
@items ||= self.item_class.find_by_quiz_id(self.id)
|
129
129
|
end
|
130
130
|
|
131
|
+
def access_control
|
132
|
+
@access_control ||= self.access_control_class.find(self.id)
|
133
|
+
end
|
134
|
+
|
131
135
|
def default_serialization
|
132
136
|
result = {
|
133
137
|
"id" => self.id,
|
@@ -137,7 +141,8 @@ module SakaiInfo
|
|
137
141
|
"type" => self.quiz_type,
|
138
142
|
"section_count" => self.section_count,
|
139
143
|
"item_count" => self.item_count,
|
140
|
-
"attempt_count" => nil
|
144
|
+
"attempt_count" => nil,
|
145
|
+
"access_control" => self.access_control.serialize(:quiz_summary)
|
141
146
|
}
|
142
147
|
if not self.site.nil?
|
143
148
|
result["site"] = self.site.serialize(:summary)
|
@@ -172,6 +177,12 @@ module SakaiInfo
|
|
172
177
|
}
|
173
178
|
end
|
174
179
|
|
180
|
+
def access_control_summary_serialization
|
181
|
+
result = self.summary_serialization
|
182
|
+
result.delete("id")
|
183
|
+
result
|
184
|
+
end
|
185
|
+
|
175
186
|
def sections_serialization
|
176
187
|
{
|
177
188
|
"sections" => self.sections.collect{|s|s.serialize(:quiz_summary)}
|
@@ -245,6 +256,10 @@ module SakaiInfo
|
|
245
256
|
def item_class
|
246
257
|
PendingQuizItem
|
247
258
|
end
|
259
|
+
|
260
|
+
def access_control_class
|
261
|
+
PendingQuizAccessControl
|
262
|
+
end
|
248
263
|
end
|
249
264
|
|
250
265
|
class PublishedQuiz < Quiz
|
@@ -301,6 +316,10 @@ module SakaiInfo
|
|
301
316
|
PublishedQuizItem
|
302
317
|
end
|
303
318
|
|
319
|
+
def access_control_class
|
320
|
+
PublishedQuizAccessControl
|
321
|
+
end
|
322
|
+
|
304
323
|
def attempt_count
|
305
324
|
@attempt_count ||= QuizAttempt.count_by_quiz_id(self.id)
|
306
325
|
end
|
@@ -1010,4 +1029,244 @@ module SakaiInfo
|
|
1010
1029
|
}
|
1011
1030
|
end
|
1012
1031
|
end
|
1032
|
+
|
1033
|
+
class QuizAccessControl < SakaiObject
|
1034
|
+
attr_reader :id, :dbrow
|
1035
|
+
|
1036
|
+
def initialize(dbrow)
|
1037
|
+
@dbrow = dbrow
|
1038
|
+
@id = dbrow[:assessmentid]
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
def unlimited_submissions?
|
1042
|
+
@dbrow[:unlimitedsubmissions] == 1
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
def submissions_allowed
|
1046
|
+
@dbrow[:submissionsallowed]
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
def submissions_saved
|
1050
|
+
@dbrow[:submissionssaved] == 1
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
def question_layout
|
1054
|
+
case @dbrow[:assessmentformat]
|
1055
|
+
when 1
|
1056
|
+
"One question per page"
|
1057
|
+
when 2
|
1058
|
+
"One part per page"
|
1059
|
+
when 3
|
1060
|
+
"Single page quiz"
|
1061
|
+
else
|
1062
|
+
@dbrow[:assessmentformat]
|
1063
|
+
end
|
1064
|
+
end
|
1065
|
+
|
1066
|
+
def late_handling
|
1067
|
+
case @dbrow[:latehandling]
|
1068
|
+
when 1
|
1069
|
+
"Late submissions accepted"
|
1070
|
+
when 2
|
1071
|
+
"Late submissions NOT accepted"
|
1072
|
+
else
|
1073
|
+
@dbrow[:latehandling]
|
1074
|
+
end
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
def item_navigation
|
1078
|
+
case @dbrow[:itemnavigation]
|
1079
|
+
when 1
|
1080
|
+
"Linear"
|
1081
|
+
when 2
|
1082
|
+
"Random"
|
1083
|
+
else
|
1084
|
+
@dbrow[:itemnavigation]
|
1085
|
+
end
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
def item_numbering
|
1089
|
+
case @dbrow[:itemnumbering]
|
1090
|
+
when 1
|
1091
|
+
"Continuous through parts"
|
1092
|
+
when 2
|
1093
|
+
"Restart numbering at each part"
|
1094
|
+
else
|
1095
|
+
@dbrow[:itemnumbering]
|
1096
|
+
end
|
1097
|
+
end
|
1098
|
+
|
1099
|
+
def submission_message
|
1100
|
+
@dbrow[:submissionmessage]
|
1101
|
+
end
|
1102
|
+
|
1103
|
+
def release_to
|
1104
|
+
@dbrow[:releaseto]
|
1105
|
+
end
|
1106
|
+
|
1107
|
+
def username
|
1108
|
+
@dbrow[:username]
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
def password
|
1112
|
+
@dbrow[:password]
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
def final_page_url
|
1116
|
+
@dbrow[:finalpageurl]
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
def mark_for_review_allowed?
|
1120
|
+
@dbrow[:markforreview] == 1
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
def authenticated?
|
1124
|
+
not (self.username.nil? and self.password.nil?)
|
1125
|
+
end
|
1126
|
+
|
1127
|
+
def time_limit
|
1128
|
+
@dbrow[:timelimit]
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
def timed?
|
1132
|
+
@dbrow[:timelimit] > 0
|
1133
|
+
end
|
1134
|
+
|
1135
|
+
def automatic_submission?
|
1136
|
+
@dbrow[:autosubmit] == 1
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
def start_date
|
1140
|
+
if @dbrow[:startdate].nil?
|
1141
|
+
nil
|
1142
|
+
else
|
1143
|
+
@dbrow[:startdate].strftime("%Y-%m-%d %H:%M:%S")
|
1144
|
+
end
|
1145
|
+
end
|
1146
|
+
|
1147
|
+
def due_date
|
1148
|
+
if @dbrow[:duedate].nil?
|
1149
|
+
nil
|
1150
|
+
else
|
1151
|
+
@dbrow[:duedate].strftime("%Y-%m-%d %H:%M:%S")
|
1152
|
+
end
|
1153
|
+
end
|
1154
|
+
|
1155
|
+
def retract_date
|
1156
|
+
if @dbrow[:retractdate].nil?
|
1157
|
+
nil
|
1158
|
+
else
|
1159
|
+
@dbrow[:retractdate].strftime("%Y-%m-%d %H:%M:%S")
|
1160
|
+
end
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
def feedback_date
|
1164
|
+
if @dbrow[:feedbackdate].nil?
|
1165
|
+
nil
|
1166
|
+
else
|
1167
|
+
@dbrow[:feedbackdate].strftime("%Y-%m-%d %H:%M:%S")
|
1168
|
+
end
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
def score_date
|
1172
|
+
if @dbrow[:scoredate].nil?
|
1173
|
+
nil
|
1174
|
+
else
|
1175
|
+
@dbrow[:scoredate].strftime("%Y-%m-%d %H:%M:%S")
|
1176
|
+
end
|
1177
|
+
end
|
1178
|
+
|
1179
|
+
def default_serialization
|
1180
|
+
result = {
|
1181
|
+
"id" => self.id,
|
1182
|
+
"quiz" => self.quiz.serialize(:access_control_summary),
|
1183
|
+
"unlimited_submissions" => self.unlimited_submissions?,
|
1184
|
+
"submissions_allowed" => self.submissions_allowed,
|
1185
|
+
"timed" => self.timed?,
|
1186
|
+
"time_limit" => self.time_limit,
|
1187
|
+
"question_layout" => self.question_layout,
|
1188
|
+
"late_handling" => self.late_handling,
|
1189
|
+
"item_navigation" => self.item_navigation,
|
1190
|
+
"item_numbering" => self.item_numbering,
|
1191
|
+
"release_to" => self.release_to,
|
1192
|
+
"authenticated" => self.authenticated?,
|
1193
|
+
"automatic_submission" => self.automatic_submission?,
|
1194
|
+
"mark_for_review_allowed" => self.mark_for_review_allowed?,
|
1195
|
+
}
|
1196
|
+
if not self.timed?
|
1197
|
+
result.delete("time_limit")
|
1198
|
+
end
|
1199
|
+
if self.unlimited_submissions?
|
1200
|
+
result.delete("submissions_allowed")
|
1201
|
+
end
|
1202
|
+
%w(username start_date due_date score_date retract_date feedback_date submission_message final_page_url).each do |field_name|
|
1203
|
+
value = self.method(field_name.to_sym).call
|
1204
|
+
if not value.nil?
|
1205
|
+
result[field_name] = value
|
1206
|
+
end
|
1207
|
+
end
|
1208
|
+
result
|
1209
|
+
end
|
1210
|
+
|
1211
|
+
def quiz_summary_serialization
|
1212
|
+
result = default_serialization
|
1213
|
+
result.delete("quiz")
|
1214
|
+
result.delete("id")
|
1215
|
+
result
|
1216
|
+
end
|
1217
|
+
|
1218
|
+
def summary_serialization
|
1219
|
+
{
|
1220
|
+
"id" => self.id,
|
1221
|
+
}
|
1222
|
+
end
|
1223
|
+
end
|
1224
|
+
|
1225
|
+
class PendingQuizAccessControl < QuizAccessControl
|
1226
|
+
def self.clear_cache
|
1227
|
+
@@cache = {}
|
1228
|
+
end
|
1229
|
+
clear_cache
|
1230
|
+
|
1231
|
+
def self.find(id)
|
1232
|
+
id = id.to_s
|
1233
|
+
if @@cache[id].nil?
|
1234
|
+
row = DB.connect[:sam_assessaccesscontrol_t].where(:assessmentid => id.to_i).first
|
1235
|
+
if row.nil?
|
1236
|
+
raise ObjectNotFoundException.new(PendingQuizAccessControl, id)
|
1237
|
+
end
|
1238
|
+
|
1239
|
+
@@cache[id] = PendingQuizAccessControl.new(row)
|
1240
|
+
end
|
1241
|
+
@@cache[id]
|
1242
|
+
end
|
1243
|
+
|
1244
|
+
def quiz
|
1245
|
+
@quiz ||= PendingQuiz.find(self.id)
|
1246
|
+
end
|
1247
|
+
end
|
1248
|
+
|
1249
|
+
class PublishedQuizAccessControl < QuizAccessControl
|
1250
|
+
def self.clear_cache
|
1251
|
+
@@cache = {}
|
1252
|
+
end
|
1253
|
+
clear_cache
|
1254
|
+
|
1255
|
+
def self.find(id)
|
1256
|
+
id = id.to_s
|
1257
|
+
if @@cache[id].nil?
|
1258
|
+
row = DB.connect[:sam_publishedaccesscontrol_t].where(:assessmentid => id.to_i).first
|
1259
|
+
if row.nil?
|
1260
|
+
raise ObjectNotFoundException.new(PublishedQuizAccessControl, id)
|
1261
|
+
end
|
1262
|
+
|
1263
|
+
@@cache[id] = PublishedQuizAccessControl.new(row)
|
1264
|
+
end
|
1265
|
+
@@cache[id]
|
1266
|
+
end
|
1267
|
+
|
1268
|
+
def quiz
|
1269
|
+
@quiz ||= PublishedQuiz.find(self.id)
|
1270
|
+
end
|
1271
|
+
end
|
1013
1272
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
# SakaiInfo::SakaiObject
|
3
3
|
#
|
4
4
|
# Created 2012-02-15 daveadams@gmail.com
|
5
|
-
# Last updated 2012-
|
5
|
+
# Last updated 2012-10-06 daveadams@gmail.com
|
6
6
|
#
|
7
7
|
# https://github.com/daveadams/sakai-info
|
8
8
|
#
|
@@ -70,6 +70,16 @@ module SakaiInfo
|
|
70
70
|
serialize(q).to_json
|
71
71
|
end
|
72
72
|
|
73
|
+
def to_csv(*fields)
|
74
|
+
values = []
|
75
|
+
fields.each do |field|
|
76
|
+
m = self.method(field.to_sym)
|
77
|
+
next if m.nil?
|
78
|
+
values << m.call.to_s
|
79
|
+
end
|
80
|
+
values.collect{|v|"\"#{v}\""}.join(",")
|
81
|
+
end
|
82
|
+
|
73
83
|
# support for CLI -- returns an array of symbols that can be
|
74
84
|
# passed back to #serialize, #to_yaml, or #to_json
|
75
85
|
# should be reimplemented in all object classes
|
data/lib/sakai-info/site.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# SakaiInfo::Site library
|
3
3
|
#
|
4
4
|
# Created 2012-02-17 daveadams@gmail.com
|
5
|
-
# Last updated 2012-
|
5
|
+
# Last updated 2012-10-06 daveadams@gmail.com
|
6
6
|
#
|
7
7
|
# https://github.com/daveadams/sakai-info
|
8
8
|
#
|
@@ -237,6 +237,19 @@ module SakaiInfo
|
|
237
237
|
where(:type => type).all.collect{|r| r[:site_id]}
|
238
238
|
end
|
239
239
|
|
240
|
+
# by_title: uses like
|
241
|
+
def self.query_by_title(title)
|
242
|
+
DB.connect[:sakai_site].where("upper(title) like ?", "%#{title.upcase}%")
|
243
|
+
end
|
244
|
+
|
245
|
+
def self.find_by_title(title)
|
246
|
+
Site.query_by_title(title).all.collect{|row| @@cache[row[:site_id]] = Site.new(row)}
|
247
|
+
end
|
248
|
+
|
249
|
+
def self.find_ids_by_title(title)
|
250
|
+
Site.query_by_title(title).select(:site_id).all.collect{|row|row[:site_id]}
|
251
|
+
end
|
252
|
+
|
240
253
|
# by_type queries
|
241
254
|
def self.query_by_type(type)
|
242
255
|
DB.connect[:sakai_site].where(:type => type)
|
data/lib/sakai-info/user.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# SakaiInfo::User library
|
3
3
|
#
|
4
4
|
# Created 2012-02-17 daveadams@gmail.com
|
5
|
-
# Last updated 2012-
|
5
|
+
# Last updated 2012-10-06 daveadams@gmail.com
|
6
6
|
#
|
7
7
|
# https://github.com/daveadams/sakai-info
|
8
8
|
#
|
@@ -174,6 +174,19 @@ module SakaiInfo
|
|
174
174
|
nil
|
175
175
|
end
|
176
176
|
|
177
|
+
# by_name: uses like
|
178
|
+
def self.query_by_name(name)
|
179
|
+
DB.connect[:sakai_user].where("upper(first_name) like ? or upper(last_name) like ? or upper(first_name)||\' \'||upper(last_name) like ?", "%#{name.upcase}%", "%#{name.upcase}%", "%#{name.upcase}%")
|
180
|
+
end
|
181
|
+
|
182
|
+
def self.find_by_name(name)
|
183
|
+
User.query_by_name(name).all.collect{|row| @@cache[row[:user_id]] = User.new(row)}
|
184
|
+
end
|
185
|
+
|
186
|
+
def self.find_ids_by_name(name)
|
187
|
+
User.query_by_name(name).select(:user_id).all.collect{|row|row[:user_id]}
|
188
|
+
end
|
189
|
+
|
177
190
|
# yaml/json serialization
|
178
191
|
def default_serialization
|
179
192
|
result = {
|
@@ -218,7 +231,12 @@ module SakaiInfo
|
|
218
231
|
end
|
219
232
|
|
220
233
|
def self.all_serializations
|
221
|
-
[
|
234
|
+
[
|
235
|
+
:default,
|
236
|
+
:sites,
|
237
|
+
:pools,
|
238
|
+
:mod,
|
239
|
+
]
|
222
240
|
end
|
223
241
|
end
|
224
242
|
|
data/lib/sakai-info/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sakai-info
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-10-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sequel
|
@@ -54,6 +54,7 @@ files:
|
|
54
54
|
- lib/sakai-info.rb
|
55
55
|
- lib/sakai-info/util.rb
|
56
56
|
- lib/sakai-info/group.rb
|
57
|
+
- lib/sakai-info/private_message.rb
|
57
58
|
- lib/sakai-info/tool.rb
|
58
59
|
- lib/sakai-info/cli/help.rb
|
59
60
|
- lib/sakai-info/cli/query.rb
|