@@ -108,6 +108,105 @@ public static function fetch_marking(stdClass $user): ?array {
108
108
// Marking.
109
109
$ marking = [];
110
110
111
+ foreach ($ courses as $ course ) {
112
+ // Skip hidden courses.
113
+ if (!$ course ->visible ) {
114
+ continue ;
115
+ }
116
+ // Skip none current course.
117
+ if (!self ::is_course_current ($ course )) {
118
+ continue ;
119
+ }
120
+ // Skip if no summative assessments.
121
+ if (!$ summatives = assess_type::get_assess_type_records_by_courseid ($ course ->id , assess_type::ASSESS_TYPE_SUMMATIVE )) {
122
+ continue ;
123
+ }
124
+
125
+ $ modinfo = get_fast_modinfo ($ course ->id );
126
+ $ mods = $ modinfo ->get_cms ();
127
+ // Mod ids array to check cmid exists.
128
+ $ cmids = [];
129
+ foreach ($ mods as $ mod ) {
130
+ $ cmids [] = $ mod ->id ;
131
+ }
132
+
133
+ // Loop through assessments for this course.
134
+ foreach ($ summatives as $ summative ) {
135
+
136
+ // Check this is a course mod.
137
+ if ($ summative ->cmid != 0 ) {
138
+ // Skip mods where cmid is not in the course.
139
+ if (!in_array ($ summative ->cmid , $ cmids )) {
140
+ continue ;
141
+ }
142
+
143
+ // Begin to build mod data for template.
144
+ $ cmid = $ summative ->cmid ;
145
+ $ mod = $ modinfo ->get_cm ($ cmid );
146
+
147
+
148
+ // Skip hidden mods.
149
+ if (!$ mod ->visible ) {
150
+ continue ;
151
+ }
152
+
153
+ // Template.
154
+ $ assess = new stdClass ;
155
+ $ assess ->cmid = $ cmid ;
156
+ $ assess ->modname = $ mod ->modname ;
157
+ // Turnitin assessments may have multiple parts. WIP
158
+ if ($ assess ->modname === 'turnitintooltwo ' ) {
159
+ $ turnitinparts = self ::get_turnitin_parts ($ mod );
160
+ foreach ($ turnitinparts as $ turnitinpart ) {
161
+ $ turnitin = clone $ assess ;
162
+ $ turnitin ->partid = $ turnitinpart ->id ;
163
+ $ turnitin = self ::get_mod_data ($ mod , $ turnitin );
164
+
165
+ // Check mod has required marking (only set when there is a due date).
166
+ if (isset ($ turnitin ->requiremarking )) {
167
+ // TODO - what is expensive here that we can do after sort and limit?
168
+ $ turnitin ->name = $ mod ->name . ' ' . $ turnitinpart ->partname ;;
169
+ $ turnitin ->coursename = $ course ->fullname ;
170
+ $ turnitin ->url = new moodle_url ('/mod/ ' . $ mod ->modname . '/view.php ' , ['id ' => $ cmid ]);
171
+ $ turnitin ->icon = course_summary_exporter::get_course_image ($ course );
172
+ $ marking [] = $ turnitin ;
173
+ }
174
+ }
175
+
176
+ } else {
177
+ // Get due date and require marking.
178
+ $ assess = self ::get_mod_data ($ mod , $ assess );
179
+ }
180
+
181
+ // Check mod has required marking (only set when there is a due date).
182
+ if (isset ($ assess ->requiremarking )) {
183
+ // TODO - what is expensive here that we can do after sort and limit?
184
+ $ assess ->name = $ mod ->name ;
185
+ $ assess ->coursename = $ course ->fullname ;
186
+ $ assess ->url = new moodle_url ('/mod/ ' . $ mod ->modname . '/view.php ' , ['id ' => $ cmid ]);
187
+ $ assess ->icon = course_summary_exporter::get_course_image ($ course );
188
+ $ marking [] = $ assess ;
189
+ }
190
+ }
191
+ }
192
+ }
193
+
194
+ // Sort and return data.
195
+ if ($ marking ) {
196
+ usort ($ marking , function ($ a , $ b ) {
197
+ return $ a ->unixtimestamp <=> $ b ->unixtimestamp ;
198
+ });
199
+
200
+ return array_slice ($ marking , 0 , 5 );
201
+ }
202
+ return null ;
203
+ }
204
+ public static function fetch_marking0 (stdClass $ user ): ?array {
205
+ // User courses.
206
+ $ courses = enrol_get_all_users_courses ($ user ->id , false , ['enddate ' ]);
207
+ // Marking.
208
+ $ marking = [];
209
+
111
210
foreach ($ courses as $ course ) {
112
211
// Skip hidden courses.
113
212
if (!$ course ->visible ) {
@@ -183,49 +282,146 @@ public static function fetch_marking(stdClass $user): ?array {
183
282
/**
184
283
* Return mod data - due date & require marking.
185
284
*
186
- * TODO - turnitin, quiz.
187
- *
188
285
* @param cm_info $mod
189
286
* @param stdClass $assess
190
287
*/
191
288
public static function get_mod_data ($ mod , $ assess ): ?stdClass {
192
- global $ CFG ;
193
- // Mods have different fields for due date, and require marking .
289
+ global $ CFG , $ DB ;
290
+ // Mods have different fields for due date.
194
291
switch ($ mod ->modname ) {
195
292
case 'assign ' :
196
-
197
293
// Check mod due date is relevant.
198
294
$ duedate = self ::duedate_in_range ($ mod ->customdata ['duedate ' ]);
199
- if (!$ duedate ) {
200
- return null ;
201
- }
295
+ break ;
296
+ case 'quiz ' :
297
+ $ record = $ DB ->get_record ('quiz ' , ['id ' => $ mod ->instance ], 'timeclose ' );
298
+ // Check if mod due date is present and relevant.
299
+ $ duedate = isset ($ record ->timeclose ) ? self ::duedate_in_range ($ record ->timeclose ) : false ;
300
+ break ;
301
+ case 'turnitintooltwo ' :
302
+ $ record = $ DB ->get_record ('turnitintooltwo_parts ' , ['id ' => $ assess ->partid ], 'dtdue ' );
303
+ // Check if mod due date is present and relevant.
304
+ $ duedate = isset ($ record ->dtdue ) ? self ::duedate_in_range ($ record ->dtdue ) : false ;
305
+ break ;
306
+ default :
307
+ return null ;
308
+ }
309
+ if (!$ duedate ) {
310
+ return null ;
311
+ }
202
312
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 ;
313
+ // Add dates.
314
+ $ assess ->unixtimestamp = $ duedate ;
315
+ $ assess ->duedate = date ('jS M ' , $ duedate );
316
+
317
+ // Require marking.
318
+ $ assess ->requiremarking = self ::get_required_markings ($ mod );
319
+ if (!$ assess ->requiremarking ) {
320
+ return null ;
321
+ }
322
+ $ assess ->markingurl = new moodle_url ('/mod/ ' . $ mod ->modname . '/view.php ' ,
323
+ ['id ' => $ assess ->cmid , 'action ' => 'grader ' ]
324
+ );
325
+
326
+ // Return template data.
327
+ return $ assess ;
328
+
329
+ }
330
+
331
+ /**
332
+ * Get the required markings for an assessment module.
333
+ *
334
+ * @param cm_info $mod
335
+ * @return int
336
+ * @throws dml_exception
337
+ */
338
+ protected static function get_required_markings (cm_info $ mod ) {
339
+ global $ CFG , $ DB ;
340
+
341
+ // Assignments provide a way to count submissions that needs grading.
342
+ if ($ mod ->modname === 'assign ' ) {
343
+ require_once ($ CFG ->dirroot .'/mod/assign/locallib.php ' );
344
+ $ context = context_module::instance ($ mod ->id );
345
+ $ assignment = new assign ($ context , $ mod , $ mod ->course );
346
+ return $ assignment ->count_submissions_need_grading ();
347
+ }
348
+
349
+ // For modules other than assignments get the student IDs that have submissions.
350
+ if ($ submissions = self ::get_module_submissions ($ mod )) {
351
+ $ sql = "SELECT DISTINCT gg.userid
352
+ FROM {grade_grades} gg
353
+ WHERE gg.itemid = :modid AND gg.finalgrade > :finalgrade " ;
354
+ $ params = ['modid ' => $ mod ->id , 'finalgrade ' => -1 ];
355
+
356
+ // Execute the query.
357
+ $ studentids = $ DB ->get_fieldset_sql ($ sql , $ params );
358
+ // Count and return all student IDs in submission that are not (yet) to be found in gradings.
359
+ $ missinggrades = 0 ;
360
+ foreach ($ submissions as $ submitterid ) {
361
+ if (!in_array ($ submitterid , $ studentids )) {
362
+ $ missinggrades ++;
214
363
}
215
- $ assess ->markingurl = new moodle_url ('/mod/ ' . $ mod ->modname . '/view.php ' ,
216
- ['id ' => $ assess ->cmid , 'action ' => 'grader ' ]
217
- );
364
+ }
365
+ return $ missinggrades ;
366
+ }
367
+ // No submissions - no missing grades.
368
+ return 0 ;
369
+ }
218
370
219
- // Return template data.
220
- return $ assess ;
371
+ /**
372
+ * Get an array of distinct student IDs with submissions for a given module.
373
+ *
374
+ * @param cm_info $mod
375
+ * @return array
376
+ */
377
+ public static function get_module_submissions (cm_info $ mod ): array {
378
+ global $ DB ;
221
379
222
- // TODO - quiz - 'timeclose' ?.
223
- case 'quiz ' :
224
- return null ;
225
- // TODO - turnitin.
226
- default :
227
- return null ;
380
+ if ($ mod ) {
381
+ switch ($ mod ->modname ) {
382
+ case 'assign ' :
383
+ // No need to support here, as assignments provide their own methods to count submissions and gradings.
384
+ return [];
385
+ case 'lesson ' :
386
+ $ sql = "SELECT DISTINCT userid FROM {lesson_attempts} WHERE lessonid = :lessonid " ;
387
+ $ params = ['lessonid ' => $ mod ->instance , 'correct ' => 1 ];
388
+ break ;
389
+ case 'quiz ' :
390
+ $ sql = "SELECT DISTINCT userid FROM {quiz_attempts} WHERE quiz = :quiz AND state = :state " ;
391
+ $ params = ['quiz ' => $ mod ->instance , 'state ' => 'finished ' ];
392
+ break ;
393
+ case 'turnitintooltwo ' :
394
+ $ sql = "SELECT DISTINCT userid FROM {turnitintooltwo_submissions} WHERE turnitintooltwoid = :turnitintooltwoid " ;
395
+ $ params = ['turnitintooltwoid ' => $ mod ->instance ];
396
+ break ;
397
+ case 'scorm ' :
398
+ return [];
399
+ case 'workshop ' :
400
+ $ sql = "SELECT DISTINCT authorid FROM {workshop_submissions} WHERE workshopid = :workshopid " ;
401
+ $ params = ['workshopid ' => $ mod ->instance ];
402
+ break ;
403
+ default :
404
+ return [];
405
+ }
406
+ return $ DB ->get_fieldset_sql ($ sql , $ params );
228
407
}
408
+ return [];
409
+ }
410
+
411
+ /**
412
+ * Get separate parts for a turnitin module.
413
+ *
414
+ * @param cm_info $mod
415
+ * @return array
416
+ * @throws dml_exception
417
+ */
418
+ public static function get_turnitin_parts (cm_info $ mod ) {
419
+ global $ DB ;
420
+
421
+ $ sql = "SELECT * FROM {turnitintooltwo_parts} WHERE turnitintooltwoid = :iteminstance " ;
422
+ $ params = ['iteminstance ' => $ mod ->instance ];
423
+ // Execute the query and return the result.
424
+ return $ DB ->get_records_sql ($ sql , $ params );
229
425
}
230
426
231
427
/**
0 commit comments