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