Passed
Push — feature/strategic-talent-poste... ( 6eb9b3...52ef9e )
by Tristan
04:27
created

JobPoster::job_poster_questions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Created by Reliese Model.
5
 * Date: Thu, 12 Jul 2018 22:39:27 +0000.
6
 */
7
8
namespace App\Models;
9
10
use App\Events\JobSaved;
11
use Backpack\CRUD\app\Models\Traits\CrudTrait;
12
use Illuminate\Notifications\Notifiable;
13
use Illuminate\Support\Facades\App;
14
use Illuminate\Support\Facades\Lang;
15
use Jenssegers\Date\Date;
16
17
use Spatie\Translatable\HasTranslations;
18
19
/**
20
 * Class JobPoster
21
 *
22
 * @property int $id
23
 * @property int $job_term_id
24
 * @property string $chosen_lang
25
 * @property int $term_qty
26
 * @property \Jenssegers\Date\Date $open_date_time
27
 * @property \Jenssegers\Date\Date $close_date_time
28
 * @property \Jenssegers\Date\Date $start_date_time
29
 * @property int $department_id
30
 * @property int $province_id
31
 * @property int $salary_min
32
 * @property int $salary_max
33
 * @property int $noc
34
 * @property int $classification_id
35
 * @property int $classification_level
36
 * @property int $security_clearance_id
37
 * @property int $language_requirement_id
38
 * @property boolean $remote_work_allowed
39
 * @property int $manager_id
40
 * @property boolean $internal_only
41
 * @property int $team_size
42
 * @property array $work_env_features This should be an array of boolean flags for features, ie json of shape {[feature: string]: boolean}
43
 * @property int $fast_vs_steady
44
 * @property int $horizontal_vs_vertical
45
 * @property int $experimental_vs_ongoing
46
 * @property int $citizen_facing_vs_back_office
47
 * @property int $collaborative_vs_independent
48
 * @property int $telework_allowed_frequency_id
49
 * @property int $flexible_hours_frequency_id
50
 * @property int $travel_requirement_id
51
 * @property int $overtime_requirement_id
52
 * @property string $process_number
53
 * @property int $priority_clearance_number
54
 * @property int $job_poster_status_id
55
 * @property \Jenssegers\Date\Date $loo_issuance_date
56
 * @property \Jenssegers\Date\Date $created_at
57
 * @property \Jenssegers\Date\Date $updated_at
58
 *
59
 * @property int $submitted_applications_count
60
 *
61
 * @property \App\Models\Lookup\Department $department
62
 * @property \App\Models\Lookup\JobTerm $job_term
63
 * @property \App\Models\Lookup\LanguageRequirement $language_requirement
64
 * @property \App\Models\Manager $manager
65
 * @property \App\Models\Lookup\Province $province
66
 * @property \App\Models\Lookup\SecurityClearance $security_clearance
67
 * @property \Illuminate\Database\Eloquent\Collection $criteria
68
 * @property \Illuminate\Database\Eloquent\Collection $job_applications
69
 * @property \Illuminate\Database\Eloquent\Collection $job_poster_key_tasks
70
 * @property \Illuminate\Database\Eloquent\Collection $job_poster_questions
71
 * @property \Illuminate\Database\Eloquent\Collection $job_poster_translations
72
 * @property \Illuminate\Database\Eloquent\Collection $submitted_applications
73
 * @property \Illuminate\Database\Eloquent\Collection $hr_advisors
74
 * @property \App\Models\Lookup\Frequency $telework_allowed_frequency
75
 * @property \App\Models\Lookup\Frequency $flexible_hours_frequency
76
 * @property \App\Models\Lookup\JobPosterStatus $job_poster_status
77
 *
78
 * Localized Properties:
79
 * @property string $city
80
 * @property string $title
81
 * @property string $dept_impact
82
 * @property string $team_impact
83
 * @property string $hire_impact
84
 * @property string $division
85
 * @property string $education
86
 * @property string $work_env_description
87
 * @property string $culture_summary
88
 * @property string $culture_special
89
 *
90
 * Methods
91
 * @method boolean isOpen()
92
 * @method boolean isClosed()
93
 * @method boolean isVisibleToHr()
94
 * @method boolean isPublic()
95
 * @method boolean isEditable()
96
 * @method string timeRemaining()
97
 * @method mixed[] toApiArray()
98
 * @method boolean isInStrategicResponseDepartment()
99
 *
100
 * Computed Properties
101
 * @property string|null $classification_code
102
 * @property string|null $classification_message
103
 */
104
class JobPoster extends BaseModel
105
{
106
    use CrudTrait;
107
    use HasTranslations;
108
    use Notifiable;
109
110
    const DATE_FORMAT = [
111
        'en' => 'M jS, Y',
112
        'fr' => 'd M Y',
113
    ];
114
    const TIME_FORMAT = [
115
        'en' => 'h:i A T',
116
        'fr' => 'H \h i T',
117
    ];
118
    const TIMEZONE = 'America/Toronto';
119
120
    /**
121
     * @var string[] $translatable
122
     */
123
    public $translatable = [
124
        'city',
125
        'title',
126
        'dept_impact',
127
        'team_impact',
128
        'hire_impact',
129
        'division',
130
        'education',
131
        'work_env_description',
132
        'culture_summary',
133
        'culture_special',
134
    ];
135
136
    /**
137
     * @var string[] $casts
138
     */
139
    protected $casts = [
140
        'job_term_id' => 'int',
141
        'department_id' => 'int',
142
        'province_id' => 'int',
143
        'salary_min' => 'int',
144
        'salary_max' => 'int',
145
        'noc' => 'int',
146
        'classification_id' => 'int',
147
        'classification_level' => 'int',
148
        'security_clearance_id' => 'int',
149
        'language_requirement_id' => 'int',
150
        'remote_work_allowed' => 'boolean',
151
        'manager_id' => 'int',
152
        'internal_only' => 'boolean',
153
        'team_size' => 'int',
154
        'work_env_features' => 'array',
155
        'fast_vs_steady' => 'int',
156
        'horizontal_vs_vertical' => 'int',
157
        'experimental_vs_ongoing' => 'int',
158
        'citizen_facing_vs_back_office' => 'int',
159
        'collaborative_vs_independent' => 'int',
160
        'telework_allowed_frequency_id' => 'int',
161
        'flexible_hours_frequency_id' => 'int',
162
        'travel_requirement_id' => 'int',
163
        'overtime_requirement_id' => 'int',
164
        'process_number' => 'string',
165
        'priority_clearance_number' => 'int'
166
    ];
167
168
    /**
169
     * @var string[] $dates
170
     */
171
    protected $dates = [
172
        'open_date_time',
173
        'close_date_time',
174
        'start_date_time',
175
        'loo_issuance_date',
176
    ];
177
178
    /**
179
     * @var string[] $fillable
180
     */
181
    protected $fillable = [
182
        'job_term_id',
183
        'chosen_lang',
184
        'term_qty',
185
        'open_date_time',
186
        'close_date_time',
187
        'start_date_time',
188
        'department_id',
189
        'province_id',
190
        'salary_min',
191
        'salary_max',
192
        'noc',
193
        'security_clearance_id',
194
        'language_requirement_id',
195
        'remote_work_allowed',
196
        'internal_only',
197
        'team_size',
198
        'work_env_features',
199
        'fast_vs_steady',
200
        'horizontal_vs_vertical',
201
        'experimental_vs_ongoing',
202
        'citizen_facing_vs_back_office',
203
        'collaborative_vs_independent',
204
        'telework_allowed_frequency_id',
205
        'flexible_hours_frequency_id',
206
        'travel_requirement_id',
207
        'overtime_requirement_id',
208
        'process_number',
209
        'priority_clearance_number',
210
        'loo_issuance_date',
211
        'classification_id',
212
        'classification_level',
213
        'city',
214
        'title',
215
        'dept_impact',
216
        'team_impact',
217
        'hire_impact',
218
        'division',
219
        'education',
220
        'work_env_description',
221
        'culture_summary',
222
        'culture_special',
223
        'job_poster_status_id', // This really shouldn't be mass-editable, but its necesary for the admin crud portal to set it.
224
    ];
225
226
    /**
227
     * The attributes that should be visible in arrays.
228
     * In this case, it blocks loaded relations from appearing.
229
     *
230
     * @var array
231
     */
232
    protected $visible = [
233
        'id',
234
        'manager_id',
235
        'chosen_lang',
236
        'term_qty',
237
        'open_date_time',
238
        'close_date_time',
239
        'start_date_time',
240
        'department_id',
241
        'province_id',
242
        'salary_min',
243
        'salary_max',
244
        'noc',
245
        'security_clearance_id',
246
        'language_requirement_id',
247
        'remote_work_allowed',
248
        'team_size',
249
        'work_env_features',
250
        'fast_vs_steady',
251
        'horizontal_vs_vertical',
252
        'experimental_vs_ongoing',
253
        'citizen_facing_vs_back_office',
254
        'collaborative_vs_independent',
255
        'telework_allowed_frequency_id',
256
        'flexible_hours_frequency_id',
257
        'travel_requirement_id',
258
        'overtime_requirement_id',
259
        'process_number',
260
        'priority_clearance_number',
261
        'loo_issuance_date',
262
        'classification_id',
263
        'classification_level',
264
        'city',
265
        'title',
266
        'dept_impact',
267
        'team_impact',
268
        'hire_impact',
269
        'division',
270
        'education',
271
        'work_env_description',
272
        'culture_summary',
273
        'culture_special',
274
        'job_poster_status_id',
275
        'created_at',
276
    ];
277
278
    /**
279
     * The accessors to append to the model's array form.
280
     *
281
     * @var string[] $appends
282
     */
283
    protected $appends = [
284
        'classification_code',
285
        'classification_message',
286
    ];
287
288
    /**
289
     * Eager loaded relationships by default.
290
     *
291
     * @var string[] $with
292
     */
293
    protected $with = [
294
        'criteria',
295
        'manager'
296
    ];
297
298
    /**
299
     * @var mixed[] $dispatchesEvents
300
     */
301
    protected $dispatchesEvents = [
302
        'saved' => JobSaved::class,
303
    ];
304
305
    // @codeCoverageIgnoreStart
306
    public function department() // phpcs:ignore
307
    {
308
        return $this->belongsTo(\App\Models\Lookup\Department::class);
309
    }
310
311
    public function job_term() // phpcs:ignore
312
    {
313
        return $this->belongsTo(\App\Models\Lookup\JobTerm::class);
314
    }
315
316
    public function language_requirement() // phpcs:ignore
317
    {
318
        return $this->belongsTo(\App\Models\Lookup\LanguageRequirement::class);
319
    }
320
321
    public function manager() // phpcs:ignore
322
    {
323
        return $this->belongsTo(\App\Models\Manager::class);
324
    }
325
326
    public function province() // phpcs:ignore
327
    {
328
        return $this->belongsTo(\App\Models\Lookup\Province::class);
329
    }
330
331
    public function security_clearance() // phpcs:ignore
332
    {
333
        return $this->belongsTo(\App\Models\Lookup\SecurityClearance::class);
334
    }
335
336
    public function criteria() // phpcs:ignore
337
    {
338
        return $this->hasMany(\App\Models\Criteria::class);
339
    }
340
341
    public function hr_advisors() // phpcs:ignore
342
    {
343
        return $this->belongsToMany(
344
            \App\Models\HrAdvisor::class,
345
            'claimed_jobs'
346
        );
347
    }
348
349
    public function job_applications() // phpcs:ignore
350
    {
351
        return $this->hasMany(\App\Models\JobApplication::class);
352
    }
353
354
    public function job_poster_key_tasks() // phpcs:ignore
355
    {
356
        return $this->hasMany(\App\Models\JobPosterKeyTask::class)->orderBy('order', 'asc');
357
    }
358
359
    public function job_poster_questions() // phpcs:ignore
360
    {
361
        return $this->hasMany(\App\Models\JobPosterQuestion::class);
362
    }
363
364
    public function telework_allowed_frequency() // phpcs:ignore
365
    {
366
        return $this->belongsTo(\App\Models\Lookup\Frequency::class);
367
    }
368
369
    public function flexible_hours_frequency() // phpcs:ignore
370
    {
371
        return $this->belongsTo(\App\Models\Lookup\Frequency::class);
372
    }
373
374
    public function travel_requirement() // phpcs:ignore
375
    {
376
        return $this->belongsTo(\App\Models\Lookup\TravelRequirement::class);
377
    }
378
379
    public function overtime_requirement() // phpcs:ignore
380
    {
381
        return $this->belongsTo(\App\Models\Lookup\OvertimeRequirement::class);
382
    }
383
384
    public function classification() // phpcs:ignore
385
    {
386
        return $this->belongsTo(\App\Models\Classification::class);
387
    }
388
389
    public function comments() // phpcs:ignore
390
    {
391
        return $this->hasMany(\App\Models\Comment::class);
392
    }
393
394
    public function job_poster_status() // phpcs:ignore
395
    {
396
        return $this->belongsTo(\App\Models\Lookup\JobPosterStatus::class);
397
    }
398
399
    public function job_poster_status_histories() // phpcs:ignore
400
    {
401
        return $this->hasMany(\App\Models\JobPosterStatusHistory::class);
402
    }
403
404
    // @codeCoverageIgnoreEnd
405
    /* Artificial Relations */
406
407
    /**
408
     * Get all of the Job Applications submitted to this
409
     * Job Poster.
410
     *
411
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
412
     */
413
    public function submitted_applications() // phpcs:ignore
414
    {
415
        return $this->hasMany(\App\Models\JobApplication::class)->whereDoesntHave('application_status', function ($query): void {
416
            $query->where('name', 'draft');
417
        });
418
    }
419
420
    /* Overrides */
421
422
    /**
423
     * Retrieve the model for a bound value.
424
     * Seems to be a useful workaround for providing submitted_applications_count
425
     * to any bound routes that receive a jobPoster instance without using the
426
     * withCount property on the model itself.
427
     * See https://github.com/laravel/framework/issues/23957 for more info.
428
     *
429
     * @param mixed $value Value used to retrieve the model instance.
430
     *
431
     * @return \Illuminate\Database\Eloquent\Model|null
432
     */
433
    public function resolveRouteBinding($value) // phpcs:ignore
434
    {
435
        return $this->withCount('submitted_applications')->where('id', $value)->first() ?? abort(404);
0 ignored issues
show
Bug introduced by
The function abort 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

435
        return $this->withCount('submitted_applications')->where('id', $value)->first() ?? /** @scrutinizer ignore-call */ abort(404);
Loading history...
436
    }
437
438
    /* Methods */
439
440
    public function submitted_applications_count() //phpcs:ignore
441
    {
442
        return $this->submitted_applications()->count();
443
    }
444
445
    /**
446
     * Formatted and localized date and time the Job Poster closes.
447
     *
448
     * @return string[]
449
     */
450
    public function applyBy(): array
451
    {
452
        $localCloseDate = new Date($this->close_date_time); // This initializes the date object in UTC time.
453
        $localCloseDate->setTimezone(new \DateTimeZone(self::TIMEZONE)); // Then set the time zone for display.
454
        $displayDate = [
455
            'date' => $localCloseDate->format(self::DATE_FORMAT[App::getLocale()]),
456
            'time' => $localCloseDate->format(self::TIME_FORMAT[App::getLocale()])
457
        ];
458
459
        if (App::isLocale('fr')) {
460
            $displayDate['time'] = str_replace(['EST', 'EDT'], ['HNE', 'HAE'], $displayDate['time']);
461
        }
462
463
        return $displayDate;
464
    }
465
466
    /**
467
     * Return whether the Job is Open or Closed.
468
     * Used by the Admin Portal JobPosterCrudController.
469
     *
470
     * @return string
471
     */
472
    public function displayStatus(): string
473
    {
474
        return $this->isOpen() ? 'Open' : 'Closed';
475
    }
476
477
    /**
478
     * Check if a Job Poster is open for applications.
479
     *
480
     * @return boolean
481
     */
482
    public function isOpen(): bool
483
    {
484
        return $this->job_poster_status->key === 'live';
485
    }
486
487
    /**
488
     * Check if a Job Poster is closed for applications.
489
     *
490
     * @return boolean
491
     */
492
    public function isClosed(): bool
493
    {
494
        return ($this->job_poster_status->key === 'assessment'
495
            || $this->job_poster_status->key === 'completed');
496
    }
497
498
    /**
499
     * Return true if this job should be visible to hr advisors.
500
     * It should become visible after Manager has requested a review.
501
     *
502
     * @return boolean
503
     */
504
    public function isVisibleToHr()
505
    {
506
        return $this->job_poster_status->key !== 'draft';
507
    }
508
509
    /**
510
     * Check if a Job Poster is open to public view.
511
     *
512
     * @return boolean
513
     */
514
    public function isPublic(): bool
515
    {
516
        return ($this->job_poster_status->key == 'live'
517
            || $this->job_poster_status->key == 'assessment'
518
            || $this->job_poster_status->key == 'completed');
519
    }
520
521
    /**
522
     * Check if a Job Poster is theoretically editable.
523
     *
524
     * @return boolean
525
     */
526
    public function isEditable(): bool
527
    {
528
        // Admins, at least, should be able to edit the job up until the public can see it.
529
        return !$this->isPublic();
530
    }
531
532
    /**
533
     * Calculate the remaining time a Job Poster is open.
534
     *
535
     * @return string
536
     */
537
    public function timeRemaining(): string
538
    {
539
        $interval = $this->close_date_time->diff(Date::now());
540
541
        $d = $interval->d;
542
        $h = $interval->h;
543
        $m = $interval->i;
544
        $s = $interval->s;
545
546
        if ($d > 0) {
547
            $unit = 'day';
548
            $count = $d;
549
        } elseif ($h > 0) {
550
            $unit = 'hour';
551
            $count = $h;
552
        } elseif ($m > 0) {
553
            $unit = 'minute';
554
            $count = $m;
555
        } else {
556
            $unit = 'second';
557
            $count = $s;
558
        }
559
560
        $key = "common/time.$unit";
561
562
        return Lang::choice($key, $count);
563
    }
564
565
566
    /**
567
     * The database model stores a foreign id to the classification table,
568
     * but to simplify the API, this model simply returns the key as classification_code.
569
     *
570
     * @return string|null
571
     */
572
    public function getClassificationCodeAttribute()
573
    {
574
        if ($this->classification_id !== null) {
575
            return $this->classification->key;
576
        }
577
        return null;
578
    }
579
580
    /**
581
     *
582
     * Get the full government classification message.
583
     *
584
     * @return string|null
585
     */
586
    public function getClassificationMessageAttribute()
587
    {
588
        if ($this->classification_id !== null && $this->classification_level !== null) {
589
            return $this->classification->key . '-0' . $this->classification_level;
590
        }
591
        return null;
592
    }
593
594
    /**
595
     *
596
     * Job Poster is from the Strategic Talent Response department
597
     *
598
     * @return boolean
599
     */
600
    public function isInStrategicResponseDepartment()
601
    {
602
        return $this->department && $this->department->id === config('app.strategic_response_department_id');
0 ignored issues
show
Bug introduced by
The function config 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

602
        return $this->department && $this->department->id === /** @scrutinizer ignore-call */ config('app.strategic_response_department_id');
Loading history...
603
    }
604
}
605