Passed
Push — feature/timeline ( d18b1c...ddfcdc )
by Yonathan
06:15 queued 11s
created

JobController::downloadApplicants()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 51
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 32
c 3
b 0
f 0
dl 0
loc 51
rs 9.0968
cc 5
nc 8
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace App\Http\Controllers;
4
5
use Illuminate\Support\Facades\Lang;
6
use Illuminate\Support\Facades\Auth;
7
use Illuminate\Support\Facades\Log;
8
use Illuminate\Http\Request;
9
use App\Http\Controllers\Controller;
10
use App\Models\JobApplication;
11
use Carbon\Carbon;
12
use App\Models\JobPoster;
13
use App\Models\JobPosterQuestion;
14
use App\Models\Lookup\ApplicationStatus;
15
use App\Models\Lookup\CitizenshipDeclaration;
16
use App\Models\Lookup\JobPosterStatus;
17
use App\Models\Lookup\VeteranStatus;
18
use App\Models\Manager;
19
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
20
use App\Services\Validation\JobPosterValidator;
21
use Facades\App\Services\WhichPortal;
22
use Illuminate\Support\Facades\Response;
23
use Illuminate\Support\Facades\Validator;
24
25
class JobController extends Controller
26
{
27
    /**
28
     * Display a listing of JobPosters.
29
     *
30
     * @return \Illuminate\Http\Response
31
     */
32
    public function index()
33
    {
34
        $now = Carbon::now();
0 ignored issues
show
Unused Code introduced by
The assignment to $now is dead and can be removed.
Loading history...
35
36
        // Find published jobs that are currently open for applications.
37
        // Eager load required relationships: Department, Province, JobTerm.
38
        // Eager load the count of submitted applications, to prevent the relationship
39
        // from being actually loaded and firing off events.
40
        $jobs = JobPoster::where('internal_only', false)
41
            ->where('job_poster_status_id', JobPosterStatus::where('key', 'live')->first()->id)
42
            ->with([
43
                'department',
44
                'province',
45
                'job_term',
46
            ])
47
            ->withCount([
48
                'submitted_applications',
49
            ])
50
            ->get();
51
        return view('applicant/job_index', [
0 ignored issues
show
Bug introduced by
The function view was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

51
        return /** @scrutinizer ignore-call */ view('applicant/job_index', [
Loading history...
52
            'job_index' => Lang::get('applicant/job_index'),
53
            'jobs' => $jobs
54
        ]);
55
    }
56
57
    /**
58
     * Display a listing of a manager's JobPosters.
59
     *
60
     * @return \Illuminate\Http\Response
61
     */
62
    public function managerIndex()
63
    {
64
        $manager = Auth::user()->manager;
65
66
        $jobs = JobPoster::where('manager_id', $manager->id)
67
            ->with('classification')
68
            ->withCount('submitted_applications')
69
            ->get();
70
71
        foreach ($jobs as &$job) {
72
            $chosen_lang = $job->chosen_lang;
73
74
            // Show chosen lang title if current title is empty.
75
            if (empty($job->title)) {
76
                $job->title = $job->getTranslation('title', $chosen_lang);
77
                $job->trans_required = true;
78
            }
79
80
            // Always preview and edit in the chosen language.
81
            $job->preview_link = LaravelLocalization::getLocalizedURL($chosen_lang, route('manager.jobs.show', $job));
0 ignored issues
show
Bug introduced by
The function route was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

81
            $job->preview_link = LaravelLocalization::getLocalizedURL($chosen_lang, /** @scrutinizer ignore-call */ route('manager.jobs.show', $job));
Loading history...
82
            $job->edit_link = LaravelLocalization::getLocalizedURL($chosen_lang, route('manager.jobs.edit', $job));
83
        }
84
85
86
        return view('manager/job_index', [
0 ignored issues
show
Bug introduced by
The function view was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

86
        return /** @scrutinizer ignore-call */ view('manager/job_index', [
Loading history...
87
            // Localization Strings.
88
            'jobs_l10n' => Lang::get('manager/job_index'),
89
            // Data.
90
            'jobs' => $jobs,
91
        ]);
92
    }
93
94
    /**
95
     * Display a listing of a hr advisor's JobPosters.
96
     *
97
     * @return \Illuminate\Http\Response
98
     */
99
    public function hrIndex(Request $request)
100
    {
101
        $hrAdvisor = $request->user()->hr_advisor;
102
        return view('hr_advisor/job_index', [
0 ignored issues
show
Bug introduced by
The function view was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

102
        return /** @scrutinizer ignore-call */ view('hr_advisor/job_index', [
Loading history...
103
            'title' => Lang::get('hr_advisor/job_index.title'),
104
            'hr_advisor_id' => $hrAdvisor->id
105
        ]);
106
    }
107
108
109
110
111
    /**
112
     * Delete a draft Job Poster.
113
     *
114
     * @param  \Illuminate\Http\Request $request   Incoming request object.
115
     * @param  \App\Models\JobPoster    $jobPoster Job Poster object.
116
     * @return \Illuminate\Http\Response
117
     */
118
    public function destroy(Request $request, JobPoster $jobPoster)
119
    {
120
        $jobPoster->delete();
121
    }
122
123
    /**
124
     * Display the specified job poster.
125
     *
126
     * @param  \Illuminate\Http\Request $request   Incoming request object.
127
     * @param  \App\Models\JobPoster    $jobPoster Job Poster object.
128
     * @return \Illuminate\Http\Response
129
     */
130
    public function show(Request $request, JobPoster $jobPoster)
131
    {
132
        $jobPoster->load([
133
            'department',
134
            'criteria.skill.skill_type',
135
            'manager.team_culture',
136
            'manager.work_environment'
137
        ]);
138
139
        $user = Auth::user();
140
141
        // TODO: Improve workplace photos, and reference them in template direction from WorkEnvironment model.
142
        $workplacePhotos = [];
143
        foreach ($jobPoster->manager->work_environment->workplace_photo_captions as $photoCaption) {
144
            $workplacePhotos[] = [
145
                'description' => $photoCaption->description,
146
                'url' => '/images/user.png'
147
            ];
148
        }
149
150
        // TODO: replace route('manager.show',manager.id) in templates with link using slug.
151
        $criteria = [
152
            'essential' => $jobPoster->criteria->filter(
153
                function ($value, $key) {
154
                    return $value->criteria_type->name == 'essential';
155
                }
156
            ),
157
            'asset' => $jobPoster->criteria->filter(
158
                function ($value, $key) {
159
                    return $value->criteria_type->name == 'asset';
160
                }
161
            ),
162
        ];
163
164
        $jobLang = Lang::get('applicant/job_post');
165
166
        $applyButton = [];
167
        if (WhichPortal::isManagerPortal()) {
168
            $applyButton = [
169
                'href' => route('manager.jobs.edit', $jobPoster->id),
0 ignored issues
show
Bug introduced by
The function route was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

169
                'href' => /** @scrutinizer ignore-call */ route('manager.jobs.edit', $jobPoster->id),
Loading history...
170
                'title' => $jobLang['apply']['edit_link_title'],
171
                'text' => $jobLang['apply']['edit_link_label'],
172
            ];
173
        } elseif (WhichPortal::isHrPortal()) {
174
            if ($jobPoster->hr_advisors->contains('user_id', $user->id)) {
175
                $applyButton = [
176
                    'href' => route('hr_advisor.jobs.summary', $jobPoster->id),
177
                    'title' => null,
178
                    'text' => Lang::get('hr_advisor/job_summary.summary_title'),
179
                ];
180
            } else {
181
                $applyButton = [
182
                    'href' => route('hr_advisor.jobs.index'),
183
                    'title' => null,
184
                    'text' => Lang::get('hr_advisor/job_index.title'),
185
                ];
186
            }
187
        } elseif (Auth::check() && $jobPoster->isOpen()) {
188
            $application = JobApplication::where('applicant_id', Auth::user()->applicant->id)
189
                ->where('job_poster_id', $jobPoster->id)->first();
190
            // If applicants job application is not draft anymore then link to application preview page.
191
            if ($application != null && $application->application_status->name != 'draft') {
192
                $applyButton = [
193
                    'href' => route('applications.show', $application->id),
194
                    'title' => $jobLang['apply']['view_link_title'],
195
                    'text' => $jobLang['apply']['view_link_label'],
196
                ];
197
            } else {
198
                $applyButton = [
199
                    'href' => route('job.application.edit.1', $jobPoster->id),
200
                    'title' => $jobLang['apply']['apply_link_title'],
201
                    'text' => $jobLang['apply']['apply_link_label'],
202
                ];
203
            }
204
        } elseif (Auth::guest() && $jobPoster->isOpen()) {
205
            $applyButton = [
206
                'href' => route('job.application.edit.1', $jobPoster->id),
207
                'title' => $jobLang['apply']['login_link_title'],
208
                'text' => $jobLang['apply']['login_link_label'],
209
            ];
210
        } else {
211
            $applyButton = [
212
                'href' => null,
213
                'title' => null,
214
                'text' => $jobLang['apply']['job_closed_label'],
215
            ];
216
        }
217
218
        $jpb_release_date = strtotime('2019-08-21 16:18:17');
219
        $job_created_at = strtotime($jobPoster->created_at);
220
221
        // If the job poster is created after the release of the JPB.
222
        // Then, render with updated poster template.
223
        // Else, render with old poster template.
224
        if ($job_created_at > $jpb_release_date) {
225
            // Updated job poster (JPB).
226
            return view(
0 ignored issues
show
Bug introduced by
The function view was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

226
            return /** @scrutinizer ignore-call */ view(
Loading history...
227
                'applicant/jpb_job_post',
228
                [
229
                    'job_post' => $jobLang,
230
                    'frequencies' => Lang::get('common/lookup/frequency'),
231
                    'skill_template' => Lang::get('common/skills'),
232
                    'job' => $jobPoster,
233
                    'manager' => $jobPoster->manager,
234
                    'criteria' => $criteria,
235
                    'apply_button' => $applyButton,
236
                ]
237
            );
238
        } else {
239
            // Old job poster.
240
            return view(
241
                'applicant/job_post',
242
                [
243
                    'job_post' => $jobLang,
244
                    'frequencies' => Lang::get('common/lookup/frequency'),
245
                    'manager' => $jobPoster->manager,
246
                    'manager_profile_photo_url' => '/images/user.png', // TODO get real photo.
247
                    'team_culture' => $jobPoster->manager->team_culture,
248
                    'work_environment' => $jobPoster->manager->work_environment,
249
                    'workplace_photos' => $workplacePhotos,
250
                    'job' => $jobPoster,
251
                    'criteria' => $criteria,
252
                    'apply_button' => $applyButton,
253
                    'skill_template' => Lang::get('common/skills'),
254
                ]
255
            );
256
        }
257
    }
258
259
    /**
260
     * Display the form for editing an existing Job Poster
261
     * Only allows editing fields that don't appear on the react-built Job Poster Builder.
262
     *
263
     * @param  \Illuminate\Http\Request $request   Incoming request object.
264
     * @param  \App\Models\JobPoster    $jobPoster Job Poster object.
265
     * @return \Illuminate\Http\Response
266
     */
267
    public function edit(Request $request, JobPoster $jobPoster)
268
    {
269
        $manager = $jobPoster->manager;
270
271
        if ($jobPoster->job_poster_questions === null || $jobPoster->job_poster_questions->count() === 0) {
272
            $jobPoster->job_poster_questions()->saveMany($this->populateDefaultQuestions());
273
            $jobPoster->refresh();
274
        }
275
276
        return view(
0 ignored issues
show
Bug introduced by
The function view was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

276
        return /** @scrutinizer ignore-call */ view(
Loading history...
277
            'manager/job_create',
278
            [
279
                // Localization Strings.
280
                'job_l10n' => Lang::get('manager/job_edit'),
281
                // Data.
282
                'manager' => $manager,
283
                'job' => $jobPoster,
284
                'form_action_url' => route('admin.jobs.update', $jobPoster),
0 ignored issues
show
Bug introduced by
The function route was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

284
                'form_action_url' => /** @scrutinizer ignore-call */ route('admin.jobs.update', $jobPoster),
Loading history...
285
            ]
286
        );
287
    }
288
289
    /**
290
     * Create a blank job poster for the specified manager
291
     *
292
     * @param  \App\Models\Manager $manager Incoming Manager object.
293
     * @return \Illuminate\Http\Response Job Create view
294
     */
295
    public function createAsManager(Manager $manager)
296
    {
297
        $jobPoster = new JobPoster();
298
        $jobPoster->manager_id = $manager->id;
299
300
        // Save manager-specific info to the job poster - equivalent to the intro step of the JPB
301
        $divisionEn = $manager->getTranslation('division', 'en');
302
        $divisionFr = $manager->getTranslation('division', 'fr');
303
        $jobPoster->fill([
304
            'department_id' => $manager->user->department_id,
305
            'division' => ['en' => $divisionEn],
306
            'division' => ['fr' => $divisionFr],
307
        ]);
308
309
        $jobPoster->save();
310
311
        return redirect()->route('manager.jobs.edit', $jobPoster->id);
0 ignored issues
show
Bug introduced by
The function redirect was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

311
        return /** @scrutinizer ignore-call */ redirect()->route('manager.jobs.edit', $jobPoster->id);
Loading history...
312
    }
313
314
    /**
315
     * Update a resource in storage
316
     * NOTE: Only saves fields that are not on the react-built Job Poster Builder
317
     *
318
     * @param  \Illuminate\Http\Request $request   Incoming request object.
319
     * @param  \App\Models\JobPoster    $jobPoster Optional Job Poster object.
320
     * @return \Illuminate\Http\Response
321
     */
322
    public function store(Request $request, JobPoster $jobPoster)
323
    {
324
        // Don't allow edits for published Job Posters
325
        // Also check auth while we're at it.
326
        $this->authorize('update', $jobPoster);
327
        JobPosterValidator::validateUnpublished($jobPoster);
328
329
        $input = $request->input();
330
331
        if ($jobPoster->manager_id == null) {
332
            $jobPoster->manager_id = $request->user()->manager->id;
333
            $jobPoster->save();
334
        }
335
336
        if ($request->input('question')) {
337
            $validator = Validator::make($request->input('question'), [
338
                '*.question.*' => 'required|string',
339
            ], [
340
                'required' => Lang::get('validation.custom.job_poster_question.required'),
341
                'string' => Lang::get('validation.custom.job_poster_question.string')
342
            ]);
343
344
            if ($validator->fails()) {
345
                $request->session()->flash('errors', $validator->errors());
346
                return redirect(route('admin.jobs.edit', $jobPoster->id));
0 ignored issues
show
Bug introduced by
The function redirect was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

346
                return /** @scrutinizer ignore-call */ redirect(route('admin.jobs.edit', $jobPoster->id));
Loading history...
Bug introduced by
The function route was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

346
                return redirect(/** @scrutinizer ignore-call */ route('admin.jobs.edit', $jobPoster->id));
Loading history...
347
            }
348
        }
349
350
        $this->fillAndSaveJobPosterQuestions($input, $jobPoster, true);
351
352
        return redirect(route('manager.jobs.show', $jobPoster->id));
353
    }
354
355
    /**
356
     * Fill Job Poster's questions and save
357
     *
358
     * @param  mixed[]               $input     Field values.
359
     * @param  \App\Models\JobPoster $jobPoster Job Poster object.
360
     * @param  boolean               $replace   Remove existing relationships.
361
     * @return void
362
     */
363
    protected function fillAndSaveJobPosterQuestions(array $input, JobPoster $jobPoster, bool $replace): void
364
    {
365
        if ($replace) {
366
            $jobPoster->job_poster_questions()->delete();
367
        }
368
369
        if (!array_key_exists('question', $input) || !is_array($input['question'])) {
370
            return;
371
        }
372
373
        foreach ($input['question'] as $question) {
374
            $jobQuestion = new JobPosterQuestion();
375
            $jobQuestion->job_poster_id = $jobPoster->id;
376
            $jobQuestion->fill(
377
                [
378
                    'question' => [
379
                        'en' => $question['question']['en'],
380
                        'fr' => $question['question']['fr']
381
382
                    ],
383
                    'description' => [
384
                        'en' => $question['description']['en'],
385
                        'fr' => $question['description']['fr']
386
                    ]
387
                ]
388
            );
389
            $jobPoster->save();
390
            $jobQuestion->save();
391
        }
392
    }
393
394
    /**
395
     * Get the localized default questions and add them to an array.
396
     *
397
     * @return mixed[]|void
398
     */
399
    protected function populateDefaultQuestions()
400
    {
401
        $defaultQuestions = [
402
            'en' => array_values(Lang::get('manager/job_create', [], 'en')['questions']),
403
            'fr' => array_values(Lang::get('manager/job_create', [], 'fr')['questions']),
404
        ];
405
406
        if (count($defaultQuestions['en']) !== count($defaultQuestions['fr'])) {
407
            Log::warning('There must be the same number of French and English default questions for a Job Poster.');
408
            return;
409
        }
410
411
        $jobQuestions = [];
412
413
        for ($i = 0; $i < count($defaultQuestions['en']); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
414
            $jobQuestion = new JobPosterQuestion();
415
            $jobQuestion->fill(
416
                [
417
                    'question' => [
418
                        'en' => $defaultQuestions['en'][$i],
419
                        'fr' => $defaultQuestions['fr'][$i],
420
                    ]
421
                ]
422
            );
423
            $jobQuestions[] = $jobQuestion;
424
        }
425
426
        return $jobQuestions;
427
    }
428
429
    /**
430
     * Downloads a CSV file with the applicants who have applied to the job poster.
431
     *
432
     * @param  \App\Models\JobPoster $jobPoster Job Poster object.
433
     * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
434
     */
435
    protected function downloadApplicants(JobPoster $jobPoster)
436
    {
437
        $tables = [];
438
        // The first row in the array represents the names of the columns in the spreadsheet.
439
        $tables[0] = ['Status', 'Applicant Name', 'Email', 'Language'];
440
441
        $application_status_id = ApplicationStatus::where('name', 'submitted')->first()->id;
442
        $applications = JobApplication::where('job_poster_id', $jobPoster->id)
443
            ->where('application_status_id', $application_status_id)
444
            ->get();
445
446
        $index = 1;
447
        foreach ($applications as $application) {
448
            $status = '';
449
            $username = $application->user_name;
450
            $user_email = $application->user_email;
451
            $language = strtoupper($application->preferred_language->name);
452
            // If the applicants veteran status name is NOT 'none' then set status to veteran.
453
            $non_veteran = VeteranStatus::where('name', 'none')->first()->id;
454
            if ($application->veteran_status_id != $non_veteran) {
455
                $status = 'Veteran';
456
            } else {
457
                // Check if the applicant is a canadian citizen.
458
                $canadian_citizen = CitizenshipDeclaration::where('name', 'citizen')->first()->id;
459
                if ($application->citizenship_declaration->id == $canadian_citizen) {
460
                    $status = 'Citizen';
461
                } else {
462
                    $status = 'Non-citizen';
463
                }
464
            }
465
            $tables[$index] = [$status, $username, $user_email, $language];
466
            $index++;
467
        }
468
469
        $filename = $jobPoster->id . '-' . 'applicants-data.csv';
470
471
        // Open file.
472
        $file = fopen($filename, 'w');
473
        // Iterate through tables and add each line to csv file.
474
        foreach ($tables as $line) {
475
            fputcsv($file, $line);
0 ignored issues
show
Bug introduced by
It seems like $file can also be of type false; however, parameter $handle of fputcsv() does only seem to accept resource, 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

475
            fputcsv(/** @scrutinizer ignore-type */ $file, $line);
Loading history...
476
        }
477
        // Close open file.
478
        fclose($file);
0 ignored issues
show
Bug introduced by
It seems like $file can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, 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

478
        fclose(/** @scrutinizer ignore-type */ $file);
Loading history...
479
480
        $headers = [
481
            'Content-Type' => 'text/csv',
482
            'Content-Disposition' => 'attachment; filename=' . $filename,
483
        ];
484
485
        return Response::download($filename, $filename, $headers);
486
    }
487
}
488