Passed
Push — feature/job-builder/step-03 ( 781974...f4292a )
by Yonathan
19:44 queued 12:21
created

JobPoster::flexible_hours_frequency()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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