Test Setup Failed
Pull Request — master (#436)
by Mohamed
08:09
created

ExaminationStudentsController::searchSimilarName()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 36
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 14
Bugs 5 Features 2
Metric Value
cc 8
eloc 25
c 14
b 5
f 2
nc 8
nop 2
dl 0
loc 36
rs 8.4444
1
<?php
2
3
namespace App\Http\Controllers;
4
5
use Session;
6
use App\Models\Institution;
7
use Illuminate\Http\Request;
8
use App\Models\Security_user;
9
use App\Models\Academic_period;
10
use App\Models\Education_grade;
11
use App\Models\Institution_class;
12
use App\Notifications\ExportReady;
13
use App\Models\Examination_student;
14
use App\Models\Institution_student;
15
use Illuminate\Support\Facades\Log;
16
use Illuminate\Support\Facades\Storage;
17
use Illuminate\Support\Facades\Response;
18
use App\Models\Institution_class_student;
19
use App\Exports\ExaminationStudentsExport;
20
use App\Imports\ExaminationStudentsImport;
21
use App\Models\Institution_student_admission;
22
23
class ExaminationStudentsController extends Controller
24
{
25
    public function __construct($year = 2019, $grade = 'G5')
26
    {
27
        $this->year = $year;
0 ignored issues
show
Bug Best Practice introduced by
The property year does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
28
        $this->grade = $grade;
0 ignored issues
show
Bug Best Practice introduced by
The property grade does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
29
        $this->student = new Security_user();
0 ignored issues
show
Bug Best Practice introduced by
The property student does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
30
        $this->examination_student = new Examination_student();
0 ignored issues
show
Bug Best Practice introduced by
The property examination_student does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
31
        $this->academic_period =  Academic_period::where('code', '=', $this->year)->first();
0 ignored issues
show
Bug Best Practice introduced by
The property academic_period does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
32
        $this->education_grade = Education_grade::where('code', '=', $this->grade)->first();
0 ignored issues
show
Bug Best Practice introduced by
The property education_grade does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
33
        $this->output = new \Symfony\Component\Console\Output\ConsoleOutput();
0 ignored issues
show
Bug Best Practice introduced by
The property output does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
34
    }
35
36
    public function index()
37
    {
38
        return view('uploadcsv');
39
    }
40
41
    public function uploadFile(Request $request)
42
    {
43
44
        if ($request->input('submit') != null) {
45
46
            $file = $request->file('file');
47
48
            // File Details
49
            $filename = 'exams_students.csv';
50
            $extension = $file->getClientOriginalExtension();
51
            $fileSize = $file->getSize();
52
53
            // Valid File Extensions
54
            $valid_extension = array("csv");
55
56
            // 20MB in Bytes
57
            $maxFileSize = 30971520;
58
59
            // Check file extension
60
            if (in_array(strtolower($extension), $valid_extension)) {
61
62
                // Check file size
63
                if ($fileSize <= $maxFileSize) {
64
65
                    // File upload location
66
                    Storage::disk('local')->putFileAs(
67
                        'examination/',
68
                        $file,
0 ignored issues
show
Bug introduced by
It seems like $file can also be of type Illuminate\Http\UploadedFile[] and array; however, parameter $file of Illuminate\Filesystem\Fi...temAdapter::putFileAs() does only seem to accept Illuminate\Http\File|Illuminate\Http\UploadedFile, maybe add an additional type check? ( Ignorable by Annotation )

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

68
                        /** @scrutinizer ignore-type */ $file,
Loading history...
69
                        $filename
70
                    );
71
                    Session::flash('message', 'File upload successfully!');
72
                    // Redirect to index
73
                } else {
74
                    Session::flash('message', 'File too large. File must be less than 20MB.');
75
                }
76
            } else {
77
                Session::flash('message', 'Invalid File Extension.');
78
            }
79
        }
80
        return redirect()->action('ExaminationStudentsController@index');
81
    }
82
83
    /**
84
     * Import students data to the Examinations table 
85
     *
86
     * @return void
87
     */
88
    public static function callOnClick()
89
    {
90
        // Import CSV to Database
91
        $excelFile = "/examination/exams_students.csv";
92
93
        $import = new ExaminationStudentsImport();
94
        try {
95
            $import->import($excelFile, 'local', \Maatwebsite\Excel\Excel::CSV);
96
            if ($import->failures()->count() > 0) {
97
                $errors = $import->failures();
98
                $columns =  [
99
                    'remarks',
100
                    'st_no',
101
                    'stu_no',
102
                    "f_name",
103
                    "medium",
104
                    "gender",
105
                    "b_date",
106
                    "a_income",
107
                    "schoolid",
108
                    "spl_need",
109
                    "pvt_address",
110
                    "disability_type",
111
                    "disability",
112
                    "sp_center"
113
                ];
114
115
                $file = 'examination/errors.csv';
116
                Storage::put($file, implode(',', $columns));
117
118
                foreach ($errors as $error) {
119
                    Storage::append($file, implode(':', $error->errors()) . ',' . implode(',', $error->values()));
120
                }
121
            }
122
        } catch (\Maatwebsite\Excel\Validators\ValidationException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
123
        }
124
    }
125
126
    /**
127
     * Iterate over existing student's data
128
     *
129
     * @return void
130
     */
131
    public  function doMatch()
132
    {
133
        $students = Examination_student::get()->toArray();
134
135
        //    array_walk($students,array($this,'clone'));
136
        array_walk($students, array($this, 'clone'));
137
    }
138
139
    /**
140
     * Set Examination values
141
     *
142
     * @param array $students
143
     * @return array
144
     */
145
    public function setIsTakingExam($students)
146
    {
147
        $students['taking_g5_exam'] = false;
148
        $students['taking_ol_exam'] = false;
149
        $students['taking_al_exam'] = false;
150
        switch ($this->education_grade->code) {
0 ignored issues
show
Bug introduced by
The property code does not seem to exist on App\Models\Education_grade. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
151
            case 'G5':
152
                $students['taking_g5_exam'] = true;
153
                break;
154
            case 'G4':
155
                $students['taking_g5_exam'] = true;
156
                break;
157
            case 'G10':
158
                $students['taking_ol_exam'] = true;
159
                break;
160
            case 'G11':
161
                $students['taking_ol_exam'] = true;
162
                break;
163
            case preg_match('13', $this->education_grade->code):
164
                $students['taking_al_exam'] = true;
165
                break;
166
        }
167
        return $students;
168
    }
169
170
171
    /**
172
     * Main function to merge the student's to SIS
173
     *
174
     * @param array $student
175
     * @return void
176
     */
177
    public function clone($student)
178
    {
179
        //get student matching with same dob and gender
180
        $matchedStudent = $this->getMatchingStudents($student);
181
182
        // if the first match missing do complete insertion
183
        $institution = Institution::where('code', '=', $student['schoolid'])->first();
184
185
        if (!is_null($institution)) {
186
187
            // ge the class lists to belong the school
188
            $institutionClass = Institution_class::where(
189
                [
190
                    'institution_id' => $institution->id,
191
                    'academic_period_id' => $this->academic_period->id,
192
                    'education_grade_id' => $this->education_grade->id
193
                ]
194
            )->join('institution_class_grades', 'institution_classes.id', 'institution_class_grades.institution_class_id')->get()->toArray();
195
196
            // set search variables 
197
            $admissionInfo = [
198
                'instituion_class' => $institutionClass,
199
                'instituion' => $institution,
200
                'education_grade' =>  $this->education_grade,
201
                'academic_period' => $this->academic_period
202
            ];
203
204
            // if no matching found
205
            if (empty($matchedStudent)) {
206
                $sis_student = $this->student->insertExaminationStudent($student);
207
208
                //TODO implement insert student to admission table
209
                $student['id'] = $sis_student['id'];
210
                $sis_student['student_id'] =  $student['id'];
211
212
                $student = $this->setIsTakingExam($student);
213
                if (count($institutionClass) == 1) {
214
                    $admissionInfo['instituion_class'] = $institutionClass[0];
215
                    Institution_student::createExaminationData($student, $admissionInfo);
216
                    Institution_student_admission::createExaminationData($student, $admissionInfo);
217
                    Institution_class_student::createExaminationData($student, $admissionInfo);
218
                } else {
219
                    Institution_student_admission::createExaminationData($student, $admissionInfo);
220
                    Institution_student::createExaminationData($student, $admissionInfo);
221
                }
222
                $this->updateStudentId($student, $sis_student);
223
                // update the matched student's data    
224
            } else {
225
                $studentData = $this->student->updateExaminationStudent($student, $matchedStudent);
226
                $matchedStudent = array_merge((array) $student, $matchedStudent);
227
                $studentData = array_merge((array) $matchedStudent, $studentData);
228
                Institution_student::updateExaminationData($studentData, $admissionInfo);
229
                $this->updateStudentId($student, $studentData);
230
            }
231
        }
232
    }
233
234
    /**
235
     * This function is implemented similar_text search algorithm 
236
     * to get the most matching name with the existing students
237
     * data set
238
     *
239
     * @param array $student
240
     * @return array
241
     */
242
    public function getMatchingStudents($student)
243
    {
244
        $sis_users = $this->student->getMatches($student);
245
        $studentData = [];
246
247
        // if the same gender same DOE has more than one 
248
        if (!is_null($sis_users) && (count($sis_users) > 1)) {
0 ignored issues
show
introduced by
The condition is_null($sis_users) is always false.
Loading history...
249
            $studentData = $this->searchSimilarName($student, $sis_users);
250
        }else{
251
            $studentData = $sis_users;
252
        }
253
        return $studentData;
254
    }
255
256
    /**
257
     * Search most matching name
258
     *
259
     * @param array $student
260
     * @param array $sis_students
261
     * @return array
262
     */
263
    public function searchSimilarName($student, $sis_students)
264
    {
265
        $highest = [];
266
        $matchedData = [];
267
        $highestDistance = null;
268
        foreach ($sis_students as $key => $value) {
269
            //search name with full name
270
            similar_text(strtoupper($student['f_name']), (strtoupper($value['first_name'])), $percentage);
271
            $distance = levenshtein(strtoupper($student['f_name']), strtoupper($value['first_name']));
272
            $value['rate'] = $percentage;
273
            switch (true) {
274
                case $value['rate'] == 100;
275
                    $highest = $value;
276
                    break;
277
                case (($distance <= 2) && ($distance < $highestDistance));
278
                    $highest = $value;
279
                    $highestDistance = $distance;
280
            }
281
        }
282
283
        if (empty($highest)) {
284
            foreach ($sis_students as $key => $value) {
285
                //search name with last name
286
                similar_text(get_l_name(strtoupper($student['f_name'])), get_l_name(strtoupper($value['first_name'])), $percentage);
287
                $distance = levenshtein(get_l_name(strtoupper($student['f_name'])), get_l_name(strtoupper($value['first_name'])));
0 ignored issues
show
Unused Code introduced by
The assignment to $distance is dead and can be removed.
Loading history...
288
                $value['rate'] = $percentage;
289
                switch (true) {
290
                    case $value['rate'] == 100;
291
                        $matchedData[] = $value;
292
                        $highest = $value;
293
                        break;
294
                }
295
            }
296
        }
297
298
        return $highest;
299
    }
300
301
    /**
302
     * Generate new NSID for students
303
     *
304
     * @param array $student
305
     * @param array $sis_student
306
     * @return void
307
     */
308
    public function updateStudentId($student, $sis_student)
309
    {
310
        try {
311
            $student['nsid'] =  $sis_student['openemis_no'];
312
            // add new NSID to the examinations data set
313
            $this->examination_student->where(['st_no' => $student['st_no']])->update($student);
314
            $this->output->writeln('Updated ' . $sis_student['student_id'] . ' to NSID' . $sis_student['openemis_no']);
315
        } catch (\Exception $th) {
316
            Log::error($th);
317
        }
318
    }
319
320
    /**
321
     * export the all data with NSID
322
     *
323
     * @return void
324
     */
325
    public function export()
326
    {
327
        $adminUser = Security_user::where('username', 'admin')->first();
328
        try {
329
            (new ExaminationStudentsExport)->store('examination/student_data_with_nsid.csv');
330
            (new ExportReady($adminUser));
331
        } catch (\Throwable $th) {
332
            //throw $th;
333
            dd($th);
334
        }
335
        return back()->withSuccess('Export started!');
0 ignored issues
show
Bug Best Practice introduced by
The expression return back()->withSuccess('Export started!') also could return the type Illuminate\Http\RedirectResponse which is incompatible with the documented return type void.
Loading history...
336
    }
337
338
    public function downloadErrors()
339
    {
340
341
        $file_path = storage_path() . '/app/examination/errors.csv';
342
        return Response::download($file_path);
343
    }
344
345
    public function downloadProcessedFile()
346
    {
347
        $file_path = storage_path() . '/app/examination/student_data_with_nsid.csv';
348
        return Response::download($file_path);
349
    }
350
}
351