@@ -153,10 +153,30 @@ public static function fetch_marking(stdClass $user): ?array {
153
153
$ assess = new stdClass ;
154
154
$ assess ->cmid = $ cmid ;
155
155
$ assess ->modname = $ mod ->modname ;
156
- // Get due date and require marking.
157
- $ assess = self ::get_mod_data ($ mod , $ assess );
156
+ // Turnitin assessments may have multiple parts, so treat them separately.
157
+ if ($ assess ->modname === 'turnitintooltwo ' ) {
158
+ $ turnitinparts = self ::get_turnitin_parts ($ mod );
159
+ foreach ($ turnitinparts as $ turnitinpart ) {
160
+ $ turnitin = clone $ assess ;
161
+ $ turnitin ->partid = $ turnitinpart ->id ;
162
+ $ turnitin = self ::get_mod_data ($ mod , $ turnitin );
163
+
164
+ // Check mod has required marking (only set when there is a due date).
165
+ if (isset ($ turnitin ->requiremarking )) {
166
+ // TODO - what is expensive here that we can do after sort and limit?
167
+ $ turnitin ->name = $ mod ->name . ' ' . $ turnitinpart ->partname ;;
168
+ $ turnitin ->coursename = $ course ->fullname ;
169
+ $ turnitin ->url = new moodle_url ('/mod/ ' . $ mod ->modname . '/view.php ' , ['id ' => $ cmid ]);
170
+ $ turnitin ->icon = course_summary_exporter::get_course_image ($ course );
171
+ $ marking [] = $ turnitin ;
172
+ }
173
+ }
174
+ } else {
175
+ // Get due date and require marking.
176
+ $ assess = self ::get_mod_data ($ mod , $ assess );
177
+ }
158
178
159
- // Check mod has require marking (only set when there is a due date).
179
+ // Check mod has required marking (only set when there is a due date).
160
180
if (isset ($ assess ->requiremarking )) {
161
181
// TODO - what is expensive here that we can do after sort and limit?
162
182
$ assess ->name = $ mod ->name ;
@@ -183,49 +203,146 @@ public static function fetch_marking(stdClass $user): ?array {
183
203
/**
184
204
* Return mod data - due date & require marking.
185
205
*
186
- * TODO - turnitin, quiz.
187
- *
188
206
* @param cm_info $mod
189
207
* @param stdClass $assess
190
208
*/
191
209
public static function get_mod_data ($ mod , $ assess ): ?stdClass {
192
- global $ CFG ;
193
- // Mods have different fields for due date, and require marking .
210
+ global $ CFG , $ DB ;
211
+ // Mods have different fields for due date.
194
212
switch ($ mod ->modname ) {
195
213
case 'assign ' :
196
-
197
214
// Check mod due date is relevant.
198
215
$ duedate = self ::duedate_in_range ($ mod ->customdata ['duedate ' ]);
199
- if (!$ duedate ) {
200
- return null ;
201
- }
216
+ break ;
217
+ case 'quiz ' :
218
+ $ record = $ DB ->get_record ('quiz ' , ['id ' => $ mod ->instance ], 'timeclose ' );
219
+ // Check if mod due date is present and relevant.
220
+ $ duedate = isset ($ record ->timeclose ) ? self ::duedate_in_range ($ record ->timeclose ) : false ;
221
+ break ;
222
+ case 'turnitintooltwo ' :
223
+ $ record = $ DB ->get_record ('turnitintooltwo_parts ' , ['id ' => $ assess ->partid ], 'dtdue ' );
224
+ // Check if mod due date is present and relevant.
225
+ $ duedate = isset ($ record ->dtdue ) ? self ::duedate_in_range ($ record ->dtdue ) : false ;
226
+ break ;
227
+ default :
228
+ return null ;
229
+ }
230
+ if (!$ duedate ) {
231
+ return null ;
232
+ }
233
+
234
+ // Add dates.
235
+ $ assess ->unixtimestamp = $ duedate ;
236
+ $ assess ->duedate = date ('jS M ' , $ duedate );
237
+
238
+ // Require marking.
239
+ $ assess ->requiremarking = self ::get_required_markings ($ mod );
240
+ if (!$ assess ->requiremarking ) {
241
+ return null ;
242
+ }
243
+ $ assess ->markingurl = new moodle_url ('/mod/ ' . $ mod ->modname . '/view.php ' ,
244
+ ['id ' => $ assess ->cmid , 'action ' => 'grader ' ]
245
+ );
246
+
247
+ // Return template data.
248
+ return $ assess ;
249
+
250
+ }
251
+
252
+ /**
253
+ * Get the required markings for an assessment module.
254
+ *
255
+ * @param cm_info $mod
256
+ * @return int
257
+ * @throws dml_exception
258
+ */
259
+ protected static function get_required_markings (cm_info $ mod ) {
260
+ global $ CFG , $ DB ;
261
+
262
+ // Assignments provide a way to count submissions that needs grading.
263
+ if ($ mod ->modname === 'assign ' ) {
264
+ require_once ($ CFG ->dirroot .'/mod/assign/locallib.php ' );
265
+ $ context = context_module::instance ($ mod ->id );
266
+ $ assignment = new assign ($ context , $ mod , $ mod ->course );
267
+ return $ assignment ->count_submissions_need_grading ();
268
+ }
202
269
203
- // Add dates.
204
- $ assess ->unixtimestamp = $ duedate ;
205
- $ assess ->duedate = date ('jS M ' , $ duedate );
206
-
207
- // Require marking.
208
- require_once ($ CFG ->dirroot .'/mod/assign/locallib.php ' );
209
- $ context = context_module::instance ($ mod ->id );
210
- $ assignment = new assign ($ context , $ mod , $ mod ->course );
211
- $ assess ->requiremarking = $ assignment ->count_submissions_need_grading ();
212
- if (!$ assess ->requiremarking ) {
213
- return null ;
270
+ // For modules other than assignments get the student IDs that have submissions.
271
+ if ($ submissions = self ::get_module_submissions ($ mod )) {
272
+ $ sql = "SELECT DISTINCT gg.userid
273
+ FROM {grade_grades} gg
274
+ WHERE gg.itemid = :modid AND gg.finalgrade > :finalgrade " ;
275
+ $ params = ['modid ' => $ mod ->id , 'finalgrade ' => -1 ];
276
+
277
+ // Execute the query.
278
+ $ studentids = $ DB ->get_fieldset_sql ($ sql , $ params );
279
+ // Count and return all student IDs in submission that are not (yet) to be found in gradings.
280
+ $ missinggrades = 0 ;
281
+ foreach ($ submissions as $ submitterid ) {
282
+ if (!in_array ($ submitterid , $ studentids )) {
283
+ $ missinggrades ++;
214
284
}
215
- $ assess ->markingurl = new moodle_url ('/mod/ ' . $ mod ->modname . '/view.php ' ,
216
- ['id ' => $ assess ->cmid , 'action ' => 'grader ' ]
217
- );
285
+ }
286
+ return $ missinggrades ;
287
+ }
288
+ // No submissions - no missing grades.
289
+ return 0 ;
290
+ }
218
291
219
- // Return template data.
220
- return $ assess ;
292
+ /**
293
+ * Get an array of distinct student IDs with submissions for a given module.
294
+ *
295
+ * @param cm_info $mod
296
+ * @return array
297
+ */
298
+ public static function get_module_submissions (cm_info $ mod ): array {
299
+ global $ DB ;
221
300
222
- // TODO - quiz - 'timeclose' ?.
223
- case 'quiz ' :
224
- return null ;
225
- // TODO - turnitin.
226
- default :
227
- return null ;
301
+ if ($ mod ) {
302
+ switch ($ mod ->modname ) {
303
+ case 'assign ' :
304
+ // No need to support here, as assignments provide their own methods to count submissions and gradings.
305
+ return [];
306
+ case 'lesson ' :
307
+ $ sql = "SELECT DISTINCT userid FROM {lesson_attempts} WHERE lessonid = :lessonid " ;
308
+ $ params = ['lessonid ' => $ mod ->instance , 'correct ' => 1 ];
309
+ break ;
310
+ case 'quiz ' :
311
+ $ sql = "SELECT DISTINCT userid FROM {quiz_attempts} WHERE quiz = :quiz AND state = :state " ;
312
+ $ params = ['quiz ' => $ mod ->instance , 'state ' => 'finished ' ];
313
+ break ;
314
+ case 'turnitintooltwo ' :
315
+ $ sql = "SELECT DISTINCT userid FROM {turnitintooltwo_submissions} WHERE turnitintooltwoid = :turnitintooltwoid " ;
316
+ $ params = ['turnitintooltwoid ' => $ mod ->instance ];
317
+ break ;
318
+ case 'scorm ' :
319
+ return [];
320
+ case 'workshop ' :
321
+ $ sql = "SELECT DISTINCT authorid FROM {workshop_submissions} WHERE workshopid = :workshopid " ;
322
+ $ params = ['workshopid ' => $ mod ->instance ];
323
+ break ;
324
+ default :
325
+ return [];
326
+ }
327
+ return $ DB ->get_fieldset_sql ($ sql , $ params );
228
328
}
329
+ return [];
330
+ }
331
+
332
+ /**
333
+ * Get separate parts for a turnitin module.
334
+ *
335
+ * @param cm_info $mod
336
+ * @return array
337
+ * @throws dml_exception
338
+ */
339
+ public static function get_turnitin_parts (cm_info $ mod ) {
340
+ global $ DB ;
341
+
342
+ $ sql = "SELECT * FROM {turnitintooltwo_parts} WHERE turnitintooltwoid = :iteminstance " ;
343
+ $ params = ['iteminstance ' => $ mod ->instance ];
344
+ // Execute the query and return the result.
345
+ return $ DB ->get_records_sql ($ sql , $ params );
229
346
}
230
347
231
348
/**
0 commit comments