Attendance   F
last analyzed

Complexity

Total Complexity 341

Size/Duplication

Total Lines 3089
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 1630
dl 0
loc 3089
rs 0.8
c 0
b 0
f 0
wmc 341

58 Methods

Rating   Name   Duplication   Size   Complexity  
F getAttendanceData() 0 167 25
B getNumberOfAttendances() 0 46 7
A attendance_edit() 0 72 5
A get_attendance_by_id() 0 16 3
A changeVisibility() 0 9 1
A attendance_delete() 0 14 2
A attendance_add() 0 53 4
A attendance_restore() 0 43 5
B setAttendanceForm() 0 82 9
A get_user_score() 0 40 3
B get_faults_average_inside_courses() 0 42 6
A get_done_attendance_calendar() 0 11 2
A getGroupListByAttendanceCalendar() 0 10 1
B getAttendanceBaseInLogin() 0 44 6
B attendance_repeat_calendar_add() 0 41 7
B getAttendanceLoginTable() 0 47 8
F attendance_sheet_export_to_pdf() 0 150 25
A set_session_id() 0 3 1
A get_attendance_weight() 0 3 1
A deleteAttendanceCalendarGroup() 0 7 1
A get_date_time() 0 3 1
A is_all_attendance_calendar_done() 0 17 3
A get_course_id() 0 3 1
A set_description() 0 3 1
A set_attendance_qualify_title() 0 3 1
B get_faults_of_user() 0 45 6
C get_users_rel_course() 0 100 14
A get_session_id() 0 3 1
A getAttendanceList() 0 8 1
A is_locked_attendance() 0 4 1
A attendance_calendar_edit() 0 30 3
A get_faults_average_by_course() 0 36 5
B exportAttendanceLogin() 0 45 6
A get_next_attendance_calendar_id() 0 20 2
C getAttendanceLogin() 0 73 11
A getAttendanceCalendarGroup() 0 10 1
A lock() 0 14 1
A addAttendanceCalendarToGroup() 0 28 5
A get_attendance_qualify_title() 0 3 1
A get_count_dates_inside_attendance_calendar() 0 16 2
C get_users_attendance_sheet() 0 84 14
B updateUsersResults() 0 70 6
A saveAttendanceSheetLog() 0 18 1
A getNextAttendanceCalendarDatetime() 0 19 2
A set_attendance_weight() 0 3 1
A get_description() 0 3 1
A set_date_time() 0 3 1
B get_number_of_attendance_calendar() 0 79 5
A attendance_calendar_delete() 0 49 4
F getCalendarSheet() 0 466 71
C getCoursesWithAttendance() 0 166 14
A get_attendance_calendar_by_id() 0 17 3
A set_course_id() 0 3 1
F get_attendance_calendar() 0 95 17
A get_title() 0 3 1
A set_title() 0 3 1
B attendance_sheet_add() 0 91 7
A attendance_calendar_add() 0 25 2

How to fix   Complexity   

Complex Class

Complex classes like Attendance often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Attendance, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\Course;
6
use Chamilo\CoreBundle\Entity\Session;
7
use Chamilo\CoreBundle\Entity\User;
8
use Chamilo\CoreBundle\Framework\Container;
9
use Chamilo\CourseBundle\Entity\CAttendance;
10
use Chamilo\CourseBundle\Entity\CAttendanceCalendar;
11
use Chamilo\CourseBundle\Entity\CAttendanceSheetLog;
12
use Doctrine\Common\Collections\Criteria;
13
use Chamilo\CoreBundle\Component\Utils\ActionIcon;
14
use Chamilo\CoreBundle\Component\Utils\ObjectIcon;
15
use Chamilo\CoreBundle\Component\Utils\StateIcon;
16
17
class Attendance
18
{
19
    public const DONE_ATTENDANCE_LOG_TYPE = 'done_attendance_sheet';
20
    public const UPDATED_ATTENDANCE_LOG_TYPE = 'updated_attendance_sheet';
21
    public const LOCKED_ATTENDANCE_LOG_TYPE = 'locked_attendance_sheet';
22
    public $category_id;
23
    private $session_id;
24
    private $course_id;
25
    private $date_time;
26
    private $title;
27
    private $description;
28
    private $attendance_qualify_title;
29
    private $attendance_weight;
30
31
    /**
32
     * Get attendance list only the id, title and attendance_qualify_max fields.
33
     *
34
     * @return CAttendance[]
35
     */
36
    public function getAttendanceList(Course $course, Session $session = null)
37
    {
38
        $repo = Container::getAttendanceRepository();
39
        $qb = $repo->getResourcesByCourse($course, $session, null);
40
        //$qb->select('resource');
41
        $qb->andWhere('resource.active = 1');
42
43
        return $qb->getQuery()->getResult();
44
45
        /*$table = Database::get_course_table(TABLE_ATTENDANCE);
46
        $course_id = (int) $course_id;
47
        if (empty($course_id)) {
48
            $course_id = api_get_course_int_id();
49
        }
50
51
        $session_id = !empty($session_id) ? (int) $session_id : api_get_session_id();
52
        $condition_session = api_get_session_condition($session_id);
53
54
        // Get attendance data
55
        $sql = "SELECT iid, title, attendance_qualify_max
56
                FROM $table
57
                WHERE active = 1 $condition_session ";
58
        $result = Database::query($sql);
59
        $data = [];
60
        if (Database::num_rows($result) > 0) {
61
            while ($row = Database::fetch_assoc($result)) {
62
                $data[$row['iid']] = $row;
63
            }
64
        }
65
66
        return $data;*/
67
    }
68
69
    /**
70
     * Get the total number of attendance inside current course and current session.
71
     *
72
     * @return int
73
     *
74
     * @see SortableTable#get_total_number_of_items()
75
     */
76
    public static function getNumberOfAttendances()
77
    {
78
        $repo = Container::getAttendanceRepository();
79
80
        $course = api_get_course_entity(api_get_course_int_id());
81
        $session = api_get_session_entity(api_get_session_id());
82
        $group = api_get_group_entity(api_get_group_id());
83
84
        $qb = $repo->getResourcesByCourse($course, $session, $group);
85
        $qb->select('count(resource)');
86
        $qb->andWhere('resource.active <> 2');
87
88
        if ((isset($_GET['isStudentView']) && 'true' == $_GET['isStudentView']) ||
89
            !api_is_allowed_to_edit(null, true)
90
        ) {
91
            $qb
92
                ->andWhere('resource.active = :active')
93
                ->setParameter('active', 1);
94
            //$active_plus = ' AND att.active = 1';
95
        }
96
97
        return $qb->getQuery()->getSingleScalarResult();
98
99
        $tbl_attendance = Database::get_course_table(TABLE_ATTENDANCE);
0 ignored issues
show
Unused Code introduced by
$tbl_attendance = Databa...table(TABLE_ATTENDANCE) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
100
        $session_id = api_get_session_id();
101
        $condition_session = '';
102
103
        $active_plus = '';
104
        if ((isset($_GET['isStudentView']) && 'true' == $_GET['isStudentView']) ||
105
            !api_is_allowed_to_edit(null, true)
106
        ) {
107
            $active_plus = ' AND att.active = 1';
108
        }
109
110
        $sql = "SELECT COUNT(att.iid) AS total_number_of_items
111
                FROM $tbl_attendance att
112
                WHERE
113
                      active <> 2 $active_plus $condition_session  ";
114
        /*$active = (int) $active;
115
        if ($active === 1 || $active === 0) {
116
            $sql .= "AND att.active = $active";
117
        }*/
118
        $res = Database::query($sql);
119
        $obj = Database::fetch_object($res);
120
121
        return (int) $obj->total_number_of_items;
122
    }
123
124
    /**
125
     * Get the attendances to display on the current page (fill the sortable-table).
126
     *
127
     * @param   int     offset of first user to recover
128
     * @param   int     Number of users to get
129
     * @param   int     Column to sort on
130
     * @param   string  Order (ASC,DESC)
131
     *
132
     * @see SortableTable#get_table_data($from)
133
     *
134
     * @return array
135
     */
136
    public static function getAttendanceData(
137
        $from,
138
        $number_of_items,
139
        $column,
140
        $direction
141
    ) {
142
        $repo = Container::getAttendanceRepository();
143
144
        $course = api_get_course_entity(api_get_course_int_id());
145
        $session = api_get_session_entity(api_get_session_id());
146
        $group = api_get_group_entity(api_get_group_id());
147
148
        $qb = $repo->getResourcesByCourse($course, $session, $group);
149
        $qb->select('resource');
150
        $qb->andWhere('resource.active <> 2');
151
152
        if ((isset($_GET['isStudentView']) && 'true' == $_GET['isStudentView']) ||
153
            !api_is_allowed_to_edit(null, true)
154
        ) {
155
            $qb
156
                ->andWhere('resource.active = :active')
157
                ->setParameter('active', 1);
158
            //$active_plus = ' AND att.active = 1';
159
        }
160
161
        $qb
162
            ->setFirstResult($from)
163
            ->setMaxResults($number_of_items)
164
        ;
165
166
        if (!in_array($direction, ['ASC', 'DESC'])) {
167
            $direction = 'ASC';
168
        }
169
170
        $attendances = $qb->getQuery()->getResult();
171
172
        $allowDelete = api_get_setting('allow_delete_attendance');
173
        $student_param = '';
174
        $studentRequestId = isset($_GET['student_id']) ? (int) $_GET['student_id'] : 0;
175
        if (api_is_drh() && !empty($studentRequestId)) {
176
            $student_param = '&student_id='.$studentRequestId;
177
        }
178
179
        $list = [];
180
        /** @var CAttendance $attendance */
181
        foreach ($attendances as $attendance) {
182
            $row = [];
183
            $id = $attendance->getIid();
184
            $title = $attendance->getTitle();
185
            $active = $attendance->getActive();
186
            $session_star = '';
187
            /*if ($session_id == $attendance[6]) {
188
                $session_star = api_get_session_image($session_id, $user_info['status']);
189
            }*/
190
            if (1 == $active) {
191
                $isDrhOfCourse = CourseManager::isUserSubscribedInCourseAsDrh(
192
                    api_get_user_id(),
193
                    api_get_course_info()
194
                ) || api_is_drh();
195
                if ($isDrhOfCourse || api_is_allowed_to_edit(null, true)) {
196
                    // Link to edit
197
                    $row[1] = '<a href="index.php?'.api_get_cidreq().'&action=attendance_sheet_list&attendance_id='.$id.$student_param.'">'.$title.'</a>'.$session_star;
198
                } else {
199
                    // Link to view
200
                    $row[1] = '<a href="index.php?'.api_get_cidreq().'&action=attendance_sheet_list_no_edit&attendance_id='.$id.$student_param.'">'.$title.'</a>'.$session_star;
201
                }
202
            } else {
203
                $row[1] = '<a class="muted" href="index.php?'.api_get_cidreq().'&action=attendance_sheet_list&attendance_id='.$id.$student_param.'">'.$title.'</a>'.$session_star;
204
            }
205
206
            if (1 == $active) {
207
                $row[3] = '<center>'.$attendance->getAttendanceQualifyMax().'</center>';
208
            } else {
209
                $row[3] = '<center><span class="muted">'.$attendance->getAttendanceQualifyMax().'</span></center>';
210
            }
211
            $row[2] = '';
212
            $row[3] = '<center>'.$row[3].'</center>';
213
            if (api_is_allowed_to_edit(null, true)) {
214
                $actions = '';
215
                $actions .= '<center>';
216
                if (api_is_platform_admin()) {
217
                    $actions .= '<a href="index.php?'.api_get_cidreq().'&action=attendance_edit&attendance_id='.$id.'">'.
218
                        Display::getMdiIcon(ActionIcon::EDIT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Edit')).'</a>&nbsp;';
219
                    // Visible
220
                    if (1 == $active) {
221
                        $actions .= '<a href="index.php?'.api_get_cidreq().'&action=attendance_set_invisible&attendance_id='.$id.'">'.
222
                            Display::getMdiIcon(StateIcon::ACTIVE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Hide')).'</a>';
223
                    } else {
224
                        $actions .= '<a href="index.php?'.api_get_cidreq().'&action=attendance_set_visible&attendance_id='.$id.'">'.
225
                            Display::getMdiIcon(StateIcon::INACTIVE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Show')).'</a>';
226
                        $row[2] = '<span class="muted">'.$attendance->getDescription().'</span>';
227
                    }
228
                    if ('true' === $allowDelete) {
229
                        $actions .= '<a href="index.php?'.api_get_cidreq().'&action=attendance_delete&attendance_id='.$id.'">'.
230
                            Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Delete')).'</a>';
231
                    }
232
                } else {
233
                    $is_locked_attendance = self::is_locked_attendance($id);
234
                    if ($is_locked_attendance) {
235
                        $actions .= Display::getMdiIcon(ActionIcon::EDIT, 'ch-tool-icon-disabled', null, ICON_SIZE_SMALL, get_lang('Edit')).'&nbsp;';
236
                        $actions .= Display::getMdiIcon(StateIcon::ACTIVE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Hide'));
237
                    } else {
238
                        $actions .= '<a href="index.php?'.api_get_cidreq().'&action=attendance_edit&attendance_id='.$id.'">'.
239
                            Display::getMdiIcon(ActionIcon::EDIT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Edit')).'</a>&nbsp;';
240
241
                        if (1 == $active) {
242
                            $actions .= ' <a href="index.php?'.api_get_cidreq().'&action=attendance_set_invisible&attendance_id='.$id.'">'.
243
                                Display::getMdiIcon(StateIcon::ACTIVE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Hide')).'</a>';
244
                        } else {
245
                            $actions .= ' <a href="index.php?'.api_get_cidreq().'&action=attendance_set_visible&attendance_id='.$id.'">'.
246
                                Display::getMdiIcon(StateIcon::INACTIVE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Show')).'</a>';
247
                            $row[2] = '<span class="muted">'.$attendance->getDescription().'</span>';
248
                        }
249
                        if ('true' === $allowDelete) {
250
                            $actions .= ' <a href="index.php?'.api_get_cidreq().'&action=attendance_delete&attendance_id='.$id.'">'.
251
                                Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Delete')).'</a>';
252
                        }
253
                    }
254
                }
255
256
                // display lock/unlock icon
257
                $is_done_all_calendar = self::is_all_attendance_calendar_done($attendance);
258
259
                if ($is_done_all_calendar) {
260
                    $locked = $attendance->getLocked();
261
                    if (0 == $locked) {
262
                        if (api_is_platform_admin()) {
263
                            $message_alert = get_lang('Are you sure you want to lock the attendance?');
264
                        } else {
265
                            $message_alert = get_lang('The attendance is not locked, which means your teacher is still able to modify it.');
266
                        }
267
                        $actions .= '&nbsp;<a
268
                            onclick="javascript:if(!confirm(\''.$message_alert.'\')) return false;"
269
                            href="index.php?'.api_get_cidreq().'&action=lock_attendance&attendance_id='.$id.'">'.
270
                            Display::getMdiIcon(ActionIcon::UNLOCK, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Lock attendance')).'</a>';
271
                    } else {
272
                        if (api_is_platform_admin()) {
273
                            $actions .= '&nbsp;<a
274
                            onclick="javascript:if(!confirm(\''.get_lang('Are you sure you want to unlock the attendance?').'\')) return false;"
275
                            href="index.php?'.api_get_cidreq().'&action=unlock_attendance&attendance_id='.$id.'">'.
276
                                    Display::getMdiIcon(ActionIcon::LOCK, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Unlock attendance')).'</a>';
277
                        } else {
278
                            $actions .= '&nbsp;'.Display::getMdiIcon(ActionIcon::LOCK, 'ch-tool-icon-disabled', null, ICON_SIZE_SMALL, get_lang('Locked attendance'));
279
                        }
280
                    }
281
                }
282
                $actions .= '</center>';
283
284
                $list[] = [
285
                    $id,
286
                    $row[1],
287
                    $row[2],
288
                    $row[3],
289
                    $actions,
290
                ];
291
            } else {
292
                $id = '&nbsp;';
293
                $list[] = [
294
                    $id,
295
                    $row[1],
296
                    $row[2],
297
                    $row[3],
298
                ];
299
            }
300
        }
301
302
        return $list;
303
    }
304
305
    /**
306
     * Get the attendances by id to display on the current page.
307
     *
308
     * @param int $attendanceId
309
     *
310
     * @return array attendance data
311
     */
312
    public function get_attendance_by_id($attendanceId)
313
    {
314
        $tbl_attendance = Database::get_course_table(TABLE_ATTENDANCE);
315
        $attendanceId = (int) $attendanceId;
316
317
        $attendance_data = [];
318
        $sql = "SELECT * FROM $tbl_attendance
319
                WHERE iid = $attendanceId";
320
        $res = Database::query($sql);
321
        if (Database::num_rows($res) > 0) {
322
            while ($row = Database::fetch_array($res)) {
323
                $attendance_data = $row;
324
            }
325
        }
326
327
        return $attendance_data;
328
    }
329
330
    /**
331
     * Add attendances sheet inside table. This is the *list of* dates, not
332
     * a specific date in itself.
333
     *
334
     * @param  bool   true for adding link in gradebook or false otherwise (optional)
335
     *
336
     * @return int last attendance id
337
     */
338
    public function attendance_add($link_to_gradebook = false)
339
    {
340
        $courseInfo = api_get_course_info();
341
        $tableLink = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
342
        $sessionId = api_get_session_id();
343
        $courseCode = $courseInfo['code'];
344
        $titleGradebook = (string) $this->attendance_qualify_title;
345
        $valueCalification = 0;
346
        $weightCalification = api_float_val($this->attendance_weight);
347
        $course = api_get_course_entity();
348
        $attendance = new CAttendance();
349
        $attendance
350
            ->setTitle($this->title)
351
            ->setDescription($this->description)
352
            ->setAttendanceQualifyTitle($titleGradebook)
353
            ->setAttendanceWeight($weightCalification)
354
            ->setParent($course)
355
            ->addCourseLink($course, api_get_session_entity())
356
        ;
357
358
        $repo = Container::getAttendanceRepository();
359
        $repo->create($attendance);
360
        $lastId = $attendance->getIid();
361
362
        // add link to gradebook
363
        if ($link_to_gradebook && !empty($this->category_id)) {
364
            $description = '';
365
            $linkInfo = GradebookUtils::isResourceInCourseGradebook(
366
                $course->getId(),
367
                7,
368
                $lastId,
369
                $sessionId
370
            );
371
            $linkId = $linkInfo['id'];
372
            if (!$linkInfo) {
373
                GradebookUtils::add_resource_to_course_gradebook(
374
                    $this->category_id,
375
                    $courseInfo['real_id'],
376
                    7,
377
                    $lastId,
378
                    $titleGradebook,
379
                    $weightCalification,
380
                    $valueCalification,
381
                    $description,
382
                    1,
383
                    $sessionId
384
                );
385
            } else {
386
                Database::query("UPDATE $tableLink SET weight = $weightCalification WHERE iid = $linkId");
387
            }
388
        }
389
390
        return $lastId;
391
    }
392
393
    /**
394
     * edit attendances inside table.
395
     *
396
     * @param CAttendance $attendance
397
     * @param bool true for adding link in gradebook or false otherwise (optional)
398
     *
399
     * @return int last id
400
     */
401
    public function attendance_edit($attendance, $link_to_gradebook = false)
402
    {
403
        $_course = api_get_course_info();
404
        $table_link = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
405
        $session_id = api_get_session_id();
406
        $course_code = $_course['code'];
407
        $title_gradebook = $this->attendance_qualify_title;
408
        $value_calification = 0;
409
        $weight_calification = api_float_val($this->attendance_weight);
410
411
        if ($attendance) {
0 ignored issues
show
introduced by
$attendance is of type Chamilo\CourseBundle\Entity\CAttendance, thus it always evaluated to true.
Loading history...
412
            $attendanceId = $attendance->getIid();
413
            $attendance
414
                ->setTitle($this->title)
415
                ->setDescription($this->description)
416
                ->setAttendanceQualifyTitle($title_gradebook)
417
                ->setAttendanceWeight($weight_calification)
418
            ;
419
420
            $repo = Container::getAttendanceRepository();
421
            $repo->update($attendance);
422
423
            /*$params = [
424
                'title' => $this->title,
425
                'description' => $this->description,
426
                'attendance_qualify_title' => $title_gradebook,
427
                'attendance_weight' => $weight_calification,
428
            ];
429
            Database::update(
430
                $tbl_attendance,
431
                $params,
432
                ['c_id = ? AND id = ?' => [$course_id, $attendanceId]]
433
            );
434
            api_item_property_update(
435
                $_course,
436
                TOOL_ATTENDANCE,
437
                $attendanceId,
438
                "AttendanceUpdated",
439
                $user_id
440
            );*/
441
442
            // add link to gradebook
443
            if ($link_to_gradebook && !empty($this->category_id)) {
444
                $description = '';
445
                $link_info = GradebookUtils::isResourceInCourseGradebook(
446
                    $_course['real_id'],
447
                    7,
448
                    $attendanceId,
449
                    $session_id
450
                );
451
                if (!$link_info) {
452
                    GradebookUtils::add_resource_to_course_gradebook(
453
                        $this->category_id,
454
                        $_course['real_id'],
455
                        7,
456
                        $attendanceId,
457
                        $title_gradebook,
458
                        $weight_calification,
459
                        $value_calification,
460
                        $description,
461
                        1,
462
                        $session_id
463
                    );
464
                } else {
465
                    Database::query('UPDATE '.$table_link.' SET weight='.$weight_calification.' WHERE iid='.$link_info['id']);
466
                }
467
            }
468
469
            return $attendanceId;
470
        }
471
472
        return null;
473
    }
474
475
    /**
476
     * Restore attendance.
477
     *
478
     * @param int|array one or many attendances id
479
     *
480
     * @return int affected rows
481
     */
482
    public function attendance_restore($attendanceId)
483
    {
484
        $_course = api_get_course_info();
485
        $tbl_attendance = Database::get_course_table(TABLE_ATTENDANCE);
486
        $user_id = api_get_user_id();
487
        $course_id = $_course['real_id'];
488
        if (is_array($attendanceId)) {
489
            foreach ($attendanceId as $id) {
490
                $id = (int) $id;
491
                $sql = "UPDATE $tbl_attendance SET active = 1
492
                        WHERE iid = '$id'";
493
                $result = Database::query($sql);
494
                $affected_rows = Database::affected_rows($result);
495
                if (!empty($affected_rows)) {
496
                    // update row item property table
497
                    /*api_item_property_update(
498
                        $_course,
499
                        TOOL_ATTENDANCE,
500
                        $id,
501
                        'restore',
502
                        $user_id
503
                    );*/
504
                }
505
            }
506
        } else {
507
            $attendanceId = (int) $attendanceId;
508
            $sql = "UPDATE $tbl_attendance SET active = 1
509
                    WHERE iid = '$attendanceId'";
510
            $result = Database::query($sql);
511
            $affected_rows = Database::affected_rows($result);
512
            if (!empty($affected_rows)) {
513
                // update row item property table
514
                /*api_item_property_update(
515
                    $_course,
516
                    TOOL_ATTENDANCE,
517
                    $attendanceId,
518
                    'restore',
519
                    $user_id
520
                );*/
521
            }
522
        }
523
524
        return $affected_rows;
525
    }
526
527
    /**
528
     * Delete attendances.
529
     *
530
     * @param CAttendance $attendance one or many attendances id
531
     *
532
     * @return bool
533
     */
534
    public function attendance_delete(CAttendance $attendance)
535
    {
536
        $allowDeleteAttendance = api_get_setting('allow_delete_attendance');
537
        if ('true' !== $allowDeleteAttendance) {
538
            return false;
539
        }
540
541
        $repo = Container::getAttendanceRepository();
542
        $attendance->setActive(2);
543
        $repo->update($attendance);
544
545
        SkillModel::deleteSkillsFromItem($attendance->getIid(), ITEM_TYPE_ATTENDANCE);
546
547
        return true;
548
549
        /*$_course = api_get_course_info();
550
        $tbl_attendance = Database::get_course_table(TABLE_ATTENDANCE);
551
        $user_id = api_get_user_id();
552
        $course_id = $_course['real_id'];
553
554
        $attendanceId = (int) $attendanceId;
555
        $sql = "UPDATE $tbl_attendance SET active = 2
556
                WHERE c_id = $course_id AND id = '$attendanceId'";
557
558
        $result = Database::query($sql);
559
        $affected_rows = Database::affected_rows($result);
560
        if (!empty($affected_rows)) {
561
            // update row item property table
562
            api_item_property_update(
563
                $_course,
564
                TOOL_ATTENDANCE,
565
                $attendanceId,
566
                "delete",
567
                $user_id
568
            );
569
        }return $affected_rows;
570
        */
571
    }
572
573
    /**
574
     * Changes visibility.
575
     *
576
     * @param CAttendance $attendance one or many attendances id
577
     * @param int         $status
578
     *
579
     * @return int affected rows
580
     */
581
    public function changeVisibility(CAttendance $attendance, $status = 1)
582
    {
583
        $status = (int) $status;
584
585
        $repo = Container::getAttendanceRepository();
586
        $attendance->setActive($status);
587
        $repo->update($attendance);
588
589
        return true;
590
        /*
591
592
        $attendanceId = (int) $attendanceId;
593
        $sql = "UPDATE $tbl_attendance SET active = $status
594
                WHERE c_id = $course_id AND id = '$attendanceId'";
595
        $result = Database::query($sql);
596
        $affected_rows = Database::affected_rows($result);
597
        if (!empty($affected_rows)) {
598
            // update row item property table
599
            api_item_property_update(
600
                $_course,
601
                TOOL_ATTENDANCE,
602
                $attendanceId,
603
                $action,
604
                $user_id
605
            );
606
        }
607
608
        return $affected_rows;
609
        */
610
    }
611
612
    /**
613
     * Lock or unlock an attendance.
614
     *
615
     * @param bool    True to lock or false otherwise
616
     *
617
     * @return bool
618
     */
619
    public function lock(CAttendance $attendance, $lock = true)
620
    {
621
        $repo = Container::getAttendanceRepository();
622
        $attendance->setLocked($lock);
623
        $repo->update($attendance);
624
625
        $this->saveAttendanceSheetLog(
626
            $attendance,
627
            api_get_utc_datetime(time(), false, true),
628
            self::LOCKED_ATTENDANCE_LOG_TYPE,
629
            api_get_user_entity()
630
        );
631
632
        return true;
633
    }
634
635
    /**
636
     * Get registered users inside current course.
637
     *
638
     * @param int $attendanceId attendance id for showing attendance result field (optional)
639
     * @param int $groupId
640
     *
641
     * @return array users data
642
     */
643
    public function get_users_rel_course($attendanceId = 0, $groupId = 0)
644
    {
645
        $current_session_id = api_get_session_id();
646
        $current_course_id = api_get_course_id();
647
        $currentCourseIntId = api_get_course_int_id();
648
        $studentInGroup = [];
649
650
        if (!empty($current_session_id)) {
651
            $a_course_users = CourseManager:: get_user_list_from_course_code(
652
                $current_course_id,
653
                $current_session_id,
654
                '',
655
                'lastname'
656
            );
657
        } else {
658
            $a_course_users = CourseManager:: get_user_list_from_course_code(
659
                $current_course_id,
660
                0,
661
                '',
662
                'lastname'
663
            );
664
        }
665
666
        if (!empty($groupId)) {
667
            $groupInfo = GroupManager::get_group_properties($groupId);
668
            $students = GroupManager::getStudents($groupInfo['iid']);
669
            if (!empty($students)) {
670
                foreach ($students as $student) {
671
                    $studentInGroup[$student['user_id']] = true;
672
                }
673
            }
674
        }
675
676
        // get registered users inside current course
677
        $a_users = [];
678
        foreach ($a_course_users as $key => $user_data) {
679
            $value = [];
680
            $uid = $user_data['user_id'];
681
            $userInfo = api_get_user_info($uid);
682
            $status = $user_data['status'];
683
684
            if (!empty($groupId)) {
685
                if (!isset($studentInGroup[$uid])) {
686
                    continue;
687
                }
688
            }
689
690
            $user_status_in_session = null;
691
            $user_status_in_course = null;
692
693
            if (api_get_session_id()) {
694
                $user_status_in_session = SessionManager::get_user_status_in_course_session(
695
                    $uid,
696
                    $currentCourseIntId,
697
                    $current_session_id
698
                );
699
            } else {
700
                $user_status_in_course = CourseManager::getUserInCourseStatus(
701
                    $uid,
702
                    $currentCourseIntId
703
                );
704
            }
705
706
            // Not taking into account DRH or COURSEMANAGER
707
            if ($uid <= 1 ||
708
                DRH == $status ||
709
                COURSEMANAGER == $user_status_in_course ||
710
                2 == $user_status_in_session
711
            ) {
712
                continue;
713
            }
714
715
            if (!empty($attendanceId)) {
716
                $user_faults = $this->get_faults_of_user(
717
                    $uid,
718
                    $attendanceId,
719
                    $groupId
720
                );
721
                $value['attendance_result'] = $user_faults['faults'].'/'.$user_faults['total'].' ('.$user_faults['faults_porcent'].'%)';
722
                $value['result_color_bar'] = $user_faults['color_bar'];
723
            }
724
725
            $photo = Display::img(
726
                $userInfo['avatar_small'],
727
                $userInfo['complete_name'],
728
                [],
729
                false
730
            );
731
732
            $value['photo'] = $photo;
733
            $value['firstname'] = $user_data['firstname'];
734
            $value['lastname'] = $user_data['lastname'];
735
            $value['username'] = $user_data['username'];
736
            $value['user_id'] = $uid;
737
738
            // Sending only 5 items in the array instead of 60
739
            $a_users[$key] = $value;
740
        }
741
742
        return $a_users;
743
    }
744
745
    /**
746
     * add attendances sheet inside table.
747
     *
748
     * @param int   $calendar_id   attendance calendar id
749
     * @param array $users_present present users during current class
750
     *
751
     * @return int affected rows
752
     */
753
    public function attendance_sheet_add($calendar_id, $users_present, CAttendance $attendance)
754
    {
755
        $tbl_attendance_sheet = Database::get_course_table(TABLE_ATTENDANCE_SHEET);
756
        $tbl_attendance_calendar = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR);
757
758
        $calendar_id = (int) $calendar_id;
759
        $users = $this->get_users_rel_course();
760
        $user_ids = array_keys($users);
761
        $users_absent = array_diff($user_ids, $users_present);
762
        $affected_rows = 0;
763
764
        // get last edit type
765
        $calendar_data = $this->get_attendance_calendar_by_id($calendar_id);
766
        $lastedit_type = self::DONE_ATTENDANCE_LOG_TYPE;
767
        if ($calendar_data['done_attendance']) {
768
            $lastedit_type = self::UPDATED_ATTENDANCE_LOG_TYPE;
769
        }
770
771
        // save users present in class
772
        foreach ($users_present as $user_present) {
773
            $uid = (int) $user_present;
774
            // check if user already was registered with the $calendar_id
775
            $sql = "SELECT user_id FROM $tbl_attendance_sheet
776
                    WHERE user_id = $uid AND attendance_calendar_id = $calendar_id";
777
            $rs = Database::query($sql);
778
            if (0 == Database::num_rows($rs)) {
779
                $sql = "INSERT INTO $tbl_attendance_sheet SET
780
                        user_id = '$uid',
781
                        attendance_calendar_id = '$calendar_id',
782
                        presence = 1,
783
                        signature = ''";
784
                $result = Database::query($sql);
785
786
                $affected_rows += Database::affected_rows($result);
787
            } else {
788
                $sql = "UPDATE $tbl_attendance_sheet
789
                        SET presence = 1
790
                        WHERE
791
                            user_id = $uid AND
792
                            attendance_calendar_id = $calendar_id
793
                        ";
794
                $result = Database::query($sql);
795
                $affected_rows += Database::affected_rows($result);
796
            }
797
        }
798
799
        // save users absent in class
800
        foreach ($users_absent as $user_absent) {
801
            $uid = (int) $user_absent;
802
            // check if user already was registered with the $calendar_id
803
            $sql = "SELECT user_id FROM $tbl_attendance_sheet
804
                    WHERE user_id = $uid AND attendance_calendar_id = '$calendar_id'";
805
            $rs = Database::query($sql);
806
            if (0 == Database::num_rows($rs)) {
807
                $sql = "INSERT INTO $tbl_attendance_sheet SET
808
                        user_id ='$uid',
809
                        attendance_calendar_id = '$calendar_id',
810
                        presence = 0,
811
                        signature = ''";
812
                $result = Database::query($sql);
813
                $affected_rows += Database::affected_rows($result);
814
            } else {
815
                $sql = "UPDATE $tbl_attendance_sheet SET presence = 0
816
                        WHERE
817
                            user_id ='$uid' AND
818
                            attendance_calendar_id = '$calendar_id'";
819
                $result = Database::query($sql);
820
                $affected_rows += Database::affected_rows($result);
821
            }
822
        }
823
824
        // update done_attendance inside attendance calendar table
825
        $sql = "UPDATE $tbl_attendance_calendar SET done_attendance = 1
826
                WHERE  iid = '$calendar_id'";
827
        Database::query($sql);
828
829
        // save users' results
830
        $this->updateUsersResults($user_ids, $attendance);
831
832
        if ($affected_rows) {
833
            //save attendance sheet log
834
            $this->saveAttendanceSheetLog(
835
                $attendance,
836
                api_get_utc_datetime(time(), false, true),
837
                $lastedit_type,
838
                api_get_user_entity(),
839
                api_get_utc_datetime($calendar_data['date_time'], true, true)
840
            );
841
        }
842
843
        return $affected_rows;
844
    }
845
846
    /**
847
     * update users' attendance results.
848
     *
849
     * @param array $user_ids registered users inside current course
850
     */
851
    public function updateUsersResults($user_ids, CAttendance $attendance)
852
    {
853
        $tbl_attendance_sheet = Database::get_course_table(TABLE_ATTENDANCE_SHEET);
854
        $tbl_attendance_result = Database::get_course_table(TABLE_ATTENDANCE_RESULT);
855
        $tbl_attendance = Database::get_course_table(TABLE_ATTENDANCE);
856
        $attendanceId = $attendance->getIid();
857
        // fill results about presence of students
858
        $attendance_calendar = $this->get_attendance_calendar(
859
            $attendanceId,
860
            'all',
861
            null,
862
            null,
863
            true
864
        );
865
        $calendar_ids = [];
866
        // get all dates from calendar by current attendance
867
        foreach ($attendance_calendar as $cal) {
868
            $calendar_ids[] = $cal['iid'];
869
        }
870
871
        // get count of presences by users inside current attendance and save like results
872
        if (count($user_ids) > 0) {
873
            foreach ($user_ids as $uid) {
874
                $uid = (int) $uid;
875
                $count_presences = 0;
876
                if (count($calendar_ids) > 0) {
877
                    $sql = "SELECT count(presence) as count_presences
878
                            FROM $tbl_attendance_sheet
879
                            WHERE
880
                                user_id = $uid AND
881
                                attendance_calendar_id IN (".implode(',', $calendar_ids).') AND
882
                                presence = 1';
883
                    $rs_count = Database::query($sql);
884
                    $row_count = Database::fetch_array($rs_count);
885
                    $count_presences = $row_count['count_presences'];
886
                }
887
888
                // save results
889
                $sql = "SELECT iid FROM $tbl_attendance_result
890
                        WHERE
891
                            user_id = '$uid' AND
892
                            attendance_id = '$attendanceId' ";
893
                $rs_check_result = Database::query($sql);
894
895
                if (Database::num_rows($rs_check_result) > 0) {
896
                    // update result
897
                    $sql = "UPDATE $tbl_attendance_result SET
898
                            score = '$count_presences'
899
                            WHERE
900
                                user_id='$uid' AND
901
                                attendance_id='$attendanceId'";
902
                    Database::query($sql);
903
                } else {
904
                    // insert new result
905
                    $sql = "INSERT INTO $tbl_attendance_result SET
906
                            user_id			= '$uid',
907
                            attendance_id 	= '$attendanceId',
908
                            score			= '$count_presences'";
909
                    Database::query($sql);
910
                    $insertId = Database::insert_id();
911
                }
912
            }
913
        }
914
915
        // update attendance qualify max
916
        $count_done_calendar = self::get_done_attendance_calendar($attendance);
917
        $sql = "UPDATE $tbl_attendance SET
918
                    attendance_qualify_max = '$count_done_calendar'
919
                WHERE iid = '$attendanceId'";
920
        Database::query($sql);
921
    }
922
923
    /**
924
     * update attendance_sheet_log table, is used as history of an attendance sheet.
925
     */
926
    public function saveAttendanceSheetLog(
927
        CAttendance $attendance,
928
        DateTime $lastEditDate,
929
        string $lastEditType,
930
        User $user,
931
        ?DateTime $dateValue = null
932
    ) {
933
        $log = new CAttendanceSheetLog();
934
        $log
935
            ->setAttendance($attendance)
936
            ->setUser($user)
937
            ->setCalendarDateValue($dateValue)
938
            ->setLasteditDate($lastEditDate)
939
            ->setLasteditType($lastEditType)
940
        ;
941
        $em = Database::getManager();
942
        $em->persist($log);
943
        $em->flush();
944
    }
945
946
    /**
947
     * Get number of done attendances inside current sheet.
948
     *
949
     * @return int number of done attendances
950
     */
951
    public static function get_done_attendance_calendar(CAttendance $attendance)
952
    {
953
        $criteria = Criteria::create()->where(
954
            Criteria::expr()->eq('doneAttendance', 1)
955
        );
956
957
        if (!$attendance->getCalendars()) {
958
            return 0;
959
        }
960
961
        return $attendance->getCalendars()->matching($criteria)->count();
962
963
        /*$table = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR);
964
        $attendanceId = (int) $attendanceId;
965
        $course_id = api_get_course_int_id();
966
        $sql = "SELECT count(done_attendance) as count
967
                FROM $table
968
                WHERE
969
                    attendance_id = '$attendanceId' AND
970
                    done_attendance = 1
971
                ";
972
        $rs = Database::query($sql);
973
        $row = Database::fetch_array($rs);
974
        $count = $row['count'];
975
976
        return $count;*/
977
    }
978
979
    /**
980
     * Get results of faults (absents) by user.
981
     *
982
     * @param int $user_id
983
     * @param int $attendanceId
984
     * @param int $groupId
985
     *
986
     * @return array results containing number of faults, total done attendance,
987
     *               percent of faults and color depend on result (red, orange)
988
     */
989
    public function get_faults_of_user($user_id, $attendanceId, $groupId = null)
990
    {
991
        $user_id = (int) $user_id;
992
        $attendanceId = (int) $attendanceId;
993
        $results = [];
994
        $calendar_count = self::get_number_of_attendance_calendar(
995
            $attendanceId,
996
            $groupId,
997
            null,
998
            $user_id
999
        );
1000
        // $total_done_attendance 	= $attendance_data['attendance_qualify_max'];
1001
        $total_done_attendance = self::get_number_of_attendance_calendar(
1002
            $attendanceId,
1003
            $groupId,
1004
            true,
1005
            $user_id
1006
        );
1007
        $attendance_user_score = $this->get_user_score($user_id, $attendanceId, $groupId);
1008
1009
        //This is the main change of the BT#1381
1010
        //$total_done_attendance = $calendar_count;
1011
1012
        // calculate results
1013
        $faults = $total_done_attendance - $attendance_user_score;
1014
1015
        if (empty($calendar_count)) {
1016
            $faults = 0;
1017
        }
1018
1019
        $faults = $faults > 0 ? $faults : 0;
1020
        $faults_porcent = $calendar_count > 0 ? round(($faults * 100) / $calendar_count, 0) : 0;
1021
        $results['faults'] = $faults;
1022
        $results['total'] = $calendar_count;
1023
        $results['faults_porcent'] = $faults_porcent;
1024
        $color_bar = '';
1025
1026
        if ($faults_porcent > 25) {
1027
            $color_bar = '#f28989';
1028
        } elseif ($faults_porcent > 10) {
1029
            $color_bar = '#F90';
1030
        }
1031
        $results['color_bar'] = $color_bar;
1032
1033
        return $results;
1034
    }
1035
1036
    /**
1037
     * Get results of faults average for all courses by user.
1038
     *
1039
     * @param int $user_id
1040
     *
1041
     * @return array results containing number of faults, total done attendance,
1042
     *               percentage of faults and color depend on result (red, orange)
1043
     */
1044
    public function get_faults_average_inside_courses($user_id)
1045
    {
1046
        // get all courses of current user
1047
        $courses = CourseManager::get_courses_list_by_user_id($user_id, true);
1048
        $user_id = (int) $user_id;
1049
        $results = [];
1050
        $total_faults = $total_weight = $porcent = 0;
1051
        foreach ($courses as $course) {
1052
            //$course_code = $course['code'];
1053
            //$course_info = api_get_course_info($course_code);
1054
            $course_id = $course['real_id'];
1055
            $tbl_attendance_result = Database::get_course_table(TABLE_ATTENDANCE_RESULT);
1056
            $attendances = $this->getAttendanceList(api_get_course_entity($course_id));
1057
1058
            foreach ($attendances as $attendance) {
1059
                $attendanceId = $attendance->getIid();
1060
                // get total faults and total weight
1061
                $total_done_attendance = $attendance->getAttendanceQualifyMax();
1062
                $sql = "SELECT score
1063
                        FROM $tbl_attendance_result
1064
                        WHERE
1065
                            user_id = $user_id AND
1066
                            attendance_id = ".$attendanceId;
1067
                $rs = Database::query($sql);
1068
                $score = 0;
1069
                if (Database::num_rows($rs) > 0) {
1070
                    $row = Database::fetch_array($rs);
1071
                    $score = $row['score'];
1072
                }
1073
                $faults = $total_done_attendance - $score;
1074
                $faults = $faults > 0 ? $faults : 0;
1075
                $total_faults += $faults;
1076
                $total_weight += $total_done_attendance;
1077
            }
1078
        }
1079
1080
        $percent = $total_weight > 0 ? round(($total_faults * 100) / $total_weight, 0) : 0;
1081
        $results['faults'] = $total_faults;
1082
        $results['total'] = $total_weight;
1083
        $results['percent'] = $percent;
1084
1085
        return $results;
1086
    }
1087
1088
    /**
1089
     * Get results of faults average by course.
1090
     *
1091
     * @return array results containing number of faults,
1092
     *               total done attendance, percent of faults and color depend on result (red, orange)
1093
     */
1094
    public function get_faults_average_by_course($user_id, Course $course, Session $session = null)
1095
    {
1096
        // Database tables and variables
1097
        $courseId = $course->getId();
1098
        $tbl_attendance_result = Database::get_course_table(TABLE_ATTENDANCE_RESULT);
1099
        $user_id = (int) $user_id;
1100
        $results = [];
1101
        $total_faults = $total_weight = 0;
1102
        $attendances = $this->getAttendanceList($course, $session);
1103
1104
        foreach ($attendances as $attendance) {
1105
            $attendanceId = $attendance->getIid();
1106
            // Get total faults and total weight
1107
            $total_done_attendance = $attendance->getAttendanceQualifyMax();
1108
            $sql = "SELECT score FROM $tbl_attendance_result
1109
                    WHERE
1110
                        user_id = $user_id AND
1111
                        attendance_id=".$attendanceId;
1112
            $rs = Database::query($sql);
1113
            $score = 0;
1114
            if (Database::num_rows($rs) > 0) {
1115
                $row = Database::fetch_array($rs);
1116
                $score = $row['score'];
1117
            }
1118
            $faults = $total_done_attendance - $score;
1119
            $faults = $faults > 0 ? $faults : 0;
1120
            $total_faults += $faults;
1121
            $total_weight += $total_done_attendance;
1122
        }
1123
1124
        $percent = $total_weight > 0 ? round(($total_faults * 100) / $total_weight, 0) : 0;
1125
        $results['faults'] = $total_faults;
1126
        $results['total'] = $total_weight;
1127
        $results['percent'] = $percent;
1128
1129
        return $results;
1130
    }
1131
1132
    /**
1133
     * Get registered users' attendance sheet inside current course.
1134
     *
1135
     * @param int $attendanceId
1136
     * @param int $user_id      for showing data for only one user (optional)
1137
     * @param int $groupId
1138
     *
1139
     * @return array users attendance sheet data
1140
     */
1141
    public function get_users_attendance_sheet(
1142
        $attendanceId,
1143
        $user_id = 0,
1144
        $groupId = 0,
1145
        $course_id = 0,
1146
        DateTime $startDate = null,
1147
        DateTime $endDate = null
1148
    ) {
1149
        //Get actual course or by course_id
1150
        $course_id = (0 == $course_id) ? api_get_course_int_id() : (int) $course_id;
1151
        $tbl_attendance_sheet = Database::get_course_table(TABLE_ATTENDANCE_SHEET);
1152
        $tbl_attendance_calendar = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR);
1153
        $attendance_calendar = $this->get_attendance_calendar(
1154
            $attendanceId,
1155
            'all',
1156
            null,
1157
            $groupId,
1158
            true,
1159
            $course_id,
1160
            $startDate,
1161
            $endDate
1162
        );
1163
        $calendar_ids = [];
1164
        // get all dates from calendar by current attendance
1165
        foreach ($attendance_calendar as $cal) {
1166
            $calendar_ids[] = $cal['iid'];
1167
        }
1168
1169
        $whereDate = '';
1170
        if (!empty($startDate)) {
1171
            $whereDate .= " AND cal.date_time >= '".$startDate->format('Y-m-d H:i:s')."'";
1172
        }
1173
        if (!empty($endDate)) {
1174
            $whereDate .= " AND cal.date_time <= '".$endDate->format('Y-m-d H:i:s')."'";
1175
        }
1176
1177
        // moved at start of this function
1178
        // $course_id = api_get_course_int_id();
1179
1180
        $data = [];
1181
        if (empty($user_id)) {
1182
            // get all registered users inside current course
1183
            $users = $this->get_users_rel_course();
1184
            $user_ids = array_keys($users);
1185
            if (count($calendar_ids) > 0 && count($user_ids) > 0) {
1186
                foreach ($user_ids as $uid) {
1187
                    $uid = (int) $uid;
1188
                    $sql = "SELECT * FROM $tbl_attendance_sheet
1189
                            WHERE
1190
                                user_id = '$uid' AND
1191
                                attendance_calendar_id IN(".implode(',', $calendar_ids).')
1192
                            ';
1193
                    $res = Database::query($sql);
1194
                    if (Database::num_rows($res) > 0) {
1195
                        while ($row = Database::fetch_array($res)) {
1196
                            $data[$uid][$row['attendance_calendar_id']]['presence'] = $row['presence'];
1197
                        }
1198
                    }
1199
                }
1200
            }
1201
        } else {
1202
            // Get attendance for current user
1203
            $user_id = (int) $user_id;
1204
            if (count($calendar_ids) > 0) {
1205
                $sql = "SELECT cal.date_time, att.presence
1206
                        FROM $tbl_attendance_sheet att
1207
                        INNER JOIN  $tbl_attendance_calendar cal
1208
                        ON cal.iid = att.attendance_calendar_id
1209
                        WHERE
1210
                            att.user_id = $user_id AND
1211
                            att.attendance_calendar_id IN (".implode(',', $calendar_ids).")
1212
                            $whereDate
1213
                        ORDER BY date_time";
1214
                $res = Database::query($sql);
1215
                if (Database::num_rows($res) > 0) {
1216
                    while ($row = Database::fetch_array($res)) {
1217
                        $row['date_time'] = api_convert_and_format_date($row['date_time'], null, date_default_timezone_get());
1218
                        $data[$user_id][] = $row;
1219
                    }
1220
                }
1221
            }
1222
        }
1223
1224
        return $data;
1225
    }
1226
1227
    /**
1228
     * Get next attendance calendar without presences (done attendances).
1229
     *
1230
     * @param int    attendance id
1231
     *
1232
     * @return int attendance calendar id
1233
     */
1234
    public function get_next_attendance_calendar_id($attendanceId)
1235
    {
1236
        $table = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR);
1237
        $attendanceId = (int) $attendanceId;
1238
        $course_id = api_get_course_int_id();
1239
1240
        $sql = "SELECT iid FROM $table
1241
                WHERE
1242
                    attendance_id = '$attendanceId' AND
1243
                    done_attendance = 0
1244
                ORDER BY date_time
1245
                LIMIT 1";
1246
        $rs = Database::query($sql);
1247
        $next_calendar_id = 0;
1248
        if (Database::num_rows($rs) > 0) {
1249
            $row = Database::fetch_array($rs);
1250
            $next_calendar_id = $row['iid'];
1251
        }
1252
1253
        return $next_calendar_id;
1254
    }
1255
1256
    /**
1257
     * Get next attendance calendar datetime without presences (done attendances).
1258
     *
1259
     * @param int    attendance id
1260
     *
1261
     * @return int UNIX time format datetime
1262
     */
1263
    public function getNextAttendanceCalendarDatetime($attendanceId)
1264
    {
1265
        $table = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR);
1266
        $course_id = api_get_course_int_id();
1267
        $attendanceId = (int) $attendanceId;
1268
        $sql = "SELECT iid, date_time FROM $table
1269
                WHERE
1270
                    attendance_id = '$attendanceId' AND
1271
                    done_attendance = 0
1272
                ORDER BY date_time
1273
                LIMIT 1";
1274
        $rs = Database::query($sql);
1275
        $next_calendar_datetime = 0;
1276
        if (Database::num_rows($rs) > 0) {
1277
            $row = Database::fetch_array($rs);
1278
            $next_calendar_datetime = api_get_local_time($row['date_time']);
1279
        }
1280
1281
        return $next_calendar_datetime;
1282
    }
1283
1284
    /**
1285
     * Get user's score from current attendance.
1286
     *
1287
     * @param int $user_id
1288
     * @param int $attendanceId
1289
     * @param int $groupId
1290
     *
1291
     * @return int score
1292
     */
1293
    public function get_user_score($user_id, $attendanceId, $groupId = 0)
1294
    {
1295
        $tbl_attendance_result = Database::get_course_table(TABLE_ATTENDANCE_RESULT);
1296
        $tbl_attendance_sheet = Database::get_course_table(TABLE_ATTENDANCE_SHEET);
1297
        $tbl_attendance_cal_rel_group = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR_REL_GROUP);
1298
        $tbl_attendance_cal = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR);
1299
        $user_id = (int) $user_id;
1300
        $attendanceId = (int) $attendanceId;
1301
        $groupId = (int) $groupId;
1302
        $course_id = api_get_course_int_id();
1303
1304
        if (empty($groupId)) {
1305
            $sql = "SELECT score FROM $tbl_attendance_result
1306
                    WHERE
1307
                        user_id='$user_id' AND
1308
                        attendance_id='$attendanceId'";
1309
        } else {
1310
            $sql = "SELECT count(presence) as score
1311
                    FROM $tbl_attendance_sheet
1312
                    WHERE
1313
                        user_id = '$user_id' AND
1314
                        presence = 1 AND
1315
                        attendance_calendar_id IN (
1316
                            SELECT calendar_id FROM $tbl_attendance_cal_rel_group crg
1317
                            INNER JOIN $tbl_attendance_cal c
1318
                            ON (crg.calendar_id = c.iid)
1319
                            WHERE
1320
                                crg.group_id = $groupId AND
1321
                                c.attendance_id = $attendanceId
1322
                        )
1323
                    ";
1324
        }
1325
        $rs = Database::query($sql);
1326
        $score = 0;
1327
        if (Database::num_rows($rs) > 0) {
1328
            $row = Database::fetch_array($rs);
1329
            $score = $row['score'];
1330
        }
1331
1332
        return $score;
1333
    }
1334
1335
    /**
1336
     * Get attendance calendar data by id.
1337
     *
1338
     * @param int    attendance calendar id
1339
     *
1340
     * @return array attendance calendar data
1341
     */
1342
    public function get_attendance_calendar_by_id($calendar_id)
1343
    {
1344
        $table = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR);
1345
        $calendar_id = (int) $calendar_id;
1346
        $course_id = api_get_course_int_id();
1347
        $sql = "SELECT * FROM $table
1348
                WHERE iid = '$calendar_id' ";
1349
        $rs = Database::query($sql);
1350
        $data = [];
1351
        if (Database::num_rows($rs) > 0) {
1352
            while ($row = Database::fetch_array($rs)) {
1353
                $row['date_time'] = api_get_local_time($row['date_time']);
1354
                $data = $row;
1355
            }
1356
        }
1357
1358
        return $data;
1359
    }
1360
1361
    /**
1362
     * Get all attendance calendar data inside current attendance.
1363
     *
1364
     * @param int      $attendanceId
1365
     * @param string   $type
1366
     * @param int      $calendar_id
1367
     * @param int      $groupId
1368
     * @param bool     $showAll      = false show group calendar items or not
1369
     * @param int      $course_id
1370
     * @param DateTime $startDate    Filter calendar with a start date
1371
     * @param DateTime $endDate      Filter calendar with a end date
1372
     *
1373
     * @return array attendance calendar data
1374
     */
1375
    public function get_attendance_calendar(
1376
        $attendanceId,
1377
        $type = 'all',
1378
        $calendar_id = null,
1379
        $groupId = 0,
1380
        $showAll = false,
1381
        $course_id = 0,
1382
        DateTime $startDate = null,
1383
        DateTime $endDate = null
1384
    ) {
1385
        $tbl_attendance_calendar = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR);
1386
        $tbl_acrg = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR_REL_GROUP);
1387
        $attendanceId = (int) $attendanceId;
1388
        $course_id = 0 == $course_id ? api_get_course_int_id() : (int) $course_id;
1389
        $whereDate = '';
1390
        if (!empty($startDate)) {
1391
            $whereDate .= " AND c.date_time >= '".$startDate->format('Y-m-d H:i:s')."'";
1392
        }
1393
        if (!empty($endDate)) {
1394
            $whereDate .= " AND c.date_time <= '".$endDate->format('Y-m-d H:i:s')."'";
1395
        }
1396
        if ($showAll) {
1397
            $sql = "SELECT * FROM $tbl_attendance_calendar c
1398
                    WHERE
1399
                        attendance_id = '$attendanceId'
1400
                        $whereDate";
1401
        } else {
1402
            $sql = "SELECT * FROM $tbl_attendance_calendar c
1403
                    WHERE
1404
                        attendance_id = '$attendanceId' AND
1405
                        iid NOT IN (
1406
                            SELECT calendar_id FROM $tbl_acrg
1407
                            WHERE attendance_id = '$attendanceId'  AND group_id != 0 AND group_id IS NOT NULL
1408
                        )
1409
                        $whereDate
1410
                    ";
1411
        }
1412
1413
        if (!empty($groupId)) {
1414
            $groupId = (int) $groupId;
1415
            $sql = "SELECT c.* FROM $tbl_attendance_calendar c
1416
                    INNER JOIN $tbl_acrg g
1417
                    ON c.iid = g.calendar_id
1418
                    WHERE
1419
                        g.group_id = '$groupId' AND
1420
                        c.attendance_id = '$attendanceId'
1421
                   ";
1422
        }
1423
1424
        if (!in_array($type, ['today', 'all', 'all_done', 'all_not_done', 'calendar_id'])) {
1425
            $type = 'all';
1426
        }
1427
1428
        switch ($type) {
1429
            case 'calendar_id':
1430
                $calendar_id = (int) $calendar_id;
1431
                if (!empty($calendar_id)) {
1432
                    $sql .= " AND c.iid = $calendar_id";
1433
                }
1434
                break;
1435
            case 'today':
1436
                //$sql .= ' AND DATE_FORMAT(date_time,"%d-%m-%Y") = DATE_FORMAT("'.api_get_utc_datetime().'", "%d-%m-%Y" )';
1437
                break;
1438
            case 'all_done':
1439
                $sql .= ' AND done_attendance = 1 ';
1440
                break;
1441
            case 'all_not_done':
1442
                $sql .= ' AND done_attendance = 0 ';
1443
                break;
1444
            case 'all':
1445
            default:
1446
                break;
1447
        }
1448
        $sql .= ' ORDER BY date_time ';
1449
1450
        $rs = Database::query($sql);
1451
        $data = [];
1452
        if (Database::num_rows($rs) > 0) {
1453
            while ($row = Database::fetch_assoc($rs)) {
1454
                $row['db_date_time'] = $row['date_time'];
1455
                $row['date_time'] = api_get_local_time($row['date_time']);
1456
                $row['date'] = api_format_date($row['date_time'], DATE_FORMAT_SHORT);
1457
                $row['time'] = api_format_date($row['date_time'], TIME_NO_SEC_FORMAT);
1458
                $row['groups'] = $this->getGroupListByAttendanceCalendar($row['iid'], $course_id);
1459
                if ('today' === $type) {
1460
                    if (date('d-m-Y', api_strtotime($row['date_time'], 'UTC')) == date('d-m-Y', time())) {
1461
                        $data[] = $row;
1462
                    }
1463
                } else {
1464
                    $data[] = $row;
1465
                }
1466
            }
1467
        }
1468
1469
        return $data;
1470
    }
1471
1472
    /**
1473
     * Get number of attendance calendar inside current attendance.
1474
     *
1475
     * @param int $attendanceId
1476
     * @param int $groupId
1477
     * @param $done_attendance
1478
     * @param int $userId
1479
     *
1480
     * @return int number of dates in attendance calendar
1481
     */
1482
    public static function get_number_of_attendance_calendar(
1483
        $attendanceId,
1484
        $groupId = 0,
1485
        $done_attendance = null,
1486
        $userId = 0
1487
    ) {
1488
        $tbl_attendance_calendar = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR);
1489
        $calendarRelGroup = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR_REL_GROUP);
1490
        $tbl_groupRelUser = Database::get_course_table(TABLE_GROUP_USER);
1491
        $attendanceId = (int) $attendanceId;
1492
        $groupId = (int) $groupId;
1493
        $course_id = api_get_course_int_id();
1494
1495
        $where_attendance = '';
1496
        if ($done_attendance) {
1497
            $where_attendance = ' done_attendance = 1 AND ';
1498
        }
1499
        if (empty($userId)) {
1500
            if (empty($groupId)) {
1501
                $sql = "SELECT count(a.iid)
1502
                        FROM $tbl_attendance_calendar a
1503
                        WHERE
1504
                            $where_attendance
1505
                            attendance_id = '$attendanceId' AND
1506
                            iid NOT IN (
1507
                                SELECT calendar_id FROM $calendarRelGroup
1508
                                WHERE
1509
                                    attendance_id = $attendanceId AND
1510
                                    group_id != 0 AND
1511
                                    group_id IS NOT NULL
1512
                            )
1513
                        ";
1514
            } else {
1515
                $sql = "SELECT count(a.iid)
1516
                        FROM $tbl_attendance_calendar a
1517
                        INNER JOIN $calendarRelGroup g
1518
                        ON (a.iid = g.calendar_id)
1519
                        WHERE
1520
                            $where_attendance
1521
                            attendance_id = '$attendanceId' AND
1522
                            group_id = $groupId
1523
                        ";
1524
            }
1525
        } else {
1526
            if (empty($groupId)) {
1527
                $sql = "SELECT count(a.iid)
1528
                        FROM $tbl_attendance_calendar a
1529
                        WHERE
1530
                            $where_attendance
1531
                            attendance_id = '$attendanceId' AND
1532
                            iid NOT IN (
1533
                                SELECT calendar_id FROM $calendarRelGroup
1534
                                WHERE
1535
                                    group_id != 0 AND
1536
                                    group_id IS NOT NULL AND
1537
                                    group_id NOT IN (
1538
                                        SELECT group_id
1539
                                        FROM $tbl_groupRelUser
1540
                                        WHERE user_id = $userId
1541
                                    )
1542
                            )
1543
                        ";
1544
            } else {
1545
                $sql = "SELECT count(a.iid)
1546
                        FROM $tbl_attendance_calendar a
1547
                        INNER JOIN $calendarRelGroup g
1548
                        ON (a.iid = g.calendar_id)
1549
                        WHERE
1550
                            $where_attendance
1551
                            attendance_id = '$attendanceId' AND
1552
                            group_id = $groupId
1553
                        ";
1554
            }
1555
        }
1556
1557
        $rs = Database::query($sql);
1558
        $row = Database::fetch_row($rs);
1559
1560
        return $row[0];
1561
    }
1562
1563
    /**
1564
     * Get count dates inside attendance calendar by attendance id.
1565
     *
1566
     * @param int $attendanceId
1567
     *
1568
     * @return int count of dates
1569
     */
1570
    public static function get_count_dates_inside_attendance_calendar($attendanceId)
1571
    {
1572
        $tbl_attendance_calendar = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR);
1573
        $attendanceId = (int) $attendanceId;
1574
        $course_id = api_get_course_int_id();
1575
        $sql = "SELECT count(iid) FROM $tbl_attendance_calendar
1576
                WHERE
1577
                    attendance_id = '$attendanceId'";
1578
        $rs = Database::query($sql);
1579
        $count = 0;
1580
        if (Database::num_rows($rs) > 0) {
1581
            $row = Database::fetch_row($rs);
1582
            $count = $row[0];
1583
        }
1584
1585
        return $count;
1586
    }
1587
1588
    /**
1589
     * check if all calendar of an attendance is done.
1590
     *
1591
     * @return bool True if all calendar is done, otherwise false
1592
     */
1593
    public static function is_all_attendance_calendar_done(CAttendance $attendance)
1594
    {
1595
        $done_calendar = self::get_done_attendance_calendar($attendance);
1596
        //$count_dates_in_calendar = self::get_count_dates_inside_attendance_calendar($attendanceId);
1597
        $count_dates_in_calendar = 0;
1598
        if ($attendance->getCalendars()) {
1599
            $count_dates_in_calendar = $attendance->getCalendars()->count();
1600
        }
1601
1602
        //$number_of_dates = self::get_number_of_attendance_calendar($attendanceId);
1603
1604
        $result = false;
1605
        if ($count_dates_in_calendar == $done_calendar) {
1606
            $result = true;
1607
        }
1608
1609
        return $result;
1610
    }
1611
1612
    /**
1613
     * check if an attendance is locked.
1614
     *
1615
     * @param int $attendanceId
1616
     * @param bool
1617
     *
1618
     * @return bool
1619
     */
1620
    public static function is_locked_attendance($attendanceId)
1621
    {
1622
        //  Use gradebook lock
1623
        return api_resource_is_locked_by_gradebook($attendanceId, LINK_ATTENDANCE);
1624
    }
1625
1626
    /**
1627
     * Add new datetime inside attendance calendar table.
1628
     *
1629
     * @param CAttendance $attendance
1630
     * @param array       $groupList
1631
     *
1632
     * @return int affected rows
1633
     */
1634
    public function attendance_calendar_add($attendance, $groupList = [])
1635
    {
1636
        $course_id = api_get_course_int_id();
1637
        $calendar = new CAttendanceCalendar();
1638
        $calendar
1639
            ->setAttendance($attendance)
1640
            ->setDateTime(new Datetime($this->date_time))
1641
            ->setDoneAttendance(false)
1642
            ->setBlocked(false);
1643
1644
        $em = Database::getManager();
1645
        $em->persist($calendar);
1646
        $em->flush();
1647
1648
        $this->addAttendanceCalendarToGroup($calendar->getIid(), $course_id, $groupList);
1649
1650
        // update locked attendance
1651
        $is_all_calendar_done = $this->is_all_attendance_calendar_done($attendance);
1652
        if (!$is_all_calendar_done) {
1653
            $this->lock($attendance, false);
1654
        } else {
1655
            $this->lock($attendance);
1656
        }
1657
1658
        return true;
1659
    }
1660
1661
    /**
1662
     * @param int   $calendarId
1663
     * @param int   $courseId
1664
     * @param array $groupList
1665
     *
1666
     * @return bool
1667
     */
1668
    public function addAttendanceCalendarToGroup($calendarId, $courseId, $groupList)
1669
    {
1670
        if (empty($groupList)) {
1671
            return false;
1672
        }
1673
1674
        $table = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR_REL_GROUP);
1675
        foreach ($groupList as $groupId) {
1676
            if (empty($groupId)) {
1677
                continue;
1678
            }
1679
1680
            $result = $this->getAttendanceCalendarGroup(
1681
                $calendarId,
1682
                $courseId,
1683
                $groupId
1684
            );
1685
1686
            if (empty($result)) {
1687
                $params = [
1688
                    'calendar_id' => $calendarId,
1689
                    'group_id' => $groupId,
1690
                ];
1691
                Database::insert($table, $params);
1692
            }
1693
        }
1694
1695
        return true;
1696
    }
1697
1698
    /**
1699
     * @param int $calendarId
1700
     *
1701
     * @return array
1702
     */
1703
    public function getGroupListByAttendanceCalendar($calendarId)
1704
    {
1705
        $table = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR_REL_GROUP);
1706
1707
        return Database::select(
1708
            '*',
1709
            $table,
1710
            [
1711
                'where' => [
1712
                    'calendar_id = ? ' => [$calendarId],
1713
                ],
1714
            ]
1715
        );
1716
    }
1717
1718
    /**
1719
     * @param int $calendarId
1720
     * @param int $courseId
1721
     * @param int $groupId
1722
     *
1723
     * @return array
1724
     */
1725
    public function getAttendanceCalendarGroup($calendarId, $groupId)
1726
    {
1727
        $table = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR_REL_GROUP);
1728
1729
        return Database::select(
1730
            '*',
1731
            $table,
1732
            [
1733
                'where' => [
1734
                    'calendar_id = ? AND group_id = ?' => [$calendarId, $groupId],
1735
                ],
1736
            ]
1737
        );
1738
    }
1739
1740
    /**
1741
     * @param int $calendarId
1742
     * @param int $courseId
1743
     */
1744
    public function deleteAttendanceCalendarGroup($calendarId, $courseId)
1745
    {
1746
        $table = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR_REL_GROUP);
1747
        Database::delete(
1748
            $table,
1749
            [
1750
                'calendar_id = ?' => [$calendarId, $courseId],
1751
            ]
1752
        );
1753
    }
1754
1755
    /**
1756
     * save repeated date inside attendance calendar table.
1757
     *
1758
     * @param CAttendance $attendance
1759
     * @param int         $start_date  start date in tms
1760
     * @param int         $end_date    end date in tms
1761
     * @param string      $repeat_type daily, weekly, monthlyByDate
1762
     * @param array       $groupList
1763
     */
1764
    public function attendance_repeat_calendar_add(
1765
        $attendance,
1766
        $start_date,
1767
        $end_date,
1768
        $repeat_type,
1769
        $groupList = []
1770
    ) {
1771
        $attendanceId = $attendance->getIid();
1772
        // save start date
1773
        $this->set_date_time(api_get_utc_datetime($start_date));
1774
        $this->attendance_calendar_add($attendance, $groupList);
1775
1776
        // 86400 = 24 hours in seconds
1777
        // 604800 = 1 week in seconds
1778
        // Saves repeated dates
1779
        switch ($repeat_type) {
1780
            case 'daily':
1781
                $j = 1;
1782
                for ($i = $start_date + 86400; ($i <= $end_date); $i += 86400) {
1783
                    $this->set_date_time(api_get_utc_datetime($i));
1784
                    $this->attendance_calendar_add($attendance, $groupList);
1785
                    $j++;
1786
                }
1787
                break;
1788
            case 'weekly':
1789
                $j = 1;
1790
                for ($i = $start_date + 604800; ($i <= $end_date); $i += 604800) {
1791
                    $this->set_date_time(api_get_utc_datetime($i));
1792
                    $this->attendance_calendar_add($attendance, $groupList);
1793
                    $j++;
1794
                }
1795
                break;
1796
            case 'monthlyByDate':
1797
                $j = 1;
1798
                //@todo fix bug with february
1799
                for ($i = $start_date + 2419200; ($i <= $end_date); $i += 2419200) {
1800
                    $this->set_date_time(api_get_utc_datetime($i));
1801
                    $this->attendance_calendar_add($attendanceId, $groupList);
1802
                    $j++;
1803
                }
1804
                break;
1805
        }
1806
    }
1807
1808
    /**
1809
     * edit a datetime inside attendance calendar table.
1810
     *
1811
     * @param int         $calendar_id
1812
     * @param CAttendance $attendance
1813
     *
1814
     * @return int affected rows
1815
     */
1816
    public function attendance_calendar_edit($calendar_id, $attendance)
1817
    {
1818
        $tbl_attendance_calendar = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR);
1819
        $affected_rows = 0;
1820
        $attendanceId = $attendance->getIid();
1821
1822
        $course_id = api_get_course_int_id();
1823
        // check if datetime already exists inside the table
1824
        $sql = "SELECT iid FROM $tbl_attendance_calendar
1825
                WHERE
1826
                    date_time = '".Database::escape_string($this->date_time)."' AND
1827
                    attendance_id = '$attendanceId'";
1828
        $rs = Database::query($sql);
1829
1830
        if (0 == Database::num_rows($rs)) {
1831
            $sql = "UPDATE $tbl_attendance_calendar
1832
                    SET date_time='".Database::escape_string($this->date_time)."'
1833
                    WHERE iid = '".$calendar_id."'";
1834
            Database::query($sql);
1835
        }
1836
1837
        // update locked attendance
1838
        $is_all_calendar_done = self::is_all_attendance_calendar_done($attendance);
1839
        if (!$is_all_calendar_done) {
1840
            $this->lock($attendance, false);
1841
        } else {
1842
            $this->lock($attendance);
1843
        }
1844
1845
        return $affected_rows;
1846
    }
1847
1848
    /**
1849
     * delete a datetime from attendance calendar table.
1850
     *
1851
     * @param int        attendance calendar id
1852
     * @param CAttendance        attendance
1853
     * @param bool true for removing all calendar inside current attendance, false for removing by calendar id
1854
     *
1855
     * @return int affected rows
1856
     */
1857
    public function attendance_calendar_delete($calendar_id, CAttendance $attendance, $all_delete = false)
1858
    {
1859
        $tbl_attendance_calendar = Database::get_course_table(TABLE_ATTENDANCE_CALENDAR);
1860
        $tbl_attendance_sheet = Database::get_course_table(TABLE_ATTENDANCE_SHEET);
1861
1862
        $attendanceId = $attendance->getIid();
1863
        $calendar_id = (int) $calendar_id;
1864
1865
        // get all registered users inside current course
1866
        $users = $this->get_users_rel_course();
1867
        $user_ids = array_keys($users);
1868
        $course_id = api_get_course_int_id();
1869
        $affected_rows = 0;
1870
        if ($all_delete) {
1871
            $attendance_calendar = $this->get_attendance_calendar($attendanceId);
1872
            // get all dates from calendar by current attendance
1873
            if (!empty($attendance_calendar)) {
1874
                foreach ($attendance_calendar as $cal) {
1875
                    // delete all data from attendance sheet
1876
                    $sql = "DELETE FROM $tbl_attendance_sheet
1877
                            WHERE attendance_calendar_id = '".intval($cal['iid'])."'";
1878
                    Database::query($sql);
1879
                    // delete data from attendance calendar
1880
                    $sql = "DELETE FROM $tbl_attendance_calendar
1881
                            WHERE iid = '".intval($cal['iid'])."'";
1882
                    Database::query($sql);
1883
1884
                    $this->deleteAttendanceCalendarGroup($cal['iid'], $course_id);
1885
                    $affected_rows++;
1886
                }
1887
            }
1888
        } else {
1889
            // delete just one row from attendance sheet by the calendar id
1890
            $sql = "DELETE FROM $tbl_attendance_sheet
1891
                    WHERE attendance_calendar_id = '".$calendar_id."'";
1892
            Database::query($sql);
1893
            // delete data from attendance calendar
1894
            $sql = "DELETE FROM $tbl_attendance_calendar
1895
                    WHERE iid = '".$calendar_id."'";
1896
            Database::query($sql);
1897
1898
            $this->deleteAttendanceCalendarGroup($calendar_id, $course_id);
1899
            $affected_rows++;
1900
        }
1901
1902
        // update users' results
1903
        $this->updateUsersResults($user_ids, $attendance);
1904
1905
        return $affected_rows;
1906
    }
1907
1908
    public function set_session_id($sessionId)
1909
    {
1910
        $this->session_id = $sessionId;
1911
    }
1912
1913
    public function set_course_id($course_id)
1914
    {
1915
        $this->course_id = $course_id;
1916
    }
1917
1918
    public function set_date_time($datetime)
1919
    {
1920
        $this->date_time = $datetime;
1921
    }
1922
1923
    public function set_title($title)
1924
    {
1925
        $this->title = $title;
1926
    }
1927
1928
    public function set_description($description)
1929
    {
1930
        $this->description = $description;
1931
    }
1932
1933
    public function set_attendance_qualify_title($attendance_qualify_title)
1934
    {
1935
        $this->attendance_qualify_title = $attendance_qualify_title;
1936
    }
1937
1938
    public function set_attendance_weight($attendance_weight)
1939
    {
1940
        $this->attendance_weight = $attendance_weight;
1941
    }
1942
1943
    /** Getters for fields of attendances tables */
1944
    public function get_session_id()
1945
    {
1946
        return $this->session_id;
1947
    }
1948
1949
    public function get_course_id()
1950
    {
1951
        return $this->course_id;
1952
    }
1953
1954
    public function get_date_time()
1955
    {
1956
        return $this->date_time;
1957
    }
1958
1959
    public function get_title()
1960
    {
1961
        return $this->title;
1962
    }
1963
1964
    public function get_description()
1965
    {
1966
        return $this->description;
1967
    }
1968
1969
    public function get_attendance_qualify_title()
1970
    {
1971
        return $this->attendance_qualify_title;
1972
    }
1973
1974
    public function get_attendance_weight()
1975
    {
1976
        return $this->attendance_weight;
1977
    }
1978
1979
    /**
1980
     * @param string $startDate in UTC time
1981
     * @param string $endDate   in UTC time
1982
     *
1983
     * @return array
1984
     */
1985
    public function getAttendanceLogin($startDate, $endDate)
1986
    {
1987
        if (empty($startDate) ||
1988
            '0000-00-00' === $startDate ||
1989
            '0000-00-00 00:00:00' === $startDate ||
1990
            empty($endDate) ||
1991
            '0000-00-00' === $endDate ||
1992
            '0000-00-00 00:00:00' === $endDate
1993
        ) {
1994
            return false;
1995
        }
1996
1997
        $sessionId = api_get_session_id();
1998
        $courseCode = api_get_course_id();
1999
        $courseId = api_get_course_int_id();
2000
2001
        if (!empty($sessionId)) {
2002
            $users = CourseManager::get_user_list_from_course_code(
2003
                $courseCode,
2004
                $sessionId,
2005
                '',
2006
                'lastname',
2007
                0
2008
            );
2009
        } else {
2010
            $users = CourseManager::get_user_list_from_course_code(
2011
                $courseCode,
2012
                0,
2013
                '',
2014
                'lastname',
2015
                STUDENT
2016
            );
2017
        }
2018
2019
        $dateTimeStartOriginal = new DateTime($startDate);
2020
        $dateTimeStart = new DateTime($startDate);
2021
        $dateTimeEnd = new DateTime($endDate);
2022
        $interval = $dateTimeStart->diff($dateTimeEnd);
2023
        $days = (int) $interval->format('%a');
2024
2025
        $dateList = [$dateTimeStart->format('Y-m-d')];
2026
        $headers = [
2027
            get_lang('User'),
2028
            $dateTimeStart->format('Y-m-d'),
2029
        ];
2030
2031
        for ($i = 0; $i < $days; $i++) {
2032
            $dateTimeStart = $dateTimeStart->add(new DateInterval('P1D'));
2033
            $date = $dateTimeStart->format('Y-m-d');
2034
            $dateList[] = $date;
2035
            $headers[] = $date;
2036
        }
2037
2038
        $accessData = CourseManager::getCourseAccessPerCourseAndSession(
2039
            $courseId,
2040
            $sessionId,
2041
            $dateTimeStartOriginal->format('Y-m-d H:i:s'),
2042
            $dateTimeEnd->format('Y-m-d H:i:s')
2043
        );
2044
2045
        $results = [];
2046
        if (!empty($accessData)) {
2047
            foreach ($accessData as $data) {
2048
                $onlyDate = substr($data['login_course_date'], 0, 10);
2049
                $results[$data['user_id']][$onlyDate] = true;
2050
            }
2051
        }
2052
2053
        return [
2054
            'users' => $users,
2055
            'dateList' => $dateList,
2056
            'headers' => $headers,
2057
            'results' => $results,
2058
        ];
2059
    }
2060
2061
    /**
2062
     * @param string $startDate in UTC time
2063
     * @param string $endDate   in UTC time
2064
     *
2065
     * @return string
2066
     */
2067
    public function getAttendanceLoginTable($startDate, $endDate)
2068
    {
2069
        $data = $this->getAttendanceLogin($startDate, $endDate);
2070
        if (!$data) {
2071
            return null;
2072
        }
2073
2074
        $headers = $data['headers'];
2075
        $dateList = $data['dateList'];
2076
        $users = $data['users'];
2077
        $results = $data['results'];
2078
2079
        $table = new HTML_Table(['class' => 'table table-hover table-striped data_table']);
2080
        $row = 0;
2081
        $column = 0;
2082
        foreach ($headers as $header) {
2083
            $table->setHeaderContents($row, $column, $header);
2084
            $column++;
2085
        }
2086
        $row = 1;
2087
        foreach ($users as $user) {
2088
            $table->setCellContents(
2089
                $row,
2090
                0,
2091
                $user['lastname'].' '.$user['firstname'].' ('.$user['username'].')'
2092
            );
2093
            $row++;
2094
        }
2095
2096
        $column = 1;
2097
        $row = 1;
2098
        foreach ($users as $user) {
2099
            foreach ($dateList as $date) {
2100
                $status = null;
2101
                if (isset($results[$user['user_id']]) &&
2102
                    isset($results[$user['user_id']][$date])
2103
                ) {
2104
                    $status = 'X';
2105
                }
2106
                $table->setCellContents($row, $column, $status);
2107
                $column++;
2108
            }
2109
            $row++;
2110
            $column = 1;
2111
        }
2112
2113
        return $table->toHtml();
2114
    }
2115
2116
    /**
2117
     * @param string $startDate in UTC time
2118
     * @param string $endDate   in UTC time
2119
     *
2120
     * @return string
2121
     */
2122
    public function exportAttendanceLogin($startDate, $endDate)
2123
    {
2124
        $data = $this->getAttendanceLogin($startDate, $endDate);
2125
2126
        if (!$data) {
2127
            return null;
2128
        }
2129
        $users = $data['users'];
2130
        $results = $data['results'];
2131
2132
        $table = new HTML_Table(['class' => 'table table-hover table-striped data_table']);
2133
        $table->setHeaderContents(0, 0, get_lang('User'));
2134
        $table->setHeaderContents(0, 1, get_lang('Date'));
2135
2136
        $row = 1;
2137
        foreach ($users as $user) {
2138
            $table->setCellContents(
2139
                $row,
2140
                0,
2141
                $user['lastname'].' '.$user['firstname'].' ('.$user['username'].')'
2142
            );
2143
            $row++;
2144
        }
2145
        $table->setColAttributes(0, ['style' => 'width:28%']);
2146
2147
        $row = 1;
2148
        foreach ($users as $user) {
2149
            if (isset($results[$user['user_id']]) &&
2150
                !empty($results[$user['user_id']])
2151
            ) {
2152
                $dates = implode(', ', array_keys($results[$user['user_id']]));
2153
                $table->setCellContents($row, 1, $dates);
2154
            }
2155
            $row++;
2156
        }
2157
2158
        $tableToString = $table->toHtml();
2159
        $params = [
2160
            'filename' => get_lang('Attendance').'_'.api_get_utc_datetime(),
2161
            'pdf_title' => get_lang('Attendance'),
2162
            'course_code' => api_get_course_id(),
2163
            'show_real_course_teachers' => true,
2164
        ];
2165
        $pdf = new PDF('A4', null, $params);
2166
        $pdf->html_to_pdf_with_template($tableToString);
2167
    }
2168
2169
    /**
2170
     * Return all course for a student between dates and order by key date (Y-m-d).
2171
     *
2172
     * @param \DateTime $startDate
2173
     * @param \DateTime $endDate
2174
     * @param bool      $orderDesc
2175
     *
2176
     * @return array
2177
     */
2178
    public function getCoursesWithAttendance(
2179
        int $studentId,
2180
        DateTime $startDate = null,
2181
        DateTime $endDate = null,
2182
        $orderDesc = false
2183
    ) {
2184
        $presentString = get_lang('Present');
2185
        $absentString = '-';
2186
        $attendanceLib = new Attendance();
2187
        $data = [];
2188
        $courses = CourseManager::get_courses_list_by_user_id($studentId);
2189
        /* Get course with (in_category) and without (not_category) category */
2190
        $i = 0;
2191
        foreach ($courses as $courseItem) {
2192
            $i++;
2193
            $courseId = $courseItem['real_id'];
2194
2195
            /* Get all attendance by courses*/
2196
            $attendanceList = $attendanceLib->getAttendanceList(api_get_course_entity($courseId));
2197
            $temp = [];
2198
            $sheetsProcessed = [];
2199
            $tempDate = [];
2200
            foreach ($attendanceList as $attendanceData) {
2201
                $attendanceId = $attendanceData->getIid();
2202
                $sheets = $attendanceLib->get_users_attendance_sheet(
2203
                    $attendanceId,
2204
                    $studentId,
2205
                    0,
2206
                    $courseId,
2207
                    $startDate,
2208
                    $endDate
2209
                );
2210
2211
                $sheetsProcessed[] = [];
2212
                foreach ($sheets as $sheetData) {
2213
                    $totalb = count($sheetData);
2214
                    $tempDate = [];
2215
                    for ($ii = 0; $ii < $totalb; $ii++) {
2216
                        $attendancesProcess = $sheetData[$ii];
2217
                        if (!empty($attendancesProcess)) {
2218
                            $dateTemp = $attendancesProcess['0'];
2219
                            $attendancesProcess[0] = $attendancesProcess[1];
2220
                            $attendancesProcess[1] = $dateTemp;
2221
2222
                            $attendancesProcess[2] = $courseItem['title'];
2223
                            $attendancesProcess['courseTitle'] = $courseItem['title'];
2224
2225
                            $attendancesProcess[3] = $courseItem['real_id'];
2226
                            $attendancesProcess['courseId'] = $courseItem['real_id'];
2227
2228
                            $attendancesProcess[4] = $attendanceData->getTitle();
2229
                            $attendancesProcess['attendanceName'] = $attendanceData->getTitle();
2230
                            $attendancesProcess['courseCode'] = $courseItem['course_code'];
2231
2232
                            $attendancesProcess[5] = $attendanceId;
2233
                            $attendancesProcess['attendanceId'] = $attendanceId;
2234
                            if (1 == $attendancesProcess['presence']) {
2235
                                $attendancesProcess['presence'] = $presentString;
2236
                                $attendancesProcess[0] = 1;
2237
                            } else {
2238
                                $attendancesProcess['presence'] = $absentString;
2239
                                $attendancesProcess[0] = 0;
2240
                            }
2241
                            $attendancesProcess['session'] = 0;
2242
                            $attendancesProcess['sessionName'] = '';
2243
                            $tempDate[] = $attendancesProcess;
2244
                            $dateKey = new DateTime($dateTemp);
2245
                            /*
2246
                            $attendancesProcess['teacher'] = '';
2247
                            if(isset($courseItem['teachers']) and isset($courseItem['teachers'][0])){
2248
                                $attendancesProcess['teacher'] = $courseItem['teachers'][0]['fullname'];
2249
                            }
2250
                            */
2251
                            $data[$dateKey->format('Y-m-d')][] = $attendancesProcess;
2252
                        }
2253
                    }
2254
                }
2255
                $sheetsProcessed[] = $tempDate;
2256
                $temp[] = $sheetsProcessed;
2257
            }
2258
            $courses['not_category'][$i]['attendanceSheet'] = $temp;
2259
        }
2260
2261
        $sql = "SELECT session_id, c_id FROM session_rel_course_rel_user
2262
                WHERE user_id = $studentId";
2263
2264
        $rs = Database::query($sql);
2265
        // get info from sessions
2266
        while ($row = Database::fetch_assoc($rs)) {
2267
            $courseId = $row['c_id'];
2268
            $sessionId = $row['session_id'];
2269
            $courseItem = api_get_course_info_by_id($courseId);
2270
            $attendanceList = $attendanceLib->getAttendanceList(
2271
            api_get_course_entity($courseId),
2272
            api_get_session_entity($sessionId)
2273
            );
2274
            $temp = [];
2275
            $sheetsProcessed = [];
2276
            $tempDate = [];
2277
            foreach ($attendanceList as $attendanceData) {
2278
                $attendanceId = $attendanceData->getIid();
2279
                $sheets = $attendanceLib->get_users_attendance_sheet(
2280
                    $attendanceId,
2281
                    $studentId,
2282
                    0,
2283
                    $courseId,
2284
                    $startDate,
2285
                    $endDate
2286
                );
2287
2288
                $sheetsProcessed[] = [];
2289
                foreach ($sheets as $sheetData) {
2290
                    $totalb = count($sheetData);
2291
                    $tempDate = [];
2292
                    for ($ii = 0; $ii < $totalb; $ii++) {
2293
                        $work = $sheetData[$ii];
2294
                        $attendancesProcess = $work;
2295
                        if (!empty($attendancesProcess)) {
2296
                            $dateTemp = $attendancesProcess['0'];
2297
                            $attendancesProcess[0] = $attendancesProcess[1];
2298
                            $attendancesProcess[1] = $dateTemp;
2299
                            $attendancesProcess[2] = $courseItem['title'];
2300
                            $attendancesProcess['courseTitle'] = $courseItem['title'];
2301
                            $attendancesProcess[3] = $courseItem['real_id'];
2302
                            $attendancesProcess['courseId'] = $courseItem['real_id'];
2303
                            $attendancesProcess[4] = $attendanceData->getTitle();
2304
                            $attendancesProcess['attendanceName'] = $attendanceData->getTitle();
2305
                            $attendancesProcess[5] = $attendanceId;
2306
                            $attendancesProcess['attendanceId'] = $attendanceId;
2307
                            $attendancesProcess['courseCode'] = $courseItem['official_code'];
2308
                            if (1 == $attendancesProcess['presence']) {
2309
                                $attendancesProcess['presence'] = $presentString;
2310
                                $attendancesProcess[0] = 1;
2311
                            } else {
2312
                                $attendancesProcess['presence'] = $absentString;
2313
                                $attendancesProcess[0] = 0;
2314
                            }
2315
                            $attendancesProcess['session'] = $sessionId;
2316
                            $attendancesProcess['sessionName'] = api_get_session_name($sessionId);
2317
2318
                            $tempDate[] = $attendancesProcess;
2319
                            $dateKey = new DateTime($dateTemp);
2320
                            /*
2321
                            $attendancesProcess['teacher'] = '';
2322
                            if(isset($courseItem['tutor_name']) ){
2323
                                $attendancesProcess['teacher'] = $courseItem['tutor_name'];
2324
                            }
2325
                            */
2326
                            $data[$dateKey->format('Y-m-d')][] = $attendancesProcess;
2327
                        }
2328
                    }
2329
                }
2330
                $sheetsProcessed[] = $tempDate;
2331
                $temp[] = $sheetsProcessed;
2332
            }
2333
            $courses['session'][$i]['attendanceSheet'] = $temp;
2334
        }
2335
2336
        /* Order desc by date,  by default */
2337
        if (true == $orderDesc) {
2338
            ksort($data);
2339
        } else {
2340
            krsort($data);
2341
        }
2342
2343
        return $data;
2344
    }
2345
2346
    public function setAttendanceForm(FormValidator $form, CAttendance $attendance = null)
2347
    {
2348
        $skillList = [];
2349
        $header = get_lang('Create a new attendance list');
2350
        if ($attendance) {
2351
            $header = get_lang('Edit');
2352
            $form->addHidden('attendance_id', $attendance->getIid());
2353
        }
2354
        $form->addHeader($header);
2355
2356
        //$form->addElement('hidden', 'sec_token', $token);
2357
        $form->addText('title', get_lang('Title'), true);
2358
        $form->applyFilter('title', 'html_filter');
2359
        $form->addHtmlEditor(
2360
            'description',
2361
            get_lang('Description'),
2362
            false,
2363
            false,
2364
            ['ToolbarSet' => 'Basic', 'Width' => '100%', 'Height' => '150']
2365
        );
2366
2367
        $sessionId = api_get_session_id();
2368
2369
        // Advanced Parameters
2370
        if ((0 != $sessionId && Gradebook::is_active()) || 0 == $sessionId) {
2371
            $form->addButtonAdvancedSettings('id_qualify');
2372
            $form->addHtml('<div id="id_qualify_options" style="display:none">');
2373
2374
            // Qualify Attendance for gradebook option
2375
            $form->addCheckBox(
2376
                'attendance_qualify_gradebook',
2377
                '',
2378
                get_lang('Grade the attendance list in the assessment tool'),
2379
                ['onclick' => '"javascript: if(this.checked){document.getElementById(\'options_field\').style.display = \'block\';}else{document.getElementById(\'options_field\').style.display = \'none\';}"']
2380
            );
2381
            $form->addElement('html', '<div id="options_field" style="display:none">');
2382
2383
            GradebookUtils::load_gradebook_select_in_tool($form);
2384
2385
            $form->addElement('text', 'attendance_qualify_title', get_lang('Column header in Competences Report'));
2386
            $form->applyFilter('attendance_qualify_title', 'html_filter');
2387
            $form->addElement(
2388
                'text',
2389
                'attendance_weight',
2390
                get_lang('Weight in Report'),
2391
                'value="0.00" Style="width:40px" onfocus="javascript: this.select();"'
2392
            );
2393
            $form->applyFilter('attendance_weight', 'html_filter');
2394
            $form->addElement('html', '</div>');
2395
2396
            $skillList = SkillModel::addSkillsToForm($form, ITEM_TYPE_ATTENDANCE, $attendance ? $attendance->getIid() : 0);
2397
2398
            $form->addElement('html', '</div>');
2399
        }
2400
2401
        if ($attendance) {
2402
            $form->addButtonUpdate(get_lang('Update'));
2403
        } else {
2404
            $form->addButtonCreate(get_lang('Save'));
2405
        }
2406
2407
        if ($attendance) {
2408
            $default = [];
2409
            $default['title'] = Security::remove_XSS($attendance->getTitle());
2410
            $default['description'] = Security::remove_XSS($attendance->getDescription(), STUDENT);
2411
            $default['attendance_qualify_title'] = $attendance->getAttendanceQualifyTitle();
2412
            $default['attendance_weight'] = $attendance->getAttendanceWeight();
2413
            $default['skills'] = array_keys($skillList);
2414
2415
            $link_info = GradebookUtils::isResourceInCourseGradebook(
2416
                api_get_course_int_id(),
2417
                7,
2418
                $attendance->getIid(),
2419
                $sessionId
2420
            );
2421
            if ($link_info) {
2422
                $default['category_id'] = $link_info['category_id'];
2423
            }
2424
            $form->setDefaults($default);
2425
        }
2426
2427
        return $form;
2428
    }
2429
2430
    public function getCalendarSheet($edit, CAttendance $attendance, $student_id)
2431
    {
2432
        $attendanceId = $attendance->getIid();
2433
        $content = '';
2434
        $groupId = isset($_REQUEST['group_id']) ? $_REQUEST['group_id'] : null;
2435
        $filter_type = 'today';
2436
        if (!empty($_REQUEST['filter'])) {
2437
            $filter_type = $_REQUEST['filter'];
2438
        }
2439
2440
        $users_in_course = $this->get_users_rel_course($attendanceId, $groupId);
2441
        $is_locked_attendance = $this->is_locked_attendance($attendanceId);
2442
2443
        $attendant_calendar_all = $this->get_attendance_calendar(
2444
            $attendanceId,
2445
            'all',
2446
            null,
2447
            $groupId
2448
        );
2449
        $attendant_calendar = $this->get_attendance_calendar(
2450
            $attendanceId,
2451
            $filter_type,
2452
            null,
2453
            $groupId
2454
        );
2455
2456
        $allowToEdit = api_is_allowed_to_edit(null, true);
2457
2458
        $isDrhOfCourse = api_is_drh() || CourseManager::isUserSubscribedInCourseAsDrh(
2459
                api_get_user_id(),
2460
                api_get_course_info()
2461
            );
2462
        if ($edit) {
2463
            if ($isDrhOfCourse || $allowToEdit) {
2464
                $users_presence = $this->get_users_attendance_sheet(
2465
                    $attendanceId,
2466
                    0,
2467
                    $groupId
2468
                );
2469
            }
2470
        } else {
2471
            if (!empty($student_id)) {
2472
                $user_id = (int) $student_id;
2473
            } else {
2474
                $user_id = api_get_user_id();
2475
            }
2476
2477
            if ($isDrhOfCourse ||
2478
                $allowToEdit ||
2479
                api_is_coach(api_get_session_id(), api_get_course_int_id())
2480
            ) {
2481
                $users_presence = $this->get_users_attendance_sheet($attendanceId, 0, $groupId);
2482
            } else {
2483
                $users_presence = $this->get_users_attendance_sheet($attendanceId, $user_id, $groupId);
2484
            }
2485
            $faults = $this->get_faults_of_user($user_id, $attendanceId, $groupId);
2486
        }
2487
2488
        $next_attendance_calendar_id = $this->get_next_attendance_calendar_id($attendanceId);
2489
        $next_attendance_calendar_datetime = $this->getNextAttendanceCalendarDatetime($attendanceId);
2490
2491
        if ($isDrhOfCourse ||
2492
            $allowToEdit ||
2493
            api_is_coach(api_get_session_id(), api_get_course_int_id())
2494
        ) {
2495
            $form = new FormValidator(
2496
                'filter',
2497
                'post',
2498
                'index.php?action=attendance_sheet_list&'.api_get_cidreq().'&attendance_id='.$attendanceId,
2499
                null,
2500
                [],
2501
                'inline'
2502
            );
2503
2504
            $values = [
2505
                'all' => get_lang('All'),
2506
                'today' => get_lang('Today'),
2507
                'all_done' => get_lang('All done'),
2508
                'all_not_done' => get_lang('All not done'),
2509
            ];
2510
            $today = api_convert_and_format_date(null, DATE_FORMAT_SHORT);
2511
            $exists_attendance_today = false;
2512
2513
            if (!empty($attendant_calendar_all)) {
2514
                $values[''] = '---------------';
2515
                foreach ($attendant_calendar_all as $attendance_date) {
2516
                    $includeCalendar = true;
2517
                    if (isset($attendance_date['groups']) && !empty($groupId)) {
2518
                        foreach ($attendance_date['groups'] as $group) {
2519
                            if ($groupId == $group['group_id']) {
2520
                                $includeCalendar = true;
2521
2522
                                break;
2523
                            } else {
2524
                                $includeCalendar = false;
2525
                            }
2526
                        }
2527
                    }
2528
2529
                    if ($today == $attendance_date['date']) {
2530
                        $exists_attendance_today = true;
2531
                    }
2532
                    if ($includeCalendar) {
2533
                        $values[$attendance_date['iid']] = $attendance_date['date_time'];
2534
                    }
2535
                }
2536
            }
2537
2538
            if (!$exists_attendance_today) {
2539
                $content .= Display::return_message(
2540
                    get_lang(
2541
                        'There is no class scheduled today, try picking another day or add your attendance entry yourself using the action icons.'
2542
                    ),
2543
                    'warning'
2544
                );
2545
            }
2546
2547
            $form->addSelect(
2548
                'filter',
2549
                get_lang('Filter'),
2550
                $values,
2551
                ['id' => 'filter_id', 'onchange' => 'submit();']
2552
            );
2553
2554
            $groupList = GroupManager::get_group_list(null, null, 1);
2555
            $groupIdList = ['--'];
2556
            foreach ($groupList as $group) {
2557
                $groupIdList[$group['iid']] = $group['title'];
2558
            }
2559
2560
            if (!empty($groupList)) {
2561
                $form->addSelect('group_id', get_lang('Group'), $groupIdList);
2562
            }
2563
2564
            $default_filter = 'today';
2565
            if (isset($_REQUEST['filter'])) {
2566
                if (in_array($_REQUEST['filter'], array_keys($values))) {
2567
                    $default_filter = $_REQUEST['filter'];
2568
                }
2569
            }
2570
            $renderer = $form->defaultRenderer();
2571
            $renderer->setCustomElementTemplate(
2572
                '<div class="col-md-2">{label}</div><div class="col-md-10"> {element} </div>'
2573
            );
2574
2575
            $form->setDefaults(
2576
                [
2577
                    'filter' => $default_filter,
2578
                    'group_id' => $groupId,
2579
                ]
2580
            );
2581
2582
            if (!$is_locked_attendance || api_is_platform_admin()) {
2583
                $actionsLeft = '<a
2584
                    style="float:left;"
2585
                    href="index.php?'.api_get_cidreq().'&action=calendar_list&attendance_id='.$attendanceId.'">'.
2586
                    Display::getMdiIcon(ObjectIcon::AGENDA, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Attendance calendar')).
2587
                    '</a>';
2588
                $actionsLeft .= '<a
2589
                    id="pdf_export" style="float:left;"
2590
                    href="index.php?'.api_get_cidreq().'&action=attendance_sheet_export_to_pdf&attendance_id='.$attendanceId.'&filter='.$default_filter.'&group_id='.$groupId.'">'.
2591
                    Display::getMdiIcon(ActionIcon::EXPORT_PDF, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Export to PDF')).'</a>';
2592
2593
                $actionsRight = $form->returnForm();
2594
                $content .= Display::toolbarAction('toolbar-attendance', [$actionsLeft, $actionsRight]);
2595
            }
2596
2597
            $message_information = get_lang(
2598
                'The attendance sheets allow you to specify a list of dates in which you will report attendance to your courses'
2599
            );
2600
            if (!empty($message_information)) {
2601
                $message = '<strong>'.get_lang('Information').'</strong><br />';
2602
                $message .= $message_information;
2603
                $content .= Display::return_message($message, 'normal', false);
2604
            }
2605
2606
            if ($is_locked_attendance) {
2607
                $content .= Display::return_message(get_lang('The attendance sheet is locked.'), 'warning', false);
2608
            }
2609
2610
            $param_filter = '&filter='.Security::remove_XSS($default_filter).'&group_id='.$groupId;
2611
2612
            if (count($users_in_course) > 0) {
2613
                $form = '
2614
                <form method="post" action="index.php?action=attendance_sheet_add&'.api_get_cidreq().$param_filter.'&attendance_id='.$attendanceId.'">
2615
                    <div
2616
                        class="attendance-sheet-content"
2617
                        style="width:100%;background-color:#E1E1E1;margin-top:20px;">
2618
                        <div
2619
                            class="divTableWithFloatingHeader attendance-users-table"
2620
                            style="width:45%;float:left;margin:0px;padding:0px;">
2621
                            <table class="tableWithFloatingHeader data_table" width="100%">
2622
                                <thead>
2623
                                <tr class="tableFloatingHeader"
2624
                                    style="position: absolute; top: 0px; left: 0px; visibility: hidden; margin:0px;padding:0px" >
2625
                                    <th width="10px">#</th>
2626
                                    <th width="10px">'.get_lang('Photo').'</th>
2627
                                    <th width="100px">'.get_lang('Last name').'</th>
2628
                                    <th width="100px">'.get_lang('First name').'</th>
2629
                                    <th width="100px">'.get_lang('Not attended').'</th>
2630
                                </tr>
2631
                                <tr class="tableFloatingHeaderOriginal">
2632
                                    <th width="10px">#</th>
2633
                                    <th width="10px">'.get_lang('Photo').'</th>
2634
                                    <th width="150px">'.get_lang('Last name').'</th>
2635
                                    <th width="140px">'.get_lang('First name').'</th>
2636
                                    <th width="100px">'.get_lang('Not attended').'</th>
2637
                                </tr>
2638
                                </thead>
2639
                                <tbody>';
2640
                $i = 1;
2641
                foreach ($users_in_course as $data) {
2642
                    $faults = 0;
2643
                    $class = 'row_even';
2644
                    if (0 == $i % 2) {
2645
                        $class = 'row_odd';
2646
                    }
2647
                    $username = api_htmlentities(
2648
                        sprintf(get_lang('Login: %s'), $data['username']),
2649
                        ENT_QUOTES
2650
                    );
2651
2652
                    $form .= '<tr class="'.$class.'">
2653
                                <td><center>'.$i.'</center></td>
2654
                                <td>'.$data['photo'].'</td>
2655
                                <td><span title="'.$username.'">'.$data['lastname'].'</span></td>
2656
                                <td>'.$data['firstname'].'</td>
2657
                                <td>
2658
                                    <div class="attendance-faults-bar"
2659
                                    style="background-color:'.(!empty($data['result_color_bar']) ? $data['result_color_bar'] : 'none').'">
2660
                                        '.$data['attendance_result'].'
2661
                                    </div>
2662
                                </td>
2663
                            </tr>';
2664
                    $i++;
2665
                }
2666
                $form .= '</tbody>
2667
                            </table>
2668
                        </div>';
2669
2670
                $form .= '<div
2671
                    class="divTableWithFloatingHeader attendance-calendar-table"
2672
                    style="margin:0px;padding:0px;float:left;width:55%;overflow:auto;overflow-y:hidden;">';
2673
                $form .= '<table class="tableWithFloatingHeader data_table" width="100%">';
2674
                $form .= '<thead>';
2675
                $result = null;
2676
                if (count($attendant_calendar) > 0) {
2677
                    foreach ($attendant_calendar as $calendar) {
2678
                        $calendarId = $calendar['iid'];
2679
                        $date = $calendar['date'];
2680
                        $time = $calendar['time'];
2681
                        $datetime = '<div class="grey">'.$date.' - '.$time.'</div>';
2682
2683
                        $img_lock = Display::getMdiIcon(ActionIcon::LOCK, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Unlock date'));
2684
2685
                        if (!empty($calendar['done_attendance'])) {
2686
                            $datetime = '<div class="blue">'.$date.' - '.$time.'</div>';
2687
                        }
2688
                        $disabled_check = 'disabled = "true"';
2689
                        $input_hidden = '<input
2690
                            type="hidden" id="hidden_input_'.$calendarId.'" name="hidden_input[]" value="" disabled />';
2691
                        if ($next_attendance_calendar_id == $calendarId) {
2692
                            $input_hidden = '<input
2693
                                type="hidden"
2694
                                id="hidden_input_'.$calendarId.'" name="hidden_input[]"
2695
                                value="'.$calendarId.'" />';
2696
                            $disabled_check = '';
2697
                            $img_lock = Display::getMdiIcon(ActionIcon::LOCK, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Lock date'));
2698
                        }
2699
2700
                        $result .= '<th>';
2701
                        $result .= '<div class="date-attendance">'.$datetime.'&nbsp;';
2702
                        if ($allowToEdit) {
2703
                            $result .= '<span
2704
                                id="attendance_lock"
2705
                                style="cursor:pointer">'.
2706
                                (!$is_locked_attendance || api_is_platform_admin() ? $img_lock : '').
2707
                                '</span>';
2708
                        }
2709
2710
                        if (false == $is_locked_attendance) {
2711
                            if ($allowToEdit) {
2712
                                $result .= '<input
2713
                                        type="checkbox"
2714
                                        class="checkbox_head_'.$calendarId.'"
2715
                                        id="checkbox_head_'.$calendarId.'" '.$disabled_check.' checked="checked" />'.
2716
                                    $input_hidden.'</div></th>';
2717
                            }
2718
                        }
2719
                    }
2720
                } else {
2721
                    $result = '<th width="2000px"><span>
2722
                    <a href="index.php?'.api_get_cidreq().'&action=calendar_list&attendance_id='.$attendanceId.'">';
2723
                    $result .= Display::getMdiIcon(ObjectIcon::AGENDA, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Attendance calendar')).' '.get_lang('Go to the attendance calendar');
2724
                    $result .= '</a></span></th>';
2725
                }
2726
2727
                $form .= '<tr
2728
                        class="tableFloatingHeader row_odd"
2729
                        style="position: absolute; top: 0px; left: 0px; visibility: hidden; margin:0px;padding:0px">';
2730
                $form .= $result;
2731
                $form .= '</tr>';
2732
                $form .= '<tr class="tableWithFloatingHeader row_odd tableFloatingHeaderOriginal">';
2733
                $form .= $result;
2734
                $form .= '</tr>';
2735
                $form .= '</thead>';
2736
                $form .= '<tbody>';
2737
                $i = 0;
2738
                foreach ($users_in_course as $user) {
2739
                    $class = 'row_odd';
2740
                    if (0 == $i % 2) {
2741
                        $class = 'row_even';
2742
                    }
2743
                    $form .= '<tr class="'.$class.'">';
2744
                    if (count($attendant_calendar) > 0) {
2745
                        foreach ($attendant_calendar as $calendar) {
2746
                            $checked = 'checked';
2747
                            $presence = -1;
2748
                            if (isset($users_presence[$user['user_id']][$calendar['iid']]['presence'])) {
2749
                                $presence = $users_presence[$user['user_id']][$calendar['iid']]['presence'];
2750
2751
                                $checked = '';
2752
                                if (1 == (int) $presence) {
2753
                                    $checked = 'checked';
2754
                                }
2755
                            } else {
2756
                                //if the user wasn't registered at that time, consider unchecked
2757
                                if (0 == $next_attendance_calendar_datetime ||
2758
                                    $calendar['date_time'] < $next_attendance_calendar_datetime
2759
                                ) {
2760
                                    $checked = '';
2761
                                }
2762
                            }
2763
                            $disabled = 'disabled';
2764
                            $style_td = '';
2765
                            if ($next_attendance_calendar_id == $calendar['iid']) {
2766
                                if (0 == $i % 2) {
2767
                                    $style_td = 'background-color:#eee;';
2768
                                } else {
2769
                                    $style_td = 'background-color:#dcdcdc;';
2770
                                }
2771
                                $disabled = '';
2772
                            }
2773
2774
                            $form .= '<td style="'.$style_td.'" class="checkboxes_col_'.$calendar['iid'].'">';
2775
                            $form .= '<div class="check">';
2776
                            if ($allowToEdit) {
2777
                                if (!$is_locked_attendance || api_is_platform_admin()) {
2778
                                    $form .= '<input
2779
                                    type="checkbox"
2780
                                    name="check_presence['.$calendar['iid'].'][]"
2781
                                    value="'.$user['user_id'].'" '.$disabled.' '.$checked.' />';
2782
                                    $form .= '<span class="anchor_'.$calendar['iid'].'"></span>';
2783
                                } else {
2784
                                    $form .= $presence ? Display::getMdiIcon(StateIcon::CHECKBOX_MARKED, 'ch-tool-icon', null, ICON_SIZE_TINY, get_lang('Assistance')) : Display::getMdiIcon(StateIcon::CHECKBOX_BLANK, 'ch-tool-icon', null, ICON_SIZE_TINY, get_lang('Assistance'));
2785
                                }
2786
                            } else {
2787
                                switch ($presence) {
2788
                                    case 1:
2789
                                        $form .= Display::getMdiIcon(StateIcon::CHECKBOX_BLANK, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Attended'));
2790
2791
                                        break;
2792
                                    case 0:
2793
                                        $form .= Display::getMdiIcon(StateIcon::CHECKBOX_MARKED, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Not attended'));
2794
2795
                                        break;
2796
                                    case -1:
2797
                                        break;
2798
                                }
2799
                            }
2800
2801
                            $form .= '</div>';
2802
                            $form .= '</td>';
2803
                        }
2804
                    } else {
2805
                        $calendarClass = null;
2806
                        if (isset($calendar)) {
2807
                            $calendarClass = 'checkboxes_col_'.$calendar['iid'];
2808
                        }
2809
                        $form .= '<td class="'.$calendarClass.'">';
2810
                        $form .= '<div>';
2811
                        $form .= '<center>&nbsp;</center>
2812
                                </div>
2813
                                </td>';
2814
                    }
2815
                    echo '</tr>';
2816
                    $i++;
2817
                }
2818
                $form .= '</tbody></table>';
2819
                $form .= '</div></div>';
2820
2821
                $form .= '<div class="row">
2822
                            <div class="col-md-12">';
2823
                if (!$is_locked_attendance || api_is_platform_admin()) {
2824
                    if ($allowToEdit) {
2825
                        $form .= '<button type="submit" class="btn btn--primary">'.get_lang('Save').'</button>';
2826
                    }
2827
                }
2828
                $form .= '</div>
2829
                        </div>
2830
                </form>';
2831
                $content .= $form;
2832
            } else {
2833
                $content .= Display::return_message(
2834
                    '<a href="'.api_get_path(WEB_CODE_PATH).'user/user.php?'.api_get_cidreq().'">'.
2835
                    get_lang('There are no registered learners inside the course').'</a>',
2836
                    'warning',
2837
                    false
2838
                );
2839
            }
2840
        } else {
2841
            $content .= Display::page_header(get_lang('Report of attendance sheets'));
2842
            if (!empty($users_presence)) {
2843
                $content .= '
2844
                <div>
2845
                    <table width="250px;">
2846
                        <tr>
2847
                            <td>'.get_lang('To attend').': </td>
2848
                            <td>
2849
                                <center>
2850
                                <div
2851
                                    class="attendance-faults-bar"
2852
                                    style="background-color:'.(!empty($faults['color_bar']) ? $faults['color_bar'] : 'none').'">
2853
                                        '.$faults['faults'].'/'.$faults['total'].' ('.$faults['faults_porcent'].'%)
2854
                                </div>
2855
                                </center>
2856
                            </td>
2857
                        </tr>
2858
                    </table>
2859
                </div>';
2860
            }
2861
2862
            $content .= '<table class="data_table">
2863
                <tr class="row_odd" >
2864
                    <th>'.get_lang('Attendance').'</th>
2865
                </tr>';
2866
2867
            if (!empty($users_presence)) {
2868
                $i = 0;
2869
                foreach ($users_presence[$user_id] as $presence) {
2870
                    $class = '';
2871
                    if (0 == $i % 2) {
2872
                        $class = 'row_even';
2873
                    } else {
2874
                        $class = 'row_odd';
2875
                    }
2876
2877
                    $check = $presence['presence'] ? Display::getMdiIcon(StateIcon::CHECKBOX_MARKED, 'ch-tool-icon', null, ICON_SIZE_TINY, get_lang('Assistance')) : Display::getMdiIcon(StateIcon::CHECKBOX_BLANK, 'ch-tool-icon', null, ICON_SIZE_TINY, get_lang('Assistance'));
2878
                    $content .= '<tr class="'.$class.'">
2879
                            <td>
2880
                                '.$check.'&nbsp; '.$presence['date_time'].'
2881
                            </td>
2882
                        </tr>';
2883
                }
2884
            } else {
2885
                $content .= '
2886
                        <tr>
2887
                            <td>
2888
                            <center>'.get_lang('You do not have attendances').'</center>
2889
                            </td>
2890
                        </tr>';
2891
            }
2892
            $content .= ' </table>';
2893
        }
2894
2895
        return $content;
2896
    }
2897
2898
    /**
2899
     * It's used to print attendance sheet.
2900
     *
2901
     * @param int $attendance_id
2902
     */
2903
    public function attendance_sheet_export_to_pdf(
2904
        $attendance_id,
2905
        $student_id = 0,
2906
        $course_id = ''
2907
    ) {
2908
        $courseInfo = api_get_course_info($course_id);
2909
        $this->set_course_id($courseInfo['code']);
2910
        $groupId = isset($_REQUEST['group_id']) ? $_REQUEST['group_id'] : null;
2911
        $data_array = [];
2912
        $data_array['attendance_id'] = $attendance_id;
2913
        $data_array['users_in_course'] = $this->get_users_rel_course($attendance_id, $groupId);
2914
2915
        $filter_type = 'today';
2916
2917
        if (!empty($_REQUEST['filter'])) {
2918
            $filter_type = $_REQUEST['filter'];
2919
        }
2920
2921
        $my_calendar_id = null;
2922
        if (is_numeric($filter_type)) {
2923
            $my_calendar_id = $filter_type;
2924
            $filter_type = 'calendar_id';
2925
        }
2926
2927
        $data_array['attendant_calendar'] = $this->get_attendance_calendar(
2928
            $attendance_id,
2929
            $filter_type,
2930
            $my_calendar_id,
2931
            $groupId
2932
        );
2933
2934
        if (api_is_allowed_to_edit(null, true) || api_is_drh()) {
2935
            $data_array['users_presence'] = $this->get_users_attendance_sheet($attendance_id, 0, $groupId);
2936
        } else {
2937
            if (!empty($student_id)) {
2938
                $user_id = (int) $student_id;
2939
            } else {
2940
                $user_id = api_get_user_id();
2941
            }
2942
            $data_array['users_presence'] = $this->get_users_attendance_sheet($attendance_id, $user_id, $groupId);
2943
            $data_array['faults'] = $this->get_faults_of_user($user_id, $attendance_id, $groupId);
2944
            $data_array['user_id'] = $user_id;
2945
        }
2946
2947
        $data_array['next_attendance_calendar_id'] = $this->get_next_attendance_calendar_id($attendance_id);
2948
2949
        // Set headers pdf.
2950
        $courseCategory = CourseManager::get_course_category($courseInfo['categoryCode']);
2951
        $teacherInfo = CourseManager::get_teacher_list_from_course_code($courseInfo['code']);
2952
        $teacherName = null;
2953
        foreach ($teacherInfo as $teacherData) {
2954
            if (null != $teacherName) {
2955
                $teacherName .= ' / ';
2956
            }
2957
            $teacherName .= api_get_person_name($teacherData['firstname'], $teacherData['lastname']);
2958
        }
2959
2960
        // Get data table
2961
        $data_table = [];
2962
        $head_table = ['#', get_lang('Name')];
2963
        foreach ($data_array['attendant_calendar'] as $class_day) {
2964
            $head_table[] =
2965
                api_format_date($class_day['date_time'], DATE_FORMAT_NUMBER_NO_YEAR).' '.
2966
                api_format_date($class_day['date_time'], TIME_NO_SEC_FORMAT);
2967
        }
2968
        $data_table[] = $head_table;
2969
        $data_attendant_calendar = $data_array['attendant_calendar'];
2970
        $data_users_presence = $data_array['users_presence'];
2971
        $count = 1;
2972
2973
        if (!empty($data_array['users_in_course'])) {
2974
            foreach ($data_array['users_in_course'] as $user) {
2975
                $cols = 1;
2976
                $result = [];
2977
                $result['count'] = $count;
2978
                $result['full_name'] = api_get_person_name($user['firstname'], $user['lastname']);
2979
                foreach ($data_array['attendant_calendar'] as $class_day) {
2980
                    if (1 == $class_day['done_attendance']) {
2981
                        if (1 == $data_users_presence[$user['user_id']][$class_day['iid']]['presence']) {
2982
                            $result[$class_day['iid']] = get_lang('P');
2983
                        } else {
2984
                            $result[$class_day['iid']] = '<span style="color:red">'.get_lang('NP').'</span>';
2985
                        }
2986
                    } else {
2987
                        $result[$class_day['iid']] = ' ';
2988
                    }
2989
                    $cols++;
2990
                }
2991
                $count++;
2992
                $data_table[] = $result;
2993
            }
2994
        }
2995
        $max_cols_per_page = 12; //10 dates + 2 name and number
2996
        $max_dates_per_page = $max_dates_per_page_original = $max_cols_per_page - 2; //10
2997
        $rows = count($data_table);
2998
2999
        if ($cols > $max_cols_per_page) {
3000
            $number_tables = round(($cols - 2) / $max_dates_per_page);
3001
            $headers = $data_table[0];
3002
            $all = [];
3003
            $tables = [];
3004
            $changed = 1;
3005
3006
            for ($i = 0; $i <= $rows; $i++) {
3007
                $row = isset($data_table[$i]) ? $data_table[$i] : null;
3008
                $key = 1;
3009
                $max_dates_per_page = 10;
3010
                $item = isset($data_table[$i]) ? $data_table[$i] : null;
3011
                $count_j = 0;
3012
3013
                if (!empty($item)) {
3014
                    foreach ($item as $value) {
3015
                        if ($count_j >= $max_dates_per_page) {
3016
                            $key++;
3017
                            $max_dates_per_page = $max_dates_per_page_original * $key;
3018
                            //magic hack
3019
                            $tables[$key][$i][] = $tables[1][$i][0];
3020
                            $tables[$key][$i][] = $tables[1][$i][1];
3021
                        }
3022
                        $tables[$key][$i][] = $value;
3023
                        $count_j++;
3024
                    }
3025
                }
3026
            }
3027
3028
            $content = null;
3029
            if (!empty($tables)) {
3030
                foreach ($tables as $sub_table) {
3031
                    $content .= Export::convert_array_to_html($sub_table).'<br /><br />';
3032
                }
3033
            }
3034
        } else {
3035
            $content = Export::convert_array_to_html(
3036
                $data_table,
3037
                ['header_attributes' => ['align' => 'center']]
3038
            );
3039
        }
3040
3041
        $params = [
3042
            'filename' => get_lang('Attendance').'-'.api_get_local_time(),
3043
            'pdf_title' => $courseInfo['title'],
3044
            'course_code' => $courseInfo['code'],
3045
            'add_signatures' => ['Drh', 'Teacher', 'Date'],
3046
            'pdf_teachers' => $teacherName,
3047
            'pdf_course_category' => $courseCategory ? $courseCategory['title'] : '',
3048
            'format' => 'A4-L',
3049
            'orientation' => 'L',
3050
        ];
3051
        Export::export_html_to_pdf($content, $params);
3052
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
3053
    }
3054
3055
    /**
3056
     * Gets attendance base in the table:
3057
     * TABLE_STATISTIC_TRACK_E_COURSE_ACCESS.
3058
     *
3059
     * @param bool $showForm
3060
     * @param bool $exportToPdf
3061
     */
3062
    public function getAttendanceBaseInLogin($showForm = false, $exportToPdf = true)
3063
    {
3064
        $table = null;
3065
        $formToDisplay = null;
3066
        $startDate = null;
3067
        $endDate = null;
3068
3069
        $sessionId = api_get_session_id();
3070
        if ($showForm) {
3071
            $form = new FormValidator(
3072
                'search',
3073
                'post',
3074
                api_get_self().'?'.api_get_cidreq().'&action=calendar_logins'
3075
            );
3076
            $form->addDateRangePicker('range', get_lang('Date range'));
3077
            $form->addButton('submit', get_lang('Submit'));
3078
3079
            if ($form->validate()) {
3080
                $values = $form->getSubmitValues();
3081
                $startDate = api_get_utc_datetime($values['range_start']);
3082
                $endDate = api_get_utc_datetime($values['range_end']);
3083
            }
3084
            $formToDisplay = $form->returnForm();
3085
        } else {
3086
            if (!empty($sessionId)) {
3087
                $sessionInfo = api_get_session_info($sessionId);
3088
                $startDate = $sessionInfo['access_start_date'];
3089
                $endDate = $sessionInfo['access_end_date'];
3090
            }
3091
        }
3092
3093
        if ($exportToPdf) {
3094
            $result = $this->exportAttendanceLogin($startDate, $endDate);
3095
            if (empty($result)) {
3096
                return false;
3097
                //api_not_allowed(true, get_lang('No data available'));
3098
            }
3099
        }
3100
3101
        $table = $this->getAttendanceLoginTable($startDate, $endDate);
3102
3103
        return [
3104
            'form' => $formToDisplay,
3105
            'table' => $table,
3106
        ];
3107
    }
3108
}
3109