Passed
Branch task/common-translation-packag... (519c3a)
by Grant
31:10 queued 17:48
created

JobPoster::toApiArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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