Passed
Push — task/classification-accessor ( af53cc...d7421e )
by Chris
13:55 queued 01:38
created

JobPoster::getClassificationCodeAttribute()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
eloc 3
dl 0
loc 6
rs 10
c 2
b 0
f 1
cc 2
nc 2
nop 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 Astrotomic\Translatable\Translatable;
11
12
use Backpack\CRUD\app\Models\Traits\CrudTrait;
13
14
use Jenssegers\Date\Date;
15
16
use Illuminate\Notifications\Notifiable;
17
use Illuminate\Support\Facades\App;
18
use Illuminate\Support\Facades\Lang;
19
20
use App\Events\JobSaved;
21
22
/**
23
 * Class JobPoster
24
 *
25
 * @property int $id
26
 * @property int $job_term_id
27
 * @property string $chosen_lang
28
 * @property int $term_qty
29
 * @property \Jenssegers\Date\Date $open_date_time
30
 * @property \Jenssegers\Date\Date $close_date_time
31
 * @property \Jenssegers\Date\Date $start_date_time
32
 * @property \Jenssegers\Date\Date $review_requested_at
33
 * @property \Jenssegers\Date\Date $published_at
34
 * @property int $department_id
35
 * @property int $province_id
36
 * @property int $salary_min
37
 * @property int $salary_max
38
 * @property int $noc
39
 * @property int $classification_id
40
 * @property int $classification_level
41
 * @property int $security_clearance_id
42
 * @property int $language_requirement_id
43
 * @property boolean $remote_work_allowed
44
 * @property int $manager_id
45
 * @property boolean $published
46
 * @property int $team_size
47
 * @property array $work_env_features This should be an array of boolean flags for features, ie json of shape {[feature: string]: boolean}
48
 * @property int $fast_vs_steady
49
 * @property int $horizontal_vs_vertical
50
 * @property int $experimental_vs_ongoing
51
 * @property int $citizen_facing_vs_back_office
52
 * @property int $collaborative_vs_independent
53
 * @property int $telework_allowed_frequency_id
54
 * @property int $flexible_hours_frequency_id
55
 * @property int $travel_requirement_id
56
 * @property int $overtime_requirement_id
57
 * @property int $process_number
58
 * @property int $priority_clearance_number
59
 * @property \Jenssegers\Date\Date $loo_issuance_date
60
 * @property \Jenssegers\Date\Date $created_at
61
 * @property \Jenssegers\Date\Date $updated_at
62
 *
63
 * @property int $submitted_applications_count
64
 *
65
 * @property \App\Models\Lookup\Department $department
66
 * @property \App\Models\Lookup\JobTerm $job_term
67
 * @property \App\Models\Lookup\LanguageRequirement $language_requirement
68
 * @property \App\Models\Manager $manager
69
 * @property \App\Models\Lookup\Province $province
70
 * @property \App\Models\Lookup\SecurityClearance $security_clearance
71
 * @property \Illuminate\Database\Eloquent\Collection $criteria
72
 * @property \Illuminate\Database\Eloquent\Collection $job_applications
73
 * @property \Illuminate\Database\Eloquent\Collection $job_poster_key_tasks
74
 * @property \Illuminate\Database\Eloquent\Collection $job_poster_questions
75
 * @property \Illuminate\Database\Eloquent\Collection $job_poster_translations
76
 * @property \Illuminate\Database\Eloquent\Collection $submitted_applications
77
 * @property \App\Models\Lookup\Frequency $telework_allowed_frequency
78
 * @property \App\Models\Lookup\Frequency $flexible_hours_frequency
79
 *
80
 * Localized Properties:
81
 * @property string $city
82
 * @property string $title
83
 * @property string $dept_impact
84
 * @property string $team_impact
85
 * @property string $hire_impact
86
 * @property string $division
87
 * @property string $education
88
 * @property string $work_env_description
89
 * @property string $culture_summary
90
 * @property string $culture_special
91
 *
92
 * Methods
93
 * @method boolean isOpen()
94
 * @method string timeRemaining()
95
 * @method mixed[] toApiArray()
96
 *
97
 * Computed Properties
98
 * @property string|null $classification_code
99
 * @property string|null $classification_message
100
 */
101
class JobPoster extends BaseModel
102
{
103
    use CrudTrait;
0 ignored issues
show
introduced by
The trait Backpack\CRUD\app\Models\Traits\CrudTrait requires some properties which are not provided by App\Models\JobPoster: $Type, $fakeColumns
Loading history...
104
    use Translatable;
0 ignored issues
show
Bug introduced by
The trait Astrotomic\Translatable\Translatable requires the property $each which is not provided by App\Models\JobPoster.
Loading history...
105
    use Notifiable;
0 ignored issues
show
Bug introduced by
The trait Illuminate\Notifications\Notifiable requires the property $email which is not provided by App\Models\JobPoster.
Loading history...
106
107
    const DATE_FORMAT = [
108
        'en' => 'M jS, Y',
109
        'fr' => 'd M Y',
110
    ];
111
    const TIME_FORMAT = [
112
        'en' => 'h:i A T',
113
        'fr' => 'H \h i T',
114
    ];
115
    const TIMEZONE = 'America/Toronto';
116
117
    /**
118
     * @var string[] $translatedAttributes
119
     */
120
    public $translatedAttributes = [
121
        'city',
122
        'title',
123
        'dept_impact',
124
        'team_impact',
125
        'hire_impact',
126
        'division',
127
        'education',
128
        'work_env_description',
129
        'culture_summary',
130
        'culture_special',
131
    ];
132
133
    /**
134
     * @var string[] $casts
135
     */
136
    protected $casts = [
137
        'job_term_id' => 'int',
138
        'department_id' => 'int',
139
        'province_id' => 'int',
140
        'salary_min' => 'int',
141
        'salary_max' => 'int',
142
        'noc' => 'int',
143
        'classification_id' => 'int',
144
        'classification_level' => 'int',
145
        'security_clearance_id' => 'int',
146
        'language_requirement_id' => 'int',
147
        'remote_work_allowed' => 'boolean',
148
        'manager_id' => 'int',
149
        'published' => 'boolean',
150
        'team_size' => 'int',
151
        'work_env_features' => 'array',
152
        'fast_vs_steady' => 'int',
153
        'horizontal_vs_vertical' => 'int',
154
        'experimental_vs_ongoing' => 'int',
155
        'citizen_facing_vs_back_office' => 'int',
156
        'collaborative_vs_independent' => 'int',
157
        'telework_allowed_frequency_id' => 'int',
158
        'flexible_hours_frequency_id' => 'int',
159
        'travel_requirement_id' => 'int',
160
        'overtime_requirement_id' => 'int',
161
    ];
162
163
    /**
164
     * @var string[] $dates
165
     */
166
    protected $dates = [
167
        'open_date_time',
168
        'close_date_time',
169
        'start_date_time',
170
        'review_requested_at',
171
        'published_at',
172
        'loo_issuance_date',
173
    ];
174
175
    /**
176
     * @var string[] $fillable
177
     */
178
    protected $fillable = [
179
        'job_term_id',
180
        'chosen_lang',
181
        'term_qty',
182
        'open_date_time',
183
        'close_date_time',
184
        'start_date_time',
185
        'department_id',
186
        'province_id',
187
        'salary_min',
188
        'salary_max',
189
        'noc',
190
        'security_clearance_id',
191
        'language_requirement_id',
192
        'remote_work_allowed',
193
        'published',
194
        'team_size',
195
        'work_env_features',
196
        'fast_vs_steady',
197
        'horizontal_vs_vertical',
198
        'experimental_vs_ongoing',
199
        'citizen_facing_vs_back_office',
200
        'collaborative_vs_independent',
201
        'telework_allowed_frequency_id',
202
        'flexible_hours_frequency_id',
203
        'travel_requirement_id',
204
        'overtime_requirement_id',
205
        'process_number',
206
        'priority_clearance_number',
207
        'loo_issuance_date',
208
        'classification_id',
209
        'classification_level',
210
    ];
211
212
    /**
213
     * The attributes that should be visible in arrays.
214
     * In this case, it blocks loaded relations from appearing.
215
     *
216
     * @var array
0 ignored issues
show
introduced by
@var annotation of property \App\Models\JobPoster::$visible does not specify type hint for its items.
Loading history...
217
     */
218
    protected $visible = [
219
        'id',
220
        'manager_id',
221
        'chosen_lang',
222
        'term_qty',
223
        'open_date_time',
224
        'close_date_time',
225
        'start_date_time',
226
        'department_id',
227
        'province_id',
228
        'salary_min',
229
        'salary_max',
230
        'noc',
231
        'security_clearance_id',
232
        'language_requirement_id',
233
        'remote_work_allowed',
234
        'published_at',
235
        'published',
236
        'review_requested_at',
237
        'team_size',
238
        'work_env_features',
239
        'fast_vs_steady',
240
        'horizontal_vs_vertical',
241
        'experimental_vs_ongoing',
242
        'citizen_facing_vs_back_office',
243
        'collaborative_vs_independent',
244
        'telework_allowed_frequency_id',
245
        'flexible_hours_frequency_id',
246
        'travel_requirement_id',
247
        'overtime_requirement_id',
248
        'process_number',
249
        'priority_clearance_number',
250
        'loo_issuance_date',
251
        'classification_id',
252
        'classification_level'
253
    ];
254
255
    /**
256
     * The accessors to append to the model's array form.
257
     *
258
     * @var string[] $appends
259
     */
260
    protected $appends = [
261
        'classification_code',
262
        'classification_message'
263
    ];
264
265
    /**
266
     * @var mixed[] $dispatchesEvents
267
     */
268
    protected $dispatchesEvents = [
269
        'saved' => JobSaved::class,
270
    ];
271
272
    // @codeCoverageIgnoreStart
273
    public function department() // phpcs:ignore
274
    {
275
        return $this->belongsTo(\App\Models\Lookup\Department::class);
276
    }
277
278
    public function job_term() // phpcs:ignore
279
    {
280
        return $this->belongsTo(\App\Models\Lookup\JobTerm::class);
281
    }
282
283
    public function language_requirement() // phpcs:ignore
284
    {
285
        return $this->belongsTo(\App\Models\Lookup\LanguageRequirement::class);
286
    }
287
288
    public function manager() // phpcs:ignore
289
    {
290
        return $this->belongsTo(\App\Models\Manager::class);
291
    }
292
293
    public function province() // phpcs:ignore
294
    {
295
        return $this->belongsTo(\App\Models\Lookup\Province::class);
296
    }
297
298
    public function security_clearance() // phpcs:ignore
299
    {
300
        return $this->belongsTo(\App\Models\Lookup\SecurityClearance::class);
301
    }
302
303
    public function criteria() // phpcs:ignore
304
    {
305
        return $this->hasMany(\App\Models\Criteria::class);
306
    }
307
308
    public function job_applications() // phpcs:ignore
309
    {
310
        return $this->hasMany(\App\Models\JobApplication::class);
311
    }
312
313
    public function job_poster_key_tasks() // phpcs:ignore
314
    {
315
        return $this->hasMany(\App\Models\JobPosterKeyTask::class);
316
    }
317
318
    public function job_poster_questions() // phpcs:ignore
319
    {
320
        return $this->hasMany(\App\Models\JobPosterQuestion::class);
321
    }
322
323
    public function job_poster_translations() // phpcs:ignore
324
    {
325
        return $this->hasMany(\App\Models\JobPosterTranslation::class);
326
    }
327
328
    public function telework_allowed_frequency() // phpcs:ignore
329
    {
330
        return $this->belongsTo(\App\Models\Lookup\Frequency::class);
331
    }
332
333
    public function flexible_hours_frequency() // phpcs:ignore
334
    {
335
        return $this->belongsTo(\App\Models\Lookup\Frequency::class);
336
    }
337
338
    public function travel_requirement() // phpcs:ignore
339
    {
340
        return $this->belongsTo(\App\Models\Lookup\TravelRequirement::class);
341
    }
342
343
    public function overtime_requirement() // phpcs:ignore
344
    {
345
        return $this->belongsTo(\App\Models\Lookup\OvertimeRequirement::class);
346
    }
347
348
    public function classification() // phpcs:ignore
349
    {
350
        return $this->belongsTo(\App\Models\Classification::class);
351
    }
352
    // @codeCoverageIgnoreEnd
353
    /* Artificial Relations */
354
355
    /**
356
     * Get all of the Job Applications submitted to this
357
     * Job Poster.
358
     *
359
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
360
     */
361
    public function submitted_applications() // phpcs:ignore
362
    {
363
        return $this->hasMany(\App\Models\JobApplication::class)->whereDoesntHave('application_status', function ($query): void {
364
            $query->where('name', 'draft');
365
        });
366
    }
367
368
    /* Overrides */
369
370
    /**
371
     * Retrieve the model for a bound value.
372
     * Seems to be a useful workaround for providing submitted_applications_count
373
     * to any bound routes that receive a jobPoster instance without using the
374
     * withCount property on the model itself.
375
     * See https://github.com/laravel/framework/issues/23957 for more info.
376
     *
377
     * @param mixed $value Value used to retrieve the model instance.
378
     *
379
     * @return \Illuminate\Database\Eloquent\Model|null
380
     */
381
    public function resolveRouteBinding($value) // phpcs:ignore
382
    {
383
        return $this->withCount('submitted_applications')->where('id', $value)->first() ?? abort(404);
384
    }
385
386
    /**
387
     * Intercept setting the "published" attribute, and set the
388
     * "published_at" timestamp if true.
389
     *
390
     * @param mixed $value Incoming value for the 'published' attribute.
391
     *
392
     * @return void
393
     */
394
    public function setPublishedAttribute($value): void
395
    {
396
        if ($value) {
397
            $this->attributes['published_at'] = new Date();
398
        } else {
399
            $this->attributes['published_at'] = null;
400
        }
401
        if ($value === null) {
402
            $value = false;
403
        }
404
        $this->attributes['published'] = $value;
405
    }
406
407
    /* Methods */
408
409
    public function submitted_applications_count() //phpcs:ignore
410
    {
411
        return $this->submitted_applications()->count();
412
    }
413
414
    /**
415
     * Formatted and localized date and time the Job Poster closes.
416
     *
417
     * @return string[]
418
     */
419
    public function applyBy(): array
420
    {
421
        $localCloseDate = new Date($this->close_date_time); // This initializes the date object in UTC time.
422
        $localCloseDate->setTimezone(new \DateTimeZone(self::TIMEZONE)); // Then set the time zone for display.
423
        $displayDate = [
424
            'date' => $localCloseDate->format(self::DATE_FORMAT[App::getLocale()]),
425
            'time' => $localCloseDate->format(self::TIME_FORMAT[App::getLocale()])
426
        ];
427
428
        if (App::isLocale('fr')) {
429
            $displayDate['time'] = str_replace(['EST', 'EDT'], ['HNE', 'HAE'], $displayDate['time']);
430
        }
431
432
        return $displayDate;
433
    }
434
435
    /**
436
     * Return whether the Job is Open or Closed.
437
     * Used by the Admin Portal JobPosterCrudController.
438
     *
439
     * @return string
440
     */
441
    public function displayStatus(): string
442
    {
443
        return $this->isOpen() ? 'Open' : 'Closed';
444
    }
445
446
    /**
447
     * Check if a Job Poster is open for applications.
448
     *
449
     * @return boolean
450
     */
451
    public function isOpen(): bool
452
    {
453
        return $this->published
454
            && $this->open_date_time !== null
455
            && $this->close_date_time !== null
456
            && $this->open_date_time->isPast()
457
            && $this->close_date_time->isFuture();
458
    }
459
460
    /**
461
     * Check if a Job Poster is closed for applications.
462
     *
463
     * @return boolean
464
     */
465
    public function isClosed(): bool
466
    {
467
        return $this->published
468
            && $this->open_date_time !== null
469
            && $this->close_date_time !== null
470
            && $this->open_date_time->isPast()
471
            && $this->close_date_time->isPast();
472
    }
473
474
    /**
475
     * Calculate the remaining time a Job Poster is open.
476
     *
477
     * @return string
478
     */
479
    public function timeRemaining(): string
480
    {
481
        $interval = $this->close_date_time->diff(Date::now());
482
483
        $d = $interval->d;
484
        $h = $interval->h;
485
        $m = $interval->i;
486
        $s = $interval->s;
487
488
        if ($d > 0) {
489
            $unit = 'day';
490
            $count = $d;
491
        } elseif ($h > 0) {
492
            $unit = 'hour';
493
            $count = $h;
494
        } elseif ($m > 0) {
495
            $unit = 'minute';
496
            $count = $m;
497
        } else {
498
            $unit = 'second';
499
            $count = $s;
500
        }
501
502
        $key = "common/time.$unit";
503
504
        return Lang::choice($key, $count);
505
    }
506
507
    /**
508
     * Return the current status for the Job Poster.
509
     * Possible values are "draft", "submitted", "published" and "closed".
510
     *
511
     * @return string
512
     */
513
    public function status(): string
514
    {
515
        $status = 'draft';
516
        if ($this->isOpen()) {
517
            $status = 'published';
518
        } elseif ($this->isClosed()) {
519
            $status = 'closed';
520
        } elseif ($this->review_requested_at !== null) {
521
            $status = 'submitted';
522
        } else {
523
            $status = 'draft';
524
        }
525
526
        return $status;
527
    }
528
529
    /**
530
     * The database model stores a foreign id to the classification table,
531
     * but to simplify the API, this model simply returns the key as classification_code.
532
     *
533
     * @return string|null
534
     */
535
    public function getClassificationCodeAttribute()
0 ignored issues
show
introduced by
Method \App\Models\JobPoster::getClassificationCodeAttribute() does not have return type hint for its return value but it should be possible to add it based on @return annotation "string|null".
Loading history...
536
    {
537
        if ($this->classification_id !== null) {
538
            return $this->classification->key;
539
        }
540
        return null;
541
    }
542
543
    /**
544
     *
545
     * Get the full government classification message.
546
     *
547
     * @return string|null
548
     */
549
    public function getClassificationMessageAttribute()
0 ignored issues
show
introduced by
Method \App\Models\JobPoster::getClassificationMessageAttribute() does not have return type hint for its return value but it should be possible to add it based on @return annotation "string|null".
Loading history...
550
    {
551
        if ($this->classification_id !== null && $this->classification_level !== null) {
552
            return $this->classification->key . '-0' . $this->classification_level;
553
        }
554
        return null;
555
    }
556
557
    /**
558
     * Return the array of values used to represent this object in an api response.
559
     * This array should contain no nested objects (besides translations).
560
     *
561
     * @return mixed[]
562
     */
563
    public function toApiArray(): array
564
    {
565
        $jobWithTranslations = array_merge($this->toArray(), $this->getTranslationsArray());
566
        return $jobWithTranslations;
567
    }
568
}
569