sakai-info 0.3.0 → 0.3.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 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