Test Setup Failed
Push — master ( 72885b...568721 )
by Mohamed
02:18 queued 11s
created

ExaminationStudentsController   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 454
Duplicated Lines 0 %

Importance

Changes 29
Bugs 11 Features 4
Metric Value
eloc 264
dl 0
loc 454
rs 8.48
c 29
b 11
f 4
wmc 49

14 Methods

Rating   Name   Duplication   Size   Complexity  
A index() 0 3 1
A __construct() 0 9 1
A uploadFile() 0 40 4
A callOnClick() 0 35 4
B doMatch() 0 63 5
A clone() 0 60 4
A setIsTakingExam() 0 23 5
B updateCensusNo() 0 29 6
A export() 0 11 2
A getMatchingStudents() 0 22 3
A updateStudentId() 0 18 2
B searchSimilarName() 0 41 10
A downloadErrors() 0 5 1
A downloadProcessedFile() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like ExaminationStudentsController 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 ExaminationStudentsController, and based on these observations, apply Extract Interface, too.

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\Models\Institution_shift;
13
use App\Notifications\ExportReady;
14
use Illuminate\Support\Facades\DB;
15
use App\Models\Examination_student;
16
use App\Models\Institution_student;
17
use Illuminate\Support\Facades\Log;
18
use Illuminate\Support\Facades\Storage;
19
use Illuminate\Support\Facades\Response;
20
use App\Models\Institution_class_student;
21
use App\Exports\ExaminationStudentsExport;
22
use App\Imports\ExaminationStudentsImport;
23
use App\Models\Institution_student_admission;
24
25
class ExaminationStudentsController extends Controller
26
{
27
    public function __construct($year = 2019, $grade = 'G5')
28
    {
29
        $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...
30
        $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...
31
        $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...
32
        $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...
33
        $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...
34
        $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...
35
        $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...
36
    }
37
38
    public function index()
39
    {
40
        return view('uploadcsv');
41
    }
42
43
    public function uploadFile(Request $request)
44
    {
45
46
        if ($request->input('submit') != null) {
47
48
            $file = $request->file('file');
49
50
            // File Details
51
            $filename = 'exams_students.csv';
52
            $extension = $file->getClientOriginalExtension();
53
            $fileSize = $file->getSize();
54
55
            // Valid File Extensions
56
            $valid_extension = array("csv");
57
58
            // 20MB in Bytes
59
            $maxFileSize = 30971520;
60
61
            // Check file extension
62
            if (in_array(strtolower($extension), $valid_extension)) {
63
64
                // Check file size
65
                if ($fileSize <= $maxFileSize) {
66
67
                    // File upload location
68
                    Storage::disk('local')->putFileAs(
69
                        'examination/',
70
                        $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

70
                        /** @scrutinizer ignore-type */ $file,
Loading history...
71
                        $filename
72
                    );
73
                    Session::flash('message', 'File upload successfully!');
74
                    // Redirect to index
75
                } else {
76
                    Session::flash('message', 'File too large. File must be less than 20MB.');
77
                }
78
            } else {
79
                Session::flash('message', 'Invalid File Extension.');
80
            }
81
        }
82
        return redirect()->action('ExaminationStudentsController@index');
83
    }
84
85
    /**
86
     * Import students data to the Examinations table 
87
     *
88
     * @return void
89
     */
90
    public static function callOnClick($year, $grade)
91
    {
92
        // Import CSV to Database
93
        $excelFile = "/examination/exams_students.csv";
94
95
        $import = new ExaminationStudentsImport($year, $grade);
96
        try {
97
            $import->import($excelFile, 'local', \Maatwebsite\Excel\Excel::CSV);
98
            if ($import->failures()->count() > 0) {
99
                $errors = $import->failures();
100
                $columns =  [
101
                    'remarks',
102
                    'st_no',
103
                    'stu_no',
104
                    "f_name",
105
                    "medium",
106
                    "gender",
107
                    "b_date",
108
                    "a_income",
109
                    "schoolid",
110
                    "spl_need",
111
                    "pvt_address",
112
                    "disability_type",
113
                    "disability",
114
                    "sp_center"
115
                ];
116
117
                $file = 'examination/errors.csv';
118
                Storage::put($file, implode(',', $columns));
119
120
                foreach ($errors as $error) {
121
                    Storage::append($file, implode(':', $error->errors()) . ',' . implode(',', $error->values()));
122
                }
123
            }
124
        } 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...
125
        }
126
    }
127
128
    /**
129
     * updated wrong census
130
     *
131
     * @param [type] $data
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
132
     * @return void
133
     */
134
    public function updateCensusNo($data)
135
    {
136
        $output = new \Symfony\Component\Console\Output\ConsoleOutput();
137
        $student = Security_user::where('openemis_no', $data['nsid'])
138
            ->select('security_users.id')
139
            ->first();
140
        if (!is_null($student)) {
141
            $student = Institution_student::where('student_id', $student['id'])->get()->toArray();
142
            $Institution = Institution::where('code', $data['schoolid'])->get()->toArray();
143
            if (!empty($Institution)) {
144
                $Institution = $Institution[0];
145
                if (count($student) == 1) {
146
                    $student = $student[0];
147
                    if (((int)$Institution['id']) !=  ((int)$student['institution_id'])) {
148
                        $studentClass = Institution_class_student::where('student_id', $student['student_id'])
149
                            ->first();
150
                        Institution_class_student::where('student_id', $student['student_id'])->delete();
151
                        Institution_student::where('student_id', $student['student_id'])
152
                            ->update(['institution_id' =>  $Institution['id']]);
153
                        $class = new Institution_class();
154
                        if (!is_null($studentClass)) {
155
                            $class->updateClassCount($studentClass->toArray());
156
                        }
157
                        $output->writeln('updated student info:' . $data['nsid']);
158
                    }
159
                } else {
160
                    Institution_student::where('institution_id', '<>', $Institution['id'])->where('student_id', $student[0]['student_id'])
161
                        ->delete();
162
                    $output->writeln('updated student info:' . $Institution['id'] . '==' . $Institution['id']);
163
                }
164
            }
165
        }
166
    }
167
168
    /**
169
     * Iterate over existing student's data
170
     *
171
     * @return void
172
     */
173
    public  function doMatch($offset, $limit, $mode)
174
    {
175
        $students = [];
176
        switch ($mode) {
177
            case 'duplicate':
178
                $students =  DB::table('examination_students as es')
179
                    ->select(DB::raw('count(*) as total'), 'e2.*')
180
                    ->where('grade', $this->grade)
181
                    ->where('year', $this->year)
182
                    ->join('examination_students as e2', 'es.nsid', 'e2.nsid')
183
                    ->having('total', '>', 1)
184
                    ->groupBy('e2.st_no')
185
                    ->orderBy('e2.st_no')
186
                    ->offset($offset)
187
                    ->limit($limit)
188
                    ->get()->toArray();
189
                $students = (array) json_decode(json_encode($students));
190
                $this->output->writeln(count($students) . 'students remaining duplicate');
191
                array_walk($students, array($this, 'clone'));
192
                $this->output->writeln('All are generated');
193
                break;
194
            case 'empty';
195
                $students = Examination_student::whereNull('nsid')
196
                    ->orWhere('nsid', '<>', '')
197
                    ->where('grade', $this->grade)
198
                    ->where('year', $this->year)
199
                    ->offset($offset)
200
                    ->limit($limit)
201
                    ->get()->toArray();
202
                $students = (array) json_decode(json_encode($students));
203
                $this->output->writeln(count($students) . 'students remaining empty');
204
                array_walk($students, array($this, 'clone'));
205
                $this->output->writeln('All are generated');
206
                break;
207
            case 'invalid';
208
                $students = Examination_student::whereRaw('CHAR_LENGTH(nsid) > 11')
209
                    ->where('grade', $this->grade)
210
                    ->where('year', $this->year)
211
                    ->get()->toArray();
212
                $students = (array) json_decode(json_encode($students));
213
                $this->output->writeln(count($students) . 'students remaining with wrong NSID');
214
                array_walk($students, array($this, 'clone'));
215
                $this->output->writeln('All are generated');
216
                break;
217
            case 'count':
218
                $count = Examination_student::distinct('nsid')
219
                    ->where('grade', $this->grade)
220
                    ->where('year', $this->year)
221
                    ->count();
222
                $all = Examination_student::select('nsid')
223
                    ->count();
224
                $this->output->writeln($all . 'Total Unique nsid are: ' . $count);
225
                break;
226
            default:
227
                $students = Examination_student::offset($offset)
228
                    ->where('grade', $this->grade)
229
                    ->where('year', $this->year)
230
                    ->limit($limit)
231
                    ->get()->toArray();
232
                $students = (array) json_decode(json_encode($students));
233
                $this->output->writeln(count($students) . 'students remaining empty');
234
                array_walk($students, array($this, 'clone'));
235
                $this->output->writeln('All are generated');
236
        }
237
    }
238
239
    /**
240
     * Set Examination values
241
     *
242
     * @param array $students
243
     * @return array
244
     */
245
    public function setIsTakingExam($students)
246
    {
247
        $students['taking_g5_exam'] = false;
248
        $students['taking_ol_exam'] = false;
249
        $students['taking_al_exam'] = false;
250
        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...
251
            case 'G5':
252
                $students['taking_g5_exam'] = true;
253
                break;
254
            case 'G4':
255
                $students['taking_g5_exam'] = true;
256
                break;
257
            case 'G10':
258
                $students['taking_ol_exam'] = true;
259
                break;
260
            case 'G11':
261
                $students['taking_ol_exam'] = true;
262
                break;
263
                // case preg_match('13', $this->education_grade->code):
264
                //     $students['taking_al_exam'] = true;
265
                //     break;
266
        }
267
        return $students;
268
    }
269
270
271
    /**
272
     * Main function to merge the student's to SIS
273
     *
274
     * @param array $student
275
     * @return void
276
     */
277
    public function clone($student)
278
    {
279
        $student = (array)json_decode(json_encode($student));
280
        //get student matching with same dob and gender
281
282
        $matchedStudent = $this->getMatchingStudents($student);
283
284
        // if the first match missing do complete insertion
285
        $institution = Institution::where('code', '=', (int)$student['schoolid'])->first();
286
287
        if (!is_null($institution)) {
288
289
            // ge the class lists to belong the school
290
            $institutionClass = Institution_class::where(
291
                [
292
                    'institution_id' => $institution->id,
293
                    'academic_period_id' => $this->academic_period->id,
294
                    'education_grade_id' => $this->education_grade->id
295
                ]
296
            )->join('institution_class_grades', 'institution_classes.id', 'institution_class_grades.institution_class_id')->get()->toArray();
297
298
            // set search variables 
299
            $admissionInfo = [
300
                'instituion_class' => $institutionClass,
301
                'instituion' => $institution,
302
                'education_grade' =>  $this->education_grade,
303
                'academic_period' => $this->academic_period
304
            ];
305
306
            // if no matching found
307
            if (empty($matchedStudent)) {
308
                $sis_student = $this->student->insertExaminationStudent($student);
309
310
                //TODO implement insert student to admission table
311
                $student['id'] = $sis_student['id'];
312
                $sis_student['student_id'] =  $student['id'];
313
314
                $student = $this->setIsTakingExam($student);
315
                if (count($institutionClass) == 1) {
316
                    $admissionInfo['instituion_class'] = $institutionClass[0];
317
                    Institution_student::createExaminationData($student, $admissionInfo);
318
                    Institution_student_admission::createExaminationData($student, $admissionInfo);
319
                    Institution_class_student::createExaminationData($student, $admissionInfo);
320
                } else {
321
                    Institution_student_admission::createExaminationData($student, $admissionInfo);
322
                    Institution_student::createExaminationData($student, $admissionInfo);
323
                }
324
                $this->updateStudentId($student, $sis_student);
325
                // update the matched student's data    
326
            } else {
327
                $student = $this->setIsTakingExam($student);
328
                $studentData = $this->student->updateExaminationStudent($student, $matchedStudent);
329
                $matchedStudent = array_merge((array) $student, $matchedStudent);
330
                $studentData = array_merge((array) $matchedStudent, $studentData);
331
                Institution_student::updateExaminationData($studentData, $admissionInfo);
332
                $this->updateStudentId($student, $studentData);
333
            }
334
        } else {
335
336
            $this->output->writeln('Student ' . $student['st_no'] . ' not imorted' . $student['f_name']);
337
        }
338
    }
339
340
    /**
341
     * This function is implemented similar_text search algorithm 
342
     * to get the most matching name with the existing students
343
     * data set
344
     *
345
     * @param array $student
346
     * @return array
347
     */
348
    public function getMatchingStudents($student)
349
    {
350
        /**
351
         */
352
        $sis_student = $this->student->getMatches($student);
353
        $doe_students =  Examination_student::where('gender', $student['gender'])
354
            ->where('b_date', $student['b_date'])
355
            ->where('schoolid', $student['schoolid'])
356
            ->where('year',$this->year)
357
            ->where('grade',$this->grade)
358
            ->count();
359
        $count = $this->student->getStudentCount($student);
360
361
        $studentData = [];
362
        $sis_users  = (array) json_decode(json_encode($sis_student), true);
363
        // if the same gender same DOE has more than one 
364
        if (($doe_students > 1) || ($count > 1)) {
365
            $studentData = $this->searchSimilarName($student, $sis_users, false);
366
        } else {
367
            $studentData = $this->searchSimilarName($student, $sis_users);
368
        }
369
        return $studentData;
370
    }
371
372
    /**
373
     * Search most matching name
374
     *
375
     * @param array $student
376
     * @param array $sis_students
377
     * @return array
378
     */
379
    public function searchSimilarName($student, $sis_students, $surname_search = true)
380
    {
381
        $highest = [];
382
        $minDistance = 0;
383
        $matches = [];
384
        $data = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $data is dead and can be removed.
Loading history...
385
        foreach ($sis_students as $key => $value) {
386
            similar_text(strtoupper($value['first_name']), (strtoupper($student['f_name'])), $percentage);
387
            $distance = levenshtein(strtoupper($student['f_name']), strtoupper($value['first_name']));
388
            $value['rate'] = $percentage;
389
            switch (true) {
390
                case $value['rate'] == 100;
391
                    $highest = $value;
392
                    break;
393
                case (($distance <= 2) && ($distance < $minDistance));
394
                    $highest = $value;
395
                    $minDistance = $distance;
396
            }
397
        }
398
399
        if ($surname_search) {
400
            if (empty($highest)) {
401
                foreach ($sis_students as $key => $value) {
402
                    //search name with last name
403
                    similar_text(strtoupper(get_l_name($student['f_name'])), strtoupper(get_l_name($value['first_name'])), $percentage);
404
                    $value['rate'] = $percentage;
405
                    switch (true) {
406
                        case ($value['rate'] == 100);
407
                            $highest = $value;
408
                            $matches[] = $value;
409
                            break;
410
                    }
411
                }
412
            }
413
        }
414
415
        if (count($matches) > 1) {
416
            $highest =  $this->searchSimilarName($student, $sis_students, false);
417
        }
418
419
        return $highest;
420
    }
421
422
    /**
423
     * Generate new NSID for students
424
     *
425
     * @param array $student
426
     * @param array $sis_student
427
     * @return void
428
     */
429
    public function updateStudentId($student, $sis_student)
430
    {
431
        try {
432
            $student['nsid'] =  $sis_student['openemis_no'];
433
            // add new NSID to the examinations data set
434
            unset($student['id']);
435
            unset($student['taking_g5_exam']);
436
            unset($student['taking_al_exam']);
437
            unset($student['taking_ol_exam']);
438
            unset($student['total']);
439
            $students['updated_at'] =  now();
0 ignored issues
show
Comprehensibility Best Practice introduced by
$students was never initialized. Although not strictly required by PHP, it is generally a good practice to add $students = array(); before regardless.
Loading history...
440
            $this->examination_student->where('st_no', $student['st_no'])->update($student);
441
            unset($student['st_no']);
442
            $this->output->writeln('Updated  to NSID' . $sis_student['openemis_no']);
443
        } catch (\Exception $th) {
444
            dd($th);
445
            $this->output->writeln('error');
446
            Log::error($th);
447
        }
448
    }
449
450
    /**
451
     * export the all data with NSID
452
     *
453
     * @return void
454
     */
455
    public function export()
456
    {
457
        $adminUser = Security_user::where('username', 'admin')->first();
458
        try {
459
            (new ExaminationStudentsExport($this->year,$this->grade))->store('examination/student_data_with_nsid.' . time() . '.csv');
460
            (new ExportReady($adminUser));
461
        } catch (\Throwable $th) {
462
            //throw $th;
463
            dd($th);
464
        }
465
        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...
466
    }
467
468
    public function downloadErrors()
469
    {
470
471
        $file_path = storage_path() . '/app/examination/errors.csv';
472
        return Response::download($file_path);
473
    }
474
475
    public function downloadProcessedFile()
476
    {
477
        $file_path = storage_path() . '/app/examination/student_data_with_nsid.csv';
478
        return Response::download($file_path);
479
    }
480
}
481