sakai-info 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # sakai-info Change History #
2
2
 
3
+ ## 0.3.1 ##
4
+
5
+ *Released 2012-02-29*
6
+
7
+ * New objects: quiz-attempt, quiz-attempt-item, quiz-attempt-item-attachment
8
+ * More new objects: assignment, assignment-submission
9
+ * SakaiXMLEntity base class supports ModProps
10
+ * New standard CL arguments: --dbrow-only and --mod-details
11
+ * First steps towards a sqlite test infrastructure: schema creation
12
+ * First steps towards better Sakai DB docs: graphviz file for quiz tables
13
+
3
14
  ## 0.3.0 ##
4
15
 
5
16
  *Released 2012-02-26*
data/README.md CHANGED
@@ -10,7 +10,7 @@ tool or the libraries.
10
10
 
11
11
  ## Meta ##
12
12
 
13
- last updated: 2012-02-26
13
+ last updated: 2012-02-29
14
14
  author: David Adams (daveadams@gmail.com)
15
15
  github url: https://github.com/daveadams/sakai-info
16
16
 
@@ -26,7 +26,7 @@ Use `rake` to test and build the gem:
26
26
  $ rake gem:build
27
27
 
28
28
  The resulting gem will be saved to the working directory as
29
- `sakai-info-0.3.0.gem`.
29
+ `sakai-info-0.3.1.gem`.
30
30
 
31
31
  Cleanup built gems using:
32
32
 
data/ROADMAP.md CHANGED
@@ -1,10 +1,6 @@
1
1
  # sakai-info Roadmap #
2
2
 
3
- *Last updated 2012-02-26*
4
-
5
- ### 0.3.1 ###
6
-
7
- * CLI access to assignments
3
+ *Last updated 2012-02-29*
8
4
 
9
5
  ### 0.3.2 ###
10
6
 
@@ -18,6 +14,22 @@
18
14
 
19
15
  * CLI access to forums
20
16
 
17
+ ### 0.3.5 ###
18
+
19
+ * CLI access to gradebook
20
+
21
+ ### 0.3.6 ###
22
+
23
+ * CLI access to announcements
24
+
25
+ ### 0.3.7 ###
26
+
27
+ * CLI access to content
28
+
29
+ ### 0.3.8 ###
30
+
31
+ * CLI access to authz (realms, roles, functions)
32
+
21
33
  ### 0.4 ###
22
34
 
23
35
  * Standardized abstraction for user and site to support --mod and --dbrow
data/bin/sakai-info CHANGED
@@ -140,9 +140,9 @@ if flags.include? "--all"
140
140
  serials = CLI::ObjectModes[mode].all_serializations
141
141
  elsif
142
142
  flags.include? "--dbrow-only"
143
- serials = [:dbrow]
143
+ serials = [:dbrow_only]
144
144
  else
145
- serials = [:default] + flags.collect{|flag|flag.gsub(/^--/,'').to_sym}
145
+ serials = [:default] + flags.collect{|flag|flag.gsub(/^--/,'').gsub("-","_").to_sym}
146
146
  end
147
147
  begin
148
148
  puts CLI::ObjectModes[mode].find(id).to_yaml(serials)
data/lib/sakai-info.rb CHANGED
@@ -73,9 +73,9 @@ end
73
73
  require 'sakai-info/database'
74
74
 
75
75
  # base objects
76
+ require 'sakai-info/mod_props'
76
77
  require 'sakai-info/sakai_object'
77
78
  require 'sakai-info/sakai_xml_entity'
78
- require 'sakai-info/mod_props'
79
79
 
80
80
  # sakai object classes
81
81
  require 'sakai-info/user'
@@ -2,7 +2,7 @@
2
2
  # SakaiInfo::Assignment library
3
3
  #
4
4
  # Created 2012-02-17 daveadams@gmail.com
5
- # Last updated 2012-02-24 daveadams@gmail.com
5
+ # Last updated 2012-02-29 daveadams@gmail.com
6
6
  #
7
7
  # https://github.com/daveadams/sakai-info
8
8
  #
@@ -11,46 +11,43 @@
11
11
 
12
12
  module SakaiInfo
13
13
  class Assignment < SakaiXMLEntity
14
- attr_reader :site
14
+ attr_reader :dbrow, :site_id
15
15
 
16
16
  @@cache = {}
17
17
  def self.find(id)
18
18
  if @@cache[id].nil?
19
19
  xml = ""
20
- row = DB.connect[:assignment_assigment].filter(:assignment_id => id).first
20
+ row = DB.connect[:assignment_assignment].where(:assignment_id => id).first
21
21
  if row.nil?
22
22
  raise ObjectNotFoundException.new(Assignment, id)
23
23
  end
24
- site = Site.find(row[:context])
25
- REXML::Document.new(row[:xml].read).write(xml, 2)
26
- @@cache[id] = Assignment.new(id, site, xml)
24
+ @@cache[id] = Assignment.new(row)
27
25
  end
28
26
  @@cache[id]
29
27
  end
30
28
 
31
29
  # raw data constructor
32
- def initialize(id, site, xml)
33
- @id = id
34
- @site = site
35
- @xml = xml
30
+ def initialize(dbrow)
31
+ @dbrow = dbrow
32
+
33
+ @id = dbrow[:assignment_id]
34
+ @site_id = dbrow[:context]
35
+
36
36
  parse_xml
37
37
  end
38
38
 
39
39
  # set lookup
40
+ def self.query_by_site_id(site_id)
41
+ DB.connect[:assignment_assignment].where(:context => site_id)
42
+ end
43
+
40
44
  def self.find_by_site_id(site_id)
41
- assignments = []
42
- site = Site.find(site_id)
43
- DB.connect[:assignment_assignment].filter(:context => site_id).all.each do |row|
44
- id = row[:assignment_id]
45
- xml = ""
46
- REXML::Document.new(row[:xml].read).write(xml, 2)
47
- assignments << Assignment.new(id, site, xml)
48
- end
49
- return assignments
45
+ Assignment.query_by_site_id(site_id).
46
+ all.collect { |row| Assignment.new(row) }
50
47
  end
51
48
 
52
49
  def self.count_by_site_id(site_id)
53
- DB.connect[:assignment_assignment].filter(:context => site_id).count
50
+ Assignment.query_by_site_id(site_id).count
54
51
  end
55
52
 
56
53
  # getters
@@ -58,12 +55,16 @@ module SakaiInfo
58
55
  @attributes["title"]
59
56
  end
60
57
 
58
+ def site
59
+ @site ||= Site.find(self.site_id)
60
+ end
61
+
61
62
  def submissions
62
- @submissions ||= AssignmentSubmission.find_by_assignment_id(@id)
63
+ @submissions ||= AssignmentSubmission.find_by_assignment_id(self.id)
63
64
  end
64
65
 
65
66
  def submission_count
66
- @submission_count ||= AssignmentSubmission.count_by_assignment_id(@id)
67
+ @submission_count ||= AssignmentSubmission.count_by_assignment_id(self.id)
67
68
  end
68
69
 
69
70
  # yaml/json serialization
@@ -72,9 +73,7 @@ module SakaiInfo
72
73
  "id" => self.id,
73
74
  "title" => self.title,
74
75
  "site" => self.site.serialize(:summary),
75
- "created_by" => self.created_by.eid,
76
- "created_at" => self.created_at,
77
- "submissions" => self.submission_count
76
+ "submission_count" => self.submission_count
78
77
  }
79
78
  end
80
79
 
@@ -82,118 +81,122 @@ module SakaiInfo
82
81
  {
83
82
  "id" => self.id,
84
83
  "title" => self.title,
85
- "created_by" => self.created_by.eid,
86
- "submissions" => self.submission_count
84
+ "submission_count" => self.submission_count
85
+ }
86
+ end
87
+
88
+ def submissions_serialization
89
+ {
90
+ "submissions" => self.submissions.collect{|s|s.serialize(:assignment_summary)}
87
91
  }
88
92
  end
89
93
  end
90
94
 
91
95
  class AssignmentSubmission < SakaiXMLEntity
92
- attr_reader :assignment, :submitter
96
+ attr_reader :dbrow, :assignment_id, :submitter_id
97
+
98
+ def initialize(dbrow)
99
+ @dbrow = dbrow
100
+
101
+ @id = dbrow[:submission_id]
102
+ @assignment_id = dbrow[:context]
103
+ @submitter_id = dbrow[:submitter_id]
104
+ @is_submitted = (dbrow[:submitted] == "true")
105
+ @is_graded = (dbrow[:graded] == "true")
93
106
 
94
- def initialize(id, assignment, xml, submitter, submitted, graded)
95
- @id = id
96
- @assignment = assignment
97
- @xml = xml
98
- @submitter = submitter
99
- @submitted = submitted
100
- @graded = graded
101
107
  parse_xml
102
108
  end
103
109
 
104
110
  @@cache = {}
105
111
  def self.find(id)
106
112
  if @@cache[id].nil?
107
- assignment = submitter = submitted_at = submitted = graded = nil
108
- xml = ""
109
- row = DB.connect[:assignment_submission].filter(:submission_id => id).first
113
+ row = DB.connect[:assignment_submission].where(:submission_id => id).first
110
114
  if row.nil?
111
115
  raise ObjectNotFoundException.new(AssignmentSubmission, id)
112
116
  end
113
117
 
114
- assignment = Assignment.find(row[:context])
115
- REXML::Document.new(row[:xml].read).write(xml, 2)
116
- submitter = User.find(row[:submitter_id])
117
- submitted = (row[:submitted] == "true")
118
- graded = (row[:graded] == "true")
119
-
120
- @@cache[id] = AssignmentSubmission.new(id, assignment, xml, submitter, submitted, graded)
118
+ @@cache[id] = AssignmentSubmission.new(row)
121
119
  end
122
120
  @@cache[id]
123
121
  end
124
122
 
123
+ def submitter
124
+ @submitter ||= User.find(self.submitter_id)
125
+ end
126
+
127
+ def assignment
128
+ @assignment ||= Assignment.find(self.assignment_id)
129
+ end
130
+
125
131
  def submitted?
126
- ( created_by.eid == @submitter.eid && @submitted ) || false
132
+ ( self.created_by_id == self.submitter_id && @is_submitted ) || false
127
133
  end
128
134
 
129
135
  def graded?
130
- @graded || false
136
+ @is_graded || false
131
137
  end
132
138
 
133
139
  def submitted_at
134
140
  @submitted_at ||= format_entity_date(@attributes["datesubmitted"])
135
141
  end
136
142
 
143
+ def self.query_by_assignment_id(assignment_id)
144
+ DB.connect[:assignment_submission].where(:context => assignment_id)
145
+ end
146
+
137
147
  def self.find_by_assignment_id(assignment_id)
138
- submissions = []
139
- assignment = Assignment.find(assignment_id)
140
- DB.connect[:assignment_submission].filter(:context => assignment_id).all.each do |row|
141
- id = row[:submission_id]
142
- xml = ""
143
- REXML::Document.new(row[:xml].read).write(xml, 2)
144
- submitter = User.find(row[:submitter_id])
145
- submitted = (row[:submitted] == "true")
146
- graded = (row[:graded] == "true")
147
- submissions << AssignmentSubmission.new(id, assignment, xml, submitter, submitted, graded)
148
- end
149
- return submissions
148
+ AssignmentSubmission.query_by_assignment_id(assignment_id).
149
+ all.collect { |row| AssignmentSubmission.new(row) }
150
150
  end
151
151
 
152
152
  def self.count_by_assignment_id(assignment_id)
153
- DB.connect[:assignment_submission].filter(:context => assignment_id).count
153
+ AssignmentSubmission.query_by_assignment_id(assignment_id).count
154
+ end
155
+
156
+ def self.query_by_user_id(user_id)
157
+ DB.connect[:assignment_submission].where(:submitter_id => user_id)
154
158
  end
155
159
 
156
160
  def self.find_by_user_id(user_id)
157
- submissions = []
158
- submitter = User.find(user_id)
159
- DB.connect[:assignment_submission].filter(:submitter_id => user_id).all.each do |row|
160
- id = row[:submission_id]
161
- assignment = Assignment.find(row[:context])
162
- xml = ""
163
- REXML::Document.new(row[:xml].read).write(xml, 2)
164
- submitted = (row[:submitted] == "true")
165
- graded = (row[:graded] == "true")
166
- submissions << AssignmentSubmission.new(id, assignment, xml, submitter, submitted, graded)
167
- end
168
- return submissions
161
+ AssignmentSubmission.query_by_user_id(user_id).
162
+ all.collect { |row| AssignmentSubmission.new(row) }
169
163
  end
170
164
 
171
165
  def self.count_by_user_id(user_id)
172
- DB.connect[:assignment_submission].filter(:submitter_id => user_id).count
166
+ AssignmentSubmission.query_by_user_id(user_id).count
173
167
  end
174
168
 
175
169
  # yaml/json serialization
176
170
  def default_serialization
177
- {
171
+ result = {
178
172
  "id" => self.id,
179
173
  "assignment" => self.assignment.serialize(:summary),
180
174
  "submitter" => self.submitter.serialize(:summary),
181
- "submitted" => self.submitted?,
175
+ "is_submitted" => self.submitted?,
182
176
  "submitted_at" => self.submitted_at,
183
- "graded" => self.graded?,
184
- "created_by" => self.created_by.eid,
185
- "created_at" => self.created_at,
186
- "modified_by" => self.modified_by.eid,
187
- "modified_at" => self.modified_at
177
+ "is_graded" => self.graded?,
188
178
  }
179
+ if not self.submitted?
180
+ result.delete("submitted_at")
181
+ result.delete("is_graded")
182
+ end
183
+ result
189
184
  end
190
185
 
191
186
  def summary_serialization
192
187
  {
193
188
  "id" => self.id,
194
- "assignment_id" => self.assignment.id,
195
- "submitter" => self.submitter.eid,
196
- "submitted" => self.submitted?
189
+ "assignment_id" => self.assignment_id,
190
+ "submitter" => User.get_eid(self.submitter_id),
191
+ "is_submitted" => self.submitted?
192
+ }
193
+ end
194
+
195
+ def assignment_summary_serialization
196
+ {
197
+ "id" => self.id,
198
+ "submitter" => User.get_eid(self.submitter_id),
199
+ "is_submitted" => self.submitted?
197
200
  }
198
201
  end
199
202
  end
@@ -211,7 +214,7 @@ module SakaiInfo
211
214
  @@cache = {}
212
215
  def self.find(id)
213
216
  if @@cache[id].nil?
214
- row = DB.connect[:assignment_content].filter(:content_id => id).first
217
+ row = DB.connect[:assignment_content].where(:content_id => id).first
215
218
  if row.nil?
216
219
  raise ObjectNotFoundException.new(AssignmentContent, id)
217
220
  end
@@ -224,7 +227,7 @@ module SakaiInfo
224
227
 
225
228
  def self.find_by_user_id(user_id)
226
229
  contents = []
227
- DB.connect[:assignment_content].filter(:context => user_id).all.each do |row|
230
+ DB.connect[:assignment_content].where(:context => user_id).all.each do |row|
228
231
  id = row[:content_id]
229
232
  context = row[:context]
230
233
  xml = ""
@@ -235,7 +238,7 @@ module SakaiInfo
235
238
  end
236
239
 
237
240
  def self.count_by_user_id(user_id)
238
- DB.connect[:assignment_content].filter(:context => user_id).count
241
+ DB.connect[:assignment_content].where(:context => user_id).count
239
242
  end
240
243
 
241
244
  # getters
@@ -2,7 +2,7 @@
2
2
  # - sakai-info command line tool support
3
3
  #
4
4
  # Created 2012-02-19 daveadams@gmail.com
5
- # Last updated 2012-02-26 daveadams@gmail.com
5
+ # Last updated 2012-02-29 daveadams@gmail.com
6
6
  #
7
7
  # https://github.com/daveadams/sakai-info
8
8
  #
@@ -19,8 +19,13 @@ module SakaiInfo
19
19
  "quiz" => Quiz,
20
20
  "quiz-section" => QuizSection,
21
21
  "quiz-item" => QuizItem,
22
+ "quiz-attempt" => QuizAttempt,
23
+ "quiz-attempt-item" => QuizAttemptItem,
24
+ "quiz-attempt-item-attachment" => QuizAttemptItemAttachment,
22
25
  "qpool" => QuestionPool,
23
26
  "question-pool" => QuestionPool,
27
+ "assignment" => Assignment,
28
+ "assignment-submission" => AssignmentSubmission,
24
29
  }
25
30
  end
26
31
  end
@@ -2,7 +2,7 @@
2
2
  # - sakai-info command line help
3
3
  #
4
4
  # Created 2012-02-19 daveadams@gmail.com
5
- # Last updated 2012-02-26 daveadams@gmail.com
5
+ # Last updated 2012-02-29 daveadams@gmail.com
6
6
  #
7
7
  # https://github.com/daveadams/sakai-info
8
8
  #
@@ -21,11 +21,22 @@ sakai-info #{VERSION}
21
21
  Object commands:
22
22
  user User information
23
23
  site Site information
24
+
24
25
  quiz Quiz aka Assessment information, pending or published
25
26
  quiz-section Quiz section information, pending or published
26
27
  quiz-item Quiz item information, pending or published
28
+ quiz-attempt Quiz attempt information
29
+ quiz-attempt-item
30
+ Information on attempted quiz items
31
+ quiz-attempt-item-attachment
32
+ Information on file attachments to attempted quiz items
33
+
27
34
  question-pool Question Pool information
28
35
 
36
+ assignment Assignment information
37
+ assignment-submission
38
+ Assignment submission information
39
+
29
40
  Misc commands:
30
41
  test Tests configured database connections
31
42
  help Prints general help
@@ -45,11 +56,17 @@ sakai-info #{VERSION}
45
56
  trace log to STDOUT.
46
57
 
47
58
  Options that work on most object types:
59
+ --dbrow
60
+ Print the raw database fields in addition to the usual summary.
61
+
48
62
  --dbrow-only
49
63
  Print only the raw database fields for the object requested.
50
64
 
51
65
  --mod
52
- Print creation and modification users and timestamps.
66
+ Print creation and modification user EIDs and timestamps.
67
+
68
+ --mod-details
69
+ Print creation and modification user details and timestamps.
53
70
 
54
71
  --all
55
72
  Print all possible information (other than dbrow)
@@ -126,13 +143,12 @@ sakai-info quiz
126
143
  include additional information:
127
144
 
128
145
  --sections Print section summary list
146
+ --attempts Print summary of user quiz attempts
129
147
  --mod Print creation/modification info
130
148
  --all Print all possible details
131
- --dbrow Print the raw database fields
132
149
 
133
150
  Not yet implemented:
134
151
  --items Print summary of items on the quiz
135
- --attempts Print summary of user quiz attempts (for published quizzes)
136
152
  EOF
137
153
 
138
154
  "quiz-section" => <<EOF,
@@ -147,7 +163,6 @@ sakai-info quiz-section
147
163
  --items Print summary of items in the section
148
164
  --mod Print creation/modification info
149
165
  --all Print all possible details
150
- --dbrow Print the raw database fields
151
166
  EOF
152
167
 
153
168
  "quiz-item" => <<EOF,
@@ -161,7 +176,6 @@ sakai-info quiz-item
161
176
 
162
177
  --mod Print creation/modification info
163
178
  --all Print all possible details
164
- --dbrow Print the raw database fields
165
179
  EOF
166
180
 
167
181
  "question-pool" => <<EOF,
@@ -174,7 +188,6 @@ sakai-info question-pool
174
188
  may be passed to include additional information:
175
189
 
176
190
  --mod Print creation/modification info
177
- --dbrow Print the raw database fields
178
191
 
179
192
  Not yet implemented:
180
193
  --items Print summary of items in the pool
@@ -182,6 +195,69 @@ sakai-info question-pool
182
195
  --all Print all possible details
183
196
  EOF
184
197
 
198
+ "quiz-attempt" => <<EOF,
199
+ sakai-info quiz-attempt
200
+
201
+ Usage: sakai-info quiz-attempt <id> [<options>]
202
+
203
+ Prints information about the quiz attempt ID specified. Additional options
204
+ may be passed to include additional information:
205
+
206
+ --items Print list of attempted items
207
+ --all Print all possible details
208
+ EOF
209
+
210
+
211
+ "quiz-attempt-item" => <<EOF,
212
+ sakai-info quiz-attempt-item
213
+
214
+ Usage: sakai-info quiz-attempt-item <id> [<options>]
215
+
216
+ Prints information about the quiz attempt item ID specified. Additional
217
+ options may be passed to include additional information:
218
+
219
+ --attachments Print a list of file attachments, if any
220
+ --all Print all possible details
221
+ EOF
222
+
223
+ "quiz-attempt-item-attachment" => <<EOF,
224
+ sakai-info quiz-attempt-item-attachment
225
+
226
+ Usage: sakai-info quiz-attempt-item-attachment <id> [<options>]
227
+
228
+ Prints information about the quiz attempt item attachment ID specified.
229
+ Additional options may be passed to include additional information:
230
+
231
+ --mod Print creation/modification info
232
+ --all Print all possible details
233
+ EOF
234
+
235
+ "assignment" => <<EOF,
236
+ sakai-info assignment
237
+
238
+ Usage: sakai-info assignment <id> [<options>]
239
+
240
+ Prints information about the assignment ID specified. Additional options
241
+ may be passed to include additional information:
242
+
243
+ --submissions Print summary of all submissions
244
+ --xml Print the raw XML
245
+ --mod Print creation/modification info
246
+ --all Print all possible details
247
+ EOF
248
+
249
+ "assignment-submission" => <<EOF,
250
+ sakai-info assignment-submission
251
+
252
+ Usage: sakai-info assignment-submission <id> [<options>]
253
+
254
+ Prints information about the assignment submission ID specified. Additional
255
+ options may be passed to include additional information:
256
+
257
+ --xml Print the raw XML
258
+ --mod Print creation/modification info
259
+ --all Print all possible details
260
+ EOF
185
261
  }
186
262
 
187
263
  def self.help(topic = :default, io = STDOUT)
@@ -61,10 +61,19 @@ module SakaiInfo
61
61
 
62
62
  def mod_serialization
63
63
  {
64
+ "created_at" => self.created_at,
65
+ "created_by" => User.get_eid(self.created_by_id),
66
+ "modified_at" => self.modified_at,
67
+ "modified_by" => User.get_eid(self.modified_by_id),
68
+ }
69
+ end
70
+
71
+ def mod_details_serialization
72
+ {
73
+ "created_at" => self.created_at,
64
74
  "created_by" => self.created_by.serialize(:summary),
65
- "created_at" => self.created_at.strftime("%Y-%m-%d %H:%M:%S"),
75
+ "modified_at" => self.modified_at,
66
76
  "modified_by" => self.modified_by.serialize(:summary),
67
- "modified_at" => self.modified_at.strftime("%Y-%m-%d %H:%M:%S")
68
77
  }
69
78
  end
70
79
  }
@@ -2,7 +2,7 @@
2
2
  # SakaiInfo::Quiz library
3
3
  #
4
4
  # Created 2012-02-17 daveadams@gmail.com
5
- # Last updated 2012-02-26 daveadams@gmail.com
5
+ # Last updated 2012-02-28 daveadams@gmail.com
6
6
  #
7
7
  # https://github.com/daveadams/sakai-info
8
8
  #
@@ -107,11 +107,17 @@ module SakaiInfo
107
107
  "title" => self.title,
108
108
  "site" => nil,
109
109
  "type" => self.quiz_type,
110
- "section_count" => self.section_count
110
+ "section_count" => self.section_count,
111
+ "attempt_count" => nil
111
112
  }
112
113
  if not self.site.nil?
113
114
  result["site"] = self.site.serialize(:summary)
114
115
  end
116
+ if self.respond_to? :attempt_count
117
+ result["attempt_count"] = self.attempt_count
118
+ else
119
+ result.delete("attempt_count")
120
+ end
115
121
  result
116
122
  end
117
123
 
@@ -140,6 +146,10 @@ module SakaiInfo
140
146
  "sections" => self.sections.collect{|s|s.serialize(:quiz_summary)}
141
147
  }
142
148
  end
149
+
150
+ def self.all_serializations
151
+ [:default, :sections]
152
+ end
143
153
  end
144
154
 
145
155
  class PendingQuiz < Quiz
@@ -223,6 +233,24 @@ module SakaiInfo
223
233
  def quiz_type
224
234
  "published"
225
235
  end
236
+
237
+ def attempt_count
238
+ @attempt_count ||= QuizAttempt.count_by_quiz_id(self.id)
239
+ end
240
+
241
+ def attempts
242
+ @attempts ||= QuizAttempt.find_by_quiz_id(self.id)
243
+ end
244
+
245
+ def attempts_serialization
246
+ {
247
+ "attempts" => self.attempts.collect { |a| a.serialize(:quiz_summary) }
248
+ }
249
+ end
250
+
251
+ def self.all_serializations
252
+ [:default, :sections, :attempts]
253
+ end
226
254
  end
227
255
 
228
256
  class QuizSection < SakaiObject
@@ -256,7 +284,7 @@ module SakaiInfo
256
284
  begin
257
285
  @@cache[id] = PublishedQuizSection.find(id)
258
286
  rescue ObjectNotFoundException
259
- raise ObjectNotFoundException(QuizSection, id)
287
+ raise ObjectNotFoundException.new(QuizSection, id)
260
288
  end
261
289
  end
262
290
  end
@@ -347,7 +375,7 @@ module SakaiInfo
347
375
  if @@cache[id].nil?
348
376
  row = DB.connect[:sam_section_t].where(:sectionid => id).first
349
377
  if row.nil?
350
- raise ObjectNotFoundException(PendingQuizSection, id)
378
+ raise ObjectNotFoundException.new(PendingQuizSection, id)
351
379
  end
352
380
 
353
381
  @@cache[id] = PendingQuizSection.new(row)
@@ -367,7 +395,7 @@ module SakaiInfo
367
395
  if @@cache[id].nil?
368
396
  row = DB.connect[:sam_publishedsection_t].where(:sectionid => id).first
369
397
  if row.nil?
370
- raise ObjectNotFoundException(PublishedQuizSection, id)
398
+ raise ObjectNotFoundException.new(PublishedQuizSection, id)
371
399
  end
372
400
 
373
401
  @@cache[id] = PublishedQuizSection.new(row)
@@ -409,7 +437,7 @@ module SakaiInfo
409
437
  begin
410
438
  @@cache[id] = PublishedQuizItem.find(id)
411
439
  rescue ObjectNotFoundException
412
- raise ObjectNotFoundException(QuizItem, id)
440
+ raise ObjectNotFoundException.new(QuizItem, id)
413
441
  end
414
442
  end
415
443
  end
@@ -500,7 +528,7 @@ module SakaiInfo
500
528
  if @@cache[id].nil?
501
529
  row = DB.connect[:sam_item_t].where(:itemid => id).first
502
530
  if row.nil?
503
- raise ObjectNotFoundException(PendingQuizItem, id)
531
+ raise ObjectNotFoundException.new(PendingQuizItem, id)
504
532
  end
505
533
 
506
534
  @@cache[id] = PendingQuizItem.new(row)
@@ -520,7 +548,7 @@ module SakaiInfo
520
548
  if @@cache[id].nil?
521
549
  row = DB.connect[:sam_publisheditem_t].where(:itemid => id).first
522
550
  if row.nil?
523
- raise ObjectNotFoundException(PublishedQuizItem, id)
551
+ raise ObjectNotFoundException.new(PublishedQuizItem, id)
524
552
  end
525
553
 
526
554
  @@cache[id] = PublishedQuizItem.new(row)
@@ -532,4 +560,295 @@ module SakaiInfo
532
560
  "published"
533
561
  end
534
562
  end
563
+
564
+ # class QuizItemAttachment < SakaiObject
565
+ # end
566
+
567
+ # class PendingQuizItemAttachment < QuizItemAttachment
568
+ # end
569
+
570
+ # class PublishedQuizItemAttachment < QuizItemAttachment
571
+ # end
572
+
573
+ class QuizAttempt < SakaiObject
574
+ attr_reader :dbrow, :submitted_at, :total_auto_score, :status, :attempted_at
575
+ attr_reader :time_elapsed, :comments, :user_id, :quiz_id
576
+
577
+ def initialize(dbrow)
578
+ @dbrow = dbrow
579
+
580
+ @id = dbrow[:assessmentgradingid]
581
+ @submitted_at = dbrow[:submitteddate]
582
+ @user_id = dbrow[:agentid]
583
+ @quiz_id = dbrow[:publishedassessmentid]
584
+ @total_auto_score = dbrow[:totalautoscore]
585
+ @status = dbrow[:status]
586
+ @attempted_at = dbrow[:attempted_at]
587
+ @time_elapsed = dbrow[:timeelapsed]
588
+ @is_auto_submitted = dbrow[:is_auto_submitted]
589
+ @is_late = dbrow[:islate]
590
+ @comments = dbrow[:comments]
591
+ end
592
+
593
+ @@cache = {}
594
+ def self.find(id)
595
+ id = id.to_s
596
+ if @@cache[id].nil?
597
+ row = DB.connect[:sam_assessmentgrading_t].where(:assessmentgradingid => id).first
598
+ if row.nil?
599
+ raise ObjectNotFoundException.new(QuizAttempt, id)
600
+ end
601
+
602
+ @@cache[id] = QuizAttempt.new(row)
603
+ end
604
+ @@cache[id]
605
+ end
606
+
607
+ def self.query_by_quiz_id(quiz_id)
608
+ DB.connect[:sam_assessmentgrading_t].where(:publishedassessmentid => quiz_id)
609
+ end
610
+
611
+ def self.count_by_quiz_id(quiz_id)
612
+ QuizAttempt.query_by_quiz_id(quiz_id).count
613
+ end
614
+
615
+ def self.find_by_quiz_id(quiz_id)
616
+ QuizAttempt.query_by_quiz_id(quiz_id).
617
+ all.collect { |row| QuizAttempt.new(row) }
618
+ end
619
+
620
+ def items
621
+ @items ||= QuizAttemptItem.find_by_attempt_id(self.id)
622
+ end
623
+
624
+ def item_count
625
+ @item_count ||= QuizAttemptItem.count_by_attempt_id(self.id)
626
+ end
627
+
628
+ def user
629
+ @user ||= User.find(@user_id)
630
+ end
631
+
632
+ def quiz
633
+ @quiz ||= PublishedQuiz.find(@quiz_id)
634
+ end
635
+
636
+ def auto_submitted?
637
+ @is_auto_submitted == 1
638
+ end
639
+
640
+ def late?
641
+ @is_late == 1
642
+ end
643
+
644
+ def default_serialization
645
+ {
646
+ "id" => self.id,
647
+ "user" => self.user.serialize(:summary),
648
+ "quiz" => self.quiz.serialize(:summary),
649
+ "item_count" => self.item_count,
650
+ "submitted_at" => self.submitted_at,
651
+ "is_auto_submitted" => self.auto_submitted?,
652
+ "is_late" => self.late?,
653
+ "status" => self.status,
654
+ "total_auto_score" => self.total_auto_score
655
+ }
656
+ end
657
+
658
+ def summary_serialization
659
+ {
660
+ "id" => self.id,
661
+ "eid" => User.get_eid(self.user_id),
662
+ "quiz_id" => self.quiz_id,
663
+ "status" => self.status
664
+ }
665
+ end
666
+
667
+ def quiz_summary_serialization
668
+ {
669
+ "id" => self.id,
670
+ "eid" => User.get_eid(self.user_id),
671
+ "status" => self.status
672
+ }
673
+ end
674
+
675
+ def items_serialization
676
+ {
677
+ "items" => self.items.collect { |i| i.serialize(:attempt_summary) }
678
+ }
679
+ end
680
+
681
+ def self.all_serializations
682
+ [:default, :items]
683
+ end
684
+ end
685
+
686
+ class QuizAttemptItem < SakaiObject
687
+ attr_reader :dbrow, :submitted_at, :answer, :user_id, :item_id, :attempt_id
688
+
689
+ def initialize(dbrow)
690
+ @dbrow = dbrow
691
+
692
+ @id = dbrow[:itemgradingid]
693
+ @submitted_at = dbrow[:submitteddate]
694
+ @user_id = dbrow[:agentid]
695
+ @attempt_id = dbrow[:assessmentgradingid]
696
+ @item_id = dbrow[:publisheditemid]
697
+ @answer = dbrow[:answertext]
698
+ end
699
+
700
+ def user
701
+ @user ||= User.find(@user_id)
702
+ end
703
+
704
+ def attempt
705
+ @attempt ||= QuizAttempt.find(@attempt_id)
706
+ end
707
+
708
+ def item
709
+ @item ||= PublishedQuizItem.find(@item_id)
710
+ end
711
+
712
+ @@cache = {}
713
+ def self.find(id)
714
+ id = id.to_s
715
+ if @@cache[id].nil?
716
+ row = DB.connect[:sam_itemgrading_t].where(:itemgradingid => id).first
717
+ if row.nil?
718
+ raise ObjectNotFoundException.new(QuizAttemptItem, id)
719
+ end
720
+
721
+ @@cache[id] = QuizAttemptItem.new(row)
722
+ end
723
+ @@cache[id]
724
+ end
725
+
726
+ def self.query_by_attempt_id(attempt_id)
727
+ DB.connect[:sam_itemgrading_t].where(:assessmentgradingid => attempt_id)
728
+ end
729
+
730
+ def self.count_by_attempt_id(attempt_id)
731
+ QuizAttemptItem.query_by_attempt_id(attempt_id).count
732
+ end
733
+
734
+ def self.find_by_attempt_id(attempt_id)
735
+ QuizAttemptItem.query_by_attempt_id(attempt_id).
736
+ all.collect { |row| QuizAttemptItem.new(row) }
737
+ end
738
+
739
+ def attachments
740
+ @attachments ||=
741
+ QuizAttemptItemAttachment.find_by_quiz_attempt_item_id(self.id)
742
+ end
743
+
744
+ def default_serialization
745
+ {
746
+ "id" => self.id,
747
+ "user" => self.user.serialize(:summary),
748
+ "attempt" => self.attempt.serialize(:summary),
749
+ "item" => self.item.serialize(:summary),
750
+ "answer" => self.answer,
751
+ "attachment_count" => self.attachments.length
752
+ }
753
+ end
754
+
755
+ def summary_serialization
756
+ {
757
+ "id" => self.id,
758
+ "eid" => User.get_eid(self.user_id),
759
+ "attempt_id" => self.attempt_id,
760
+ "item_id" => self.item_id
761
+ }
762
+ end
763
+
764
+ def attempt_summary_serialization
765
+ {
766
+ "id" => self.id,
767
+ "item_id" => self.item_id
768
+ }
769
+ end
770
+
771
+ def attachments_serialization
772
+ {
773
+ "attachments" => self.attachments.collect{|a|a.serialize(:attempt_item_summary)}
774
+ }
775
+ end
776
+
777
+ def self.all_serializations
778
+ [:default, :attachments]
779
+ end
780
+ end
781
+
782
+ class QuizAttemptItemAttachment < SakaiObject
783
+ attr_reader :dbrow, :status, :filepath, :filename, :filesize, :mimetype
784
+ attr_reader :description, :quiz_attempt_item_id
785
+
786
+ include ModProps
787
+ created_by_key :createdby
788
+ created_at_key :createddate
789
+ modified_by_key :lastmodifiedby
790
+ modified_at_key :lastmodifieddate
791
+
792
+ def initialize(dbrow)
793
+ @dbrow = dbrow
794
+
795
+ @id = dbrow[:mediaid]
796
+ @quiz_attempt_item_id = dbrow[:itemgradingid]
797
+ @status = dbrow[:status]
798
+ @filepath = dbrow[:location]
799
+ @filename = dbrow[:filename]
800
+ @filesize = dbrow[:filesize]
801
+ @mimetype = dbrow[:mimetype]
802
+ @description = dbrow[:description]
803
+ end
804
+
805
+ def quiz_attempt_item
806
+ @quiz_attempt_item ||= QuizAttemptItem.find(@quiz_attempt_item_id)
807
+ end
808
+
809
+ def self.find_by_quiz_attempt_item_id(quiz_attempt_item_id)
810
+ DB.connect[:sam_media_t].where(:itemgradingid => quiz_attempt_item_id).
811
+ all.collect { |row| QuizAttemptItemAttachment.new(row) }
812
+ end
813
+
814
+ @@cache = {}
815
+ def self.find(id)
816
+ id = id.to_s
817
+ if @@cache[id].nil?
818
+ row = DB.connect[:sam_media_t].where(:mediaid => id).first
819
+ if row.nil?
820
+ raise ObjectNotFoundException.new(QuizAttemptItemAttachment, id)
821
+ end
822
+
823
+ @@cache[id] = QuizAttemptItemAttachment.new(row)
824
+ end
825
+ @@cache[id]
826
+ end
827
+
828
+ def default_serialization
829
+ {
830
+ "id" => self.id,
831
+ "filename" => self.filename,
832
+ "mimetype" => self.mimetype,
833
+ "filesize" => self.filesize,
834
+ "status" => self.status,
835
+ "quiz_attempt_item" => self.quiz_attempt_item.serialize(:summary)
836
+ }
837
+ end
838
+
839
+ def summary_serialization
840
+ {
841
+ "id" => self.id,
842
+ "filename" => self.filename,
843
+ "quiz_attempt_item_id" => self.quiz_attempt_item_id
844
+ }
845
+ end
846
+
847
+ def attempt_item_summary_serialization
848
+ {
849
+ "id" => self.id,
850
+ "filename" => self.filename
851
+ }
852
+ end
853
+ end
535
854
  end
@@ -2,7 +2,7 @@
2
2
  # SakaiInfo::SakaiObject
3
3
  #
4
4
  # Created 2012-02-15 daveadams@gmail.com
5
- # Last updated 2012-02-26 daveadams@gmail.com
5
+ # Last updated 2012-02-29 daveadams@gmail.com
6
6
  #
7
7
  # https://github.com/daveadams/sakai-info
8
8
  #
@@ -50,6 +50,14 @@ module SakaiInfo
50
50
  end
51
51
  end
52
52
 
53
+ def dbrow_only_serialization
54
+ if not self.dbrow_serialization["dbrow"].nil?
55
+ self.dbrow_serialization["dbrow"]
56
+ else
57
+ {}
58
+ end
59
+ end
60
+
53
61
  def default_serialization
54
62
  object_type_serialization
55
63
  end
@@ -2,7 +2,7 @@
2
2
  # SakaiInfo::SakaiXMLEntity
3
3
  #
4
4
  # Created 2012-02-16 daveadams@gmail.com
5
- # Last updated 2012-02-18 daveadams@gmail.com
5
+ # Last updated 2012-02-29 daveadams@gmail.com
6
6
  #
7
7
  # https://github.com/daveadams/sakai-info
8
8
  #
@@ -39,12 +39,22 @@ module SakaiInfo
39
39
  class SakaiXMLEntity < SakaiObject
40
40
  attr_reader :xml, :xmldoc, :attributes, :properties
41
41
 
42
- private
42
+ include ModProps
43
+ created_by_key :_xml_entity_created_by
44
+ created_at_key :_xml_entity_created_at
45
+ modified_by_key :_xml_entity_modified_by
46
+ modified_at_key :_xml_entity_modified_at
47
+
43
48
  # this method parses the universal XML field for all entities
44
49
  # down to two collections: attributes (XML attributes defined in the
45
50
  # top-level tag) and properties (<property> tags inside the top-level
46
51
  # tag). Properties are generally base64 encoded
47
52
  def parse_xml
53
+ if @xml.nil?
54
+ @xml = ""
55
+ REXML::Document.new(@dbrow[:xml].read).write(@xml, 2)
56
+ end
57
+
48
58
  @xmldoc = REXML::Document.new(@xml)
49
59
  @attributes = {}
50
60
  @xmldoc.root.attributes.keys.each do |att_name|
@@ -60,67 +70,62 @@ module SakaiInfo
60
70
  if prop_encoding == "BASE64"
61
71
  prop_value = Base64.decode64(prop_value)
62
72
  else
63
- raise UnrecognizedPropertyEncodingException(prop_name, prop_encoding, prop_value)
73
+ raise UnrecognizedPropertyEncodingException.new(prop_name, prop_encoding, prop_value)
64
74
  end
65
75
  @properties[prop_name] = prop_value
66
76
  end
67
- end
68
-
69
- def format_entity_date(raw)
70
- raw.gsub(/^(....)(..)(..)(..)(..)(..).*$/, '\1-\2-\3 \4:\5:\6')
71
- end
72
-
73
- public
74
- # standard property for all entities
75
- def created_by
76
- @created_by ||= User.find(@properties["CHEF:creator"])
77
- end
78
-
79
- # standard property for all entities
80
- def modified_by
81
- @modified_by ||= User.find(@properties["CHEF:modifiedby"])
82
- end
83
-
84
- # standard property for all entities
85
- def created_at
86
- format_entity_date(@properties["DAV:creationdate"])
87
- end
88
77
 
89
- # standard property for all entities
90
- def modified_at
91
- format_entity_date(@properties["DAV:getlastmodified"])
78
+ @dbrow[:_xml_entity_created_by] = @properties["CHEF:creator"]
79
+ @dbrow[:_xml_entity_modified_by] = @properties["CHEF:modifiedby"]
80
+ @dbrow[:_xml_entity_created_at] = format_entity_date(@properties["DAV:creationdate"])
81
+ @dbrow[:_xml_entity_modified_at] = format_entity_date(@properties["DAV:getlastmodified"])
92
82
  end
93
83
 
94
- # by default, serialize all the common properties
95
- def default_serialization
96
- {
97
- "id" => self.id,
98
- "created_by" => self.created_by,
99
- "created_at" => self.created_at,
100
- "modified_by" => self.modified_by,
101
- "modified_at" => self.modified_at
102
- }
84
+ def format_entity_date(raw)
85
+ if raw =~ /^(....)(..)(..)(..)(..)(..).*$/
86
+ # I believe these are usually in UTC
87
+ Time.utc($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i).getlocal
88
+ end
103
89
  end
104
90
 
105
91
  # serialize all attributes
106
92
  def attributes_serialization
107
93
  {
108
- "attributes" => @attributes
94
+ "attributes" => self.attributes
109
95
  }
110
96
  end
111
97
 
112
98
  # serialize all properties
113
99
  def properties_serialization
114
100
  {
115
- "properties" => @properties
101
+ "properties" => self.properties
116
102
  }
117
103
  end
118
104
 
119
105
  # xml dump serialization option
120
106
  def xml_serialization
121
107
  {
122
- "xml" => xml
108
+ "xml" => self.xml
109
+ }
110
+ end
111
+
112
+ # tweak the dbrow out since we hacked dbrow for other purposes
113
+ # and since the xml field doesn't display typically
114
+ def dbrow_serialization
115
+ dbrow = super["dbrow"]
116
+ dbrow[:xml] = self.xml
117
+ dbrow.delete(:_xml_entity_created_by)
118
+ dbrow.delete(:_xml_entity_created_at)
119
+ dbrow.delete(:_xml_entity_modified_by)
120
+ dbrow.delete(:_xml_entity_modified_at)
121
+
122
+ {
123
+ "dbrow" => dbrow
123
124
  }
124
125
  end
126
+
127
+ def self.all_serializations
128
+ [ :default, :attributes, :properties, :xml ]
129
+ end
125
130
  end
126
131
  end
@@ -2,7 +2,7 @@
2
2
  # SakaiInfo::Site library
3
3
  #
4
4
  # Created 2012-02-17 daveadams@gmail.com
5
- # Last updated 2012-02-26 daveadams@gmail.com
5
+ # Last updated 2012-02-28 daveadams@gmail.com
6
6
  #
7
7
  # https://github.com/daveadams/sakai-info
8
8
  #
@@ -273,7 +273,7 @@ module SakaiInfo
273
273
  "pending_quiz_count" => self.pending_quiz_count,
274
274
  "published_quiz_count" => self.published_quiz_count,
275
275
  "assignment_count" => self.assignment_count,
276
- "announcement_count" => self.announcement_count,
276
+ # "announcement_count" => self.announcement_count,
277
277
  "gradebook_item_count" => (self.gradebook.nil? ? 0 : self.gradebook.item_count),
278
278
  "forum_count" => self.forum_count
279
279
  }
@@ -445,7 +445,7 @@ module SakaiInfo
445
445
  if @@cache[id].nil?
446
446
  row = DB.connect[:sakai_site_page].where(:page_id => id).first
447
447
  if row.nil?
448
- raise ObjectNotFoundException(Page, id)
448
+ raise ObjectNotFoundException.new(Page, id)
449
449
  end
450
450
 
451
451
  site = Site.find(row[:site_id])
@@ -1,3 +1,3 @@
1
1
  module SakaiInfo
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sakai-info
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 17
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 3
9
- - 0
10
- version: 0.3.0
9
+ - 1
10
+ version: 0.3.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - David Adams
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-02-26 00:00:00 -06:00
18
+ date: 2012-02-29 00:00:00 -06:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency