ApolearnService   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 241
Duplicated Lines 0 %

Importance

Changes 8
Bugs 0 Features 0
Metric Value
eloc 130
c 8
b 0
f 0
dl 0
loc 241
rs 9.92
wmc 31

11 Methods

Rating   Name   Duplication   Size   Complexity  
A updateUser() 0 19 3
A __construct() 0 4 1
A authenticate() 0 10 1
A createUser() 0 34 4
A createCourse() 0 34 2
B updateCourse() 0 49 9
A removeStudent() 0 7 1
A addTeacher() 0 18 3
A removeTeacher() 0 11 1
A enrollStudent() 0 17 3
A actionSucceeded() 0 3 3
1
<?php
2
3
namespace App\Services;
4
5
use App\Interfaces\LMSInterface;
6
use App\Models\Course;
7
use App\Models\Student;
8
use App\Models\User;
9
use Illuminate\Http\Client\Response;
10
use Illuminate\Support\Arr;
11
use Illuminate\Support\Facades\Http;
12
use Illuminate\Support\Facades\Log;
13
14
class ApolearnService implements LMSInterface
15
{
16
    public string $apiKey;
17
18
    private string $token;
19
20
    public function __construct()
21
    {
22
        $this->apiKey = config('lms.apolearn.api_key');
23
        $this->token = $this->authenticate();
24
    }
25
26
    public function authenticate(): string
27
    {
28
        Log::info('launching API');
29
        $response = Http::post(config('lms.apolearn.url').'/auth.gettoken', [
30
            'api_key' => $this->apiKey,
31
            'username' => config('lms.apolearn.username'),
32
            'password' => config('lms.apolearn.password'),
33
        ]);
34
35
        return $response['result'] ?? '';
36
    }
37
38
    public function createUser(User $user, ?string $password = null): void
39
    {
40
        Log::info('checking if user exists for local ID '.$user->id);
41
        // first check if the user already exists (email)
42
        $response = Http::get(config('lms.apolearn.url')."/users/getbyemail/$user->email", [
43
            'auth_token' => $this->token,
44
            'api_key' => $this->apiKey,
45
        ]);
46
47
        // if the user exists, just update their ID
48
        if ($this->actionSucceeded($response)) {
49
            Log::info('user found, saving their LMS ID to our DB');
50
            $user->update(['lms_id' => $response->json()['result']['user']['id']]);
51
        } else {
52
            // otherwise create them
53
            Log::info('user not found, creating them on the remote API now');
54
            $data = [
55
                'firstname' => $user->firstname,
56
                'lastname' => $user->lastname,
57
                'email' => $user->email,
58
                'auth_token' => $this->token,
59
                'api_key' => $this->apiKey,
60
            ];
61
62
            if ($password !== '') {
63
                $data = Arr::add($data, 'password', $password);
64
            }
65
66
            $response = Http::post(config('lms.apolearn.url').'/users', $data);
67
68
            if ($this->actionSucceeded($response)) {
69
                $user->update(['lms_id' => $response->json()['result']['id']]);
70
            } else {
71
                Log::error($response->json());
0 ignored issues
show
Bug introduced by
$response->json() of type array is incompatible with the type string expected by parameter $message of Illuminate\Support\Facades\Log::error(). ( Ignorable by Annotation )

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

71
                Log::error(/** @scrutinizer ignore-type */ $response->json());
Loading history...
72
            }
73
        }
74
    }
75
76
    public function updateUser(User $user, string $password = null): void
77
    {
78
        // if the user has no remote id, create them
79
        if ($user->lms_id == null) {
80
            $this->createUser($user);
81
        } else {
82
            $data = [
83
                'firstname' => $user->firstname,
84
                'lastname' => $user->lastname,
85
                'email' => $user->email,
86
                'auth_token' => $this->token,
87
                'api_key' => $this->apiKey,
88
            ];
89
90
            if ($password !== '') {
91
                $data = Arr::add($data, 'password', $password);
92
            }
93
94
            $response = Http::put(config('lms.apolearn.url').'/users/'.$user->lms_id, $data);
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
95
        }
96
    }
97
98
    public function createCourse(Course $course): void
99
    {
100
        // first check if the course already has an ID (meaning it exists on the remote lms)
101
        if ($course->lms_id) {
102
            abort(422, 'This course already exists on the remote platform');
103
        }
104
105
        Log::info('pushing local course '.$course->id.' to API');
106
        $response = Http::post(config('lms.apolearn.url').'/classrooms', [
107
            'name' => $course->name,
108
            'shortname' => $course->shortname,
109
            'description' => $course->description,
110
            'start_date' => strtotime($course->start_date),
111
            'end_date' => strtotime($course->end_date),
112
            'category_id' => $course->rhythm->lms_id ?? config('lms.apolearn.default_category_id'),
113
            'level_id' => $course->level->lms_id ?? config('lms.apolearn.default_level_id'),
114
            'auth_token' => $this->token,
115
            'api_key' => $this->apiKey,
116
        ]);
117
118
        $courseId = $response->json()['result']['id'];
119
120
        // store the course ID in our database
121
        $course->update(['lms_id' => $courseId]);
122
123
        // assign an admin to the new class
124
        $response = Http::post(config('lms.apolearn.url')."/classrooms/addadmin/$courseId", [
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
125
            'user_id' => config('lms.apolearn.admin_user_id'),
126
            'auth_token' => $this->token,
127
            'api_key' => $this->apiKey,
128
        ]);
129
130
        // and a teacher
131
        $this->addTeacher($course);
132
    }
133
134
    public function updateCourse(Course $course): void
135
    {
136
        if (! $course->lms_id) {
137
            $this->createCourse($course);
138
        }
139
140
        Log::info('updating course with locale ID'.$course->id);
141
        $response = Http::put(config('lms.apolearn.url')."/classrooms/$course->lms_id", [
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
142
            'name' => $course->name,
143
            'shortname' => $course->shortname,
144
            'description' => $course->description,
145
            'start_date' => strtotime($course->start_date),
146
            'end_date' => strtotime($course->end_date),
147
            'category_id' => $course->rhythm->lms_id ?? config('lms.apolearn.default_category_id'),
148
            'level_id' => $course->level->lms_id ?? config('lms.apolearn.default_level_id'),
149
            'auth_token' => $this->token,
150
            'api_key' => $this->apiKey,
151
        ]);
152
153
        Log::info('updating the course teacher');
154
        // ensure the teacher is up to date
155
        $response = Http::get(config('lms.apolearn.url')."/classrooms/teachers/$course->lms_id", [
156
            'auth_token' => $this->token,
157
            'api_key' => $this->apiKey,
158
        ]);
159
160
        if ($this->actionSucceeded($response)) {
161
            $teachers = collect($response->json()['result']['users']);
162
            Log::info('found these teachers IDs on LMS:'.implode(', ', $teachers->pluck('id')->toArray()));
163
164
            // if the course has no teacher, stop
165
            if (! $course->teacher) {
166
                Log::alert('The course has no teacher on local system, removing all teachers from remote');
167
                foreach ($teachers as $teacher) {
168
                    $this->removeTeacher($course->lms_id, $teacher['id']);
169
                }
170
            } else {
171
                // check if remote course teachers are still valid
172
                foreach ($teachers as $teacher) {
173
                    Log::info('comparing '.$teacher['id'].' and '.$course->teacher->user->lms_id);
174
                    if ($teacher['id'] !== $course->teacher->user->lms_id) {
175
                        Log::info('Removing teacher '.$teacher['id'].' from course');
176
                        $this->removeTeacher($course->lms_id, $teacher['id']);
177
                    }
178
                }
179
180
                if (! $course->teacher->user->lms_id || ! $teachers->contains('id', $course->teacher->user->lms_id)) {
181
                    Log::info('the course teacher has changed, need to update it');
182
                    $this->addTeacher($course);
183
                }
184
            }
185
        }
186
    }
187
188
    public function enrollStudent(Course $course, Student $student): void
189
    {
190
        if (! $course->lms_id) {
191
            abort(404, 'This course is not synced with external LMS');
192
        }
193
194
        $courseId = $course->lms_id;
195
196
        // if the student is not synced with the LMS, create them
197
        if (! $student->user->lms_id) {
198
            $this->createUser($student->user);
199
        }
200
201
        $response = Http::post(config('lms.apolearn.url')."/classrooms/addstudent/$courseId", [
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
202
            'user_id' => $student->user->lms_id,
203
            'auth_token' => $this->token,
204
            'api_key' => $this->apiKey,
205
        ]);
206
    }
207
208
    protected function actionSucceeded(Response $response): bool
209
    {
210
        return $response->ok() && array_key_exists('result', $response->json()) && $response->json()['result']['success'] == true;
211
    }
212
213
    protected function addTeacher(Course $course): void
214
    {
215
        Log::info('adding teacher');
216
217
        // only process if the course has a teacher
218
        if ($course->teacher_id) {
219
            // if the teacher doesn't exist on LMS, create them
220
            if (! $course->teacher->user->lms_id) {
221
                Log::info('creating user now');
222
                $this->createUser($course->teacher->user);
223
            }
224
225
            // then sync to API
226
            Log::info('pushing course user to API');
227
            $response = Http::post(config('lms.apolearn.url')."/classrooms/addteacher/$course->lms_id", [
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
228
                'user_id' => $course->teacher->user->lms_id,
229
                'auth_token' => $this->token,
230
                'api_key' => $this->apiKey,
231
            ]);
232
        }
233
    }
234
235
    protected function removeTeacher($courseId, $teacherId): void
236
    {
237
        Log::info('Removing teacher '.$teacherId.' from course '.$courseId);
238
        $response = Http::put(config('lms.apolearn.url')."/classrooms/removeteacher/$courseId", [
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
239
            'user_id' => $teacherId,
240
            'auth_token' => $this->token,
241
            'api_key' => $this->apiKey,
242
        ]);
243
244
        Log::info('and removing them again as students');
245
        $this->removeStudent($courseId, $teacherId);
246
    }
247
248
    public function removeStudent($courseId, $userId): void
249
    {
250
        Log::info('removing user id '.$userId.' from course '.$courseId);
251
        $response = Http::put(config('lms.apolearn.url')."/classrooms/removestudent/$courseId", [
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
252
            'user_id' => $userId,
253
            'auth_token' => $this->token,
254
            'api_key' => $this->apiKey,
255
        ]);
256
    }
257
}
258