Issues (350)

app/Http/Controllers/AttendanceController.php (8 issues)

1
<?php
2
3
namespace App\Http\Controllers;
4
5
use App\Models\Attendance;
6
use App\Models\AttendanceType;
7
use App\Models\Course;
8
use App\Models\Event;
9
use App\Models\Student;
10
use App\Traits\PeriodSelection;
11
use Carbon\Carbon;
12
use Illuminate\Http\Request;
13
use Illuminate\Support\Facades\Gate;
14
use Illuminate\Support\Facades\Log;
15
use Prologue\Alerts\Facades\Alert;
16
17
class AttendanceController extends Controller
18
{
19
    use PeriodSelection;
0 ignored issues
show
The trait App\Traits\PeriodSelection requires the property $id which is not provided by App\Http\Controllers\AttendanceController.
Loading history...
20
21
    public function __construct()
22
    {
23
        parent::__construct();
24
        $this->middleware('permission:attendance.view', ['except' => ['showCourse', 'showEvent', 'showStudentAttendanceForCourse', 'store']]);
25
    }
26
27
    /**
28
     * Monitor attendance for all students.
29
     */
30
    public function index(Request $request)
31
    {
32
        Log::info('Attendance dashboard viewed by '.backpack_user()->id);
0 ignored issues
show
Accessing id on the interface Illuminate\Contracts\Auth\Authenticatable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
33
        $selected_period = $this->selectPeriod($request);
34
35
        // student attendance overview
36
        $absences = (new Attendance())->get_absence_count_per_student($selected_period);
37
38
        // get all courses for period and preload relations
39
        $courses = $selected_period->courses()->whereHas('events')->whereHas('enrollments')->with('attendance')->with('events')->get();
40
41
        // loop through all courses and get the number of events with incomplete attendance
42
        foreach ($courses as $course) {
43
            $eventsWithMissingAttendanceCount = 0;
44
            $coursesdata[$course->id]['name'] = $course->name;
45
            $coursesdata[$course->id]['id'] = $course->id;
46
            $coursesdata[$course->id]['exempt_attendance'] = $course->exempt_attendance;
47
            $coursesdata[$course->id]['teachername'] = $course->course_teacher_name;
48
49
            foreach ($course->eventsWithExpectedAttendance as $event) {
50
                foreach ($course->enrollments as $enrollment) {
51
                    // if a student has no attendance record for the class (event)
52
                    $hasNotAttended = $course->attendance->where('student_id', $enrollment->student_id)
53
                    ->where('event_id', $event->id)
54
                    ->isEmpty();
55
56
                    // count one and break loop
57
                    if ($hasNotAttended) {
58
                        $eventsWithMissingAttendanceCount++;
59
                        break;
60
                    }
61
                }
62
            }
63
64
            $coursesdata[$course->id]['missing'] = $eventsWithMissingAttendanceCount;
65
        }
66
67
        // sort by number of events with missing attendance
68
        $courses = collect($coursesdata ?? [])->sortByDesc('missing')->toArray();
69
        $isadmin = backpack_user()->hasPermissionTo('courses.edit');
0 ignored issues
show
The method hasPermissionTo() does not exist on Illuminate\Contracts\Auth\Authenticatable. It seems like you code against a sub-type of Illuminate\Contracts\Auth\Authenticatable such as Illuminate\Foundation\Auth\User. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

69
        $isadmin = backpack_user()->/** @scrutinizer ignore-call */ hasPermissionTo('courses.edit');
Loading history...
70
71
        return view('attendance.monitor', compact('absences', 'courses', 'selected_period', 'isadmin'));
72
    }
73
74
    /**
75
     * Store a newly created attendance record.
76
     */
77
    public function store(Request $request)
78
    {
79
        $event = Event::findOrFail($request->input('event_id'));
80
81
        // If the user is not allowed to perform this action
82
        if (Gate::forUser(backpack_user())->denies('edit-attendance', $event)) {
83
            abort(403);
84
        }
85
86
        $student = Student::findOrFail($request->input('student_id'));
87
        $attendance_type = AttendanceType::findOrFail($request->input('attendance_type_id'));
88
89
        $attendance = Attendance::firstOrNew([
90
            'student_id' => $student->id,
91
            'event_id' => $event->id,
92
        ]);
93
94
        $attendance->attendance_type_id = $attendance_type->id;
95
96
        $attendance->save();
97
98
        Log::info('Attendance recorded by '.backpack_user()->id);
0 ignored issues
show
Accessing id on the interface Illuminate\Contracts\Auth\Authenticatable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
99
100
        return $attendance;
101
    }
102
103
    /**
104
     * Show the attendance records for a course.
105
     */
106
    public function showCourse(Course $course)
107
    {
108
109
    // The current is not allowed to view the page
110
        if (Gate::forUser(backpack_user())->denies('view-course-attendance', $course)) {
111
            abort(403);
112
        }
113
114
        // get past events for the course
115
        $events = $course->events->filter(fn ($value, $key) => Carbon::parse($value->start) < Carbon::now())->sortByDesc('start');
0 ignored issues
show
The parameter $key is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

115
        $events = $course->events->filter(fn ($value, /** @scrutinizer ignore-unused */ $key) => Carbon::parse($value->start) < Carbon::now())->sortByDesc('start');

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
116
117
        // if the course has any past events
118
        if (($events->count() == 0) || ($course->enrollments()->count() == 0)) {
119
            Alert::add('error', 'This course has no events.')->flash();
120
121
            return redirect()->back();
122
        }
123
124
        $enrollments = $course->enrollments()->with('student')->get();
125
        $attendances = [];
126
127
        foreach ($enrollments as $enrollment) {
128
            foreach ($events as $event) {
129
                if ($event->exempt_attendance == 1) {
130
                    $attendances[$enrollment->student->id][]['attendance'] = '';
131
                } else {
132
                    $attendances[$enrollment->student->id]['student'] = $enrollment->student->firstname.' '.$enrollment->student->lastname;
133
                    $attendances[$enrollment->student->id][]['attendance'] = $event->attendance->where('student_id', $enrollment->student_id)->first();
134
                }
135
            }
136
        }
137
        Log::info('Attendance for course viewed by '.backpack_user()->id);
0 ignored issues
show
Accessing id on the interface Illuminate\Contracts\Auth\Authenticatable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
138
139
        $isadmin = backpack_user()->hasPermissionTo('courses.edit');
140
141
        return view('attendance/course', compact('attendances', 'isadmin', 'course', 'events'));
142
    }
143
144
    public function showEvent(Event $event)
145
    {
146
147
    // The current is not allowed to view the page
148
        if (Gate::forUser(backpack_user())->denies('view-event-attendance', $event)) {
149
            abort(403);
150
        }
151
152
        // get students
153
        $enrollments = $event->enrollments()->with('student')->get();
154
155
        // get the attendance record for the event
156
        $attendance = $event->attendance;
157
158
        $attendances = [];
159
160
        $attendance_types = AttendanceType::all();
161
162
        // build a collection : for each student, display attendance
163
164
        foreach ($enrollments as $enrollment) {
165
            $attendances[$enrollment->student->id]['student'] = $enrollment->student->name;
166
            $attendances[$enrollment->student->id]['student_id'] = $enrollment->student->id;
167
            $attendances[$enrollment->student->id]['attendance'] = $attendance->where('student_id', $enrollment->student->id)->first() ?? '[attendance][attendance_type_id]';
168
        }
169
        Log::info('Attendance for event viewed by '.backpack_user()->id);
0 ignored issues
show
Accessing id on the interface Illuminate\Contracts\Auth\Authenticatable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
170
171
        return view('attendance/event', compact('attendances', 'event', 'attendance_types'));
172
    }
173
174
    public function showStudentAttendanceForCourse(Student $student, Request $request)
175
    {
176
        if ($request->query('course_id') == null) {
0 ignored issues
show
It seems like you are loosely comparing $request->query('course_id') of type array|null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
177
            $selectedCourse = $student->enrollments->last()->course;
178
        } else {
179
            $selectedCourse = Course::find($request->query('course_id'));
180
        }
181
182
        // If the current is not allowed to view the page
183
        if (Gate::forUser(backpack_user())->denies('view-course-attendance', $selectedCourse)) {
184
            abort(403);
185
        }
186
187
        $studentEnrollments = $student->enrollments()->with('course')->get();
188
        $courseEventIds = $selectedCourse->events->pluck('id');
189
190
        $attendances = $student->attendance()->with('event')->get()->whereIn('event_id', $courseEventIds);
191
        $enrollment = $studentEnrollments->where('course_id', $selectedCourse->id)->first();
192
        $attendanceratio = $enrollment->attendance_ratio;
193
194
        return view('attendance.student', compact('student', 'selectedCourse', 'studentEnrollments', 'attendances', 'attendanceratio'));
195
    }
196
197
    public function toggleEventAttendanceStatus(Event $event, Request $request)
198
    {
199
        if (! backpack_user()->hasPermissionTo('courses.edit')) {
200
            abort(403);
201
        }
202
        $event->exempt_attendance = (int) $request->status;
203
        $event->save();
204
205
        return (int) $event->exempt_attendance;
206
    }
207
208
    public function toggleCourseAttendanceStatus(Course $course, Request $request)
209
    {
210
        if (! backpack_user()->hasPermissionTo('courses.edit')) {
211
            abort(403);
212
        }
213
        $course->exempt_attendance = (int) $request->status;
214
        $course->save();
215
216
        // apply the same change to all course events
217
        foreach ($course->events as $event) {
218
            $event->exempt_attendance = (int) $request->status;
219
            $event->save();
220
        }
221
222
        return (int) $course->exempt_attendance;
223
    }
224
}
225