Skip to content

Commit

Permalink
[Fix] Timestamp updates on manual status change (#12734)
Browse files Browse the repository at this point in the history
* null timestamps on status update

* add test for null timestamps

* write command to update existing candidates

* allow setting multiple timestamps

* fix tests

* fix test for timestamp update

* add missing status

* add expired back with comment

* update suspended at for withdrew

* update command description

* update command to also set timestamps

* do not set placed at for tentative status

* do not override final decsion for removed status

* update applicants quietly
  • Loading branch information
esizer authored and brindasasi committed Feb 12, 2025
1 parent 59a7f1e commit f77899b
Show file tree
Hide file tree
Showing 4 changed files with 329 additions and 25 deletions.
154 changes: 154 additions & 0 deletions api/app/Console/Commands/UpdateApplicationTimestamps.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
<?php

namespace App\Console\Commands;

use App\Enums\PoolCandidateStatus;
use App\Models\PoolCandidate;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Builder;

class UpdateApplicationTimestamps extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:update-application-timestamps
{--dry : Dry run to see how many candidates will be affected}
';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Updates application timestamps based on status.';

private $dry = false;

private $nullStatuses = [
PoolCandidateStatus::NEW_APPLICATION->name,
PoolCandidateStatus::APPLICATION_REVIEW->name,
PoolCandidateStatus::SCREENED_IN->name,
PoolCandidateStatus::UNDER_ASSESSMENT->name,
];

private $finalStatuses = [
PoolCandidateStatus::SCREENED_OUT_APPLICATION->name,
PoolCandidateStatus::SCREENED_OUT_ASSESSMENT->name,
PoolCandidateStatus::QUALIFIED_AVAILABLE->name,
PoolCandidateStatus::QUALIFIED_UNAVAILABLE->name,
PoolCandidateStatus::QUALIFIED_WITHDREW->name,
PoolCandidateStatus::PLACED_TENTATIVE->name,
PoolCandidateStatus::EXPIRED->name,
];

private $removedStatuses = [
PoolCandidateStatus::SCREENED_OUT_NOT_INTERESTED->name,
PoolCandidateStatus::SCREENED_OUT_NOT_RESPONSIVE->name,
PoolCandidateStatus::REMOVED->name,
];

private $placedStatuses = [
PoolCandidateStatus::PLACED_INDETERMINATE->name,
PoolCandidateStatus::PLACED_TERM->name,
PoolCandidateStatus::PLACED_CASUAL->name,
];

/**
* Execute the console command.
*/
public function handle()
{
$this->dry = $this->option('dry');

$now = now();

// Null out all timestamps
$nullCandidates = PoolCandidate::whereIn('pool_candidate_status', $this->nullStatuses)
->where(function ($query) {
$query->whereNotNull('removed_at')
->orWhereNotNull('placed_at')
->orWhereNotNull('final_decision_at');
});

$nullCount = $this->update($nullCandidates, [
'removed_at' => null,
'placed_at' => null,
'final_decision_at' => null,
]);

// Null non final decision fields
$final = PoolCandidate::whereIn('pool_candidate_status', $this->finalStatuses)
->where(function ($query) {
$query->whereNotNull('removed_at')
->orWhereNotNull('placed_at');
});

$finalCount = $this->update($final, [
'removed_at' => null,
'placed_at' => null,
]);

// Set final decision where null
$finalNull = PoolCandidate::whereIn('pool_candidate_status', $this->finalStatuses)
->whereNull('final_decision_at');

$finalCount += $this->update($finalNull, ['final_decision_at' => $now]);

// Null non removed at fields
$removed = PoolCandidate::whereIn('pool_candidate_status', $this->removedStatuses)
->whereNotNull('placed_at');

$removedCount = $this->update($removed, [
'placed_at' => null,
]);

// Set removed at where null
$removedNull = PoolCandidate::whereIn('pool_candidate_status', $this->removedStatuses)
->whereNull('removed_at');

$removedCount += $this->update($removedNull, ['removed_at' => $now]);

$placed = PoolCandidate::whereIn('pool_candidate_status', $this->placedStatuses)
->whereNotNull('removed_at');

$placedCount = $this->update($placed, ['removed_at' => null]);

$placedNullFinal = PoolCandidate::whereIn('pool_candidate_status', $this->placedStatuses)
->whereNull('final_decision_at');

$placedCount += $this->update($placedNullFinal, ['final_decision_at' => $now]);

$placedNullPlaced = PoolCandidate::whereIn('pool_candidate_status', $this->placedStatuses)
->whereNull('placed_at');

$placedCount += $this->update($placedNullPlaced, ['placed_at' => $now]);

$suspended = PoolCandidate::where('pool_candidate_status', PoolCandidateStatus::QUALIFIED_WITHDREW->name)
->whereNull('suspended_at');

$suspendedCount = $this->update($suspended, ['suspended_at' => $now]);

$total = $nullCount + $finalCount + $removedCount + $placedCount + $suspendedCount;
$this->table(
['Null', 'Final', 'Removed', 'Placed', 'Suspended', 'Total'],
[[$nullCount, $finalCount, $removedCount, $placedCount, $suspendedCount, $total]]
);
}

private function update(Builder $query, array $state)
{
if (! $this->dry) {
$query->chunk(100, function ($candidates) use ($state) {
foreach ($candidates as $candidate) {
$candidate->updateQuietly($state);
}
});
}

return $query->count();

}
}
41 changes: 29 additions & 12 deletions api/app/GraphQL/Mutations/UpdatePoolCandidateStatus.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,41 +12,58 @@
public function __invoke(null $_, array $args)
{
$candidate = PoolCandidate::findOrFail($args['id']);
$values = [];

if (isset($args['pool_candidate_status']) && $args['pool_candidate_status'] !== $candidate->pool_candidate_status) {
$status = $args['pool_candidate_status'];

$timestamp = match ($status) {
$now = now();
$values = [
'removed_at' => null,
'final_decision_at' => null,
'placed_at' => null,
];

$timestamps = match ($status) {
PoolCandidateStatus::EXPIRED->name,
PoolCandidateStatus::SCREENED_OUT_ASSESSMENT->name,
PoolCandidateStatus::SCREENED_OUT_APPLICATION->name,
PoolCandidateStatus::QUALIFIED_AVAILABLE->name => 'final_decision_at',
PoolCandidateStatus::PLACED_TENTATIVE->name,
PoolCandidateStatus::QUALIFIED_UNAVAILABLE->name,
PoolCandidateStatus::QUALIFIED_WITHDREW->name,
PoolCandidateStatus::QUALIFIED_AVAILABLE->name => ['final_decision_at'],

PoolCandidateStatus::SCREENED_OUT_NOT_RESPONSIVE->name,
PoolCandidateStatus::SCREENED_OUT_NOT_INTERESTED->name,
PoolCandidateStatus::QUALIFIED_UNAVAILABLE->name,
PoolCandidateStatus::QUALIFIED_WITHDREW->name,
PoolCandidateStatus::REMOVED->name => 'removed_at',
PoolCandidateStatus::REMOVED->name => ['removed_at'],

PoolCandidateStatus::PLACED_TENTATIVE->name,
PoolCandidateStatus::PLACED_CASUAL->name,
PoolCandidateStatus::PLACED_TERM->name,
PoolCandidateStatus::PLACED_INDETERMINATE->name => 'placed_at',
PoolCandidateStatus::PLACED_INDETERMINATE->name => ['placed_at', 'final_decision_at'],

default => null// no-op
};

if ($timestamp) {
$candidate->$timestamp = now();
if ($timestamps) {
foreach ($timestamps as $timestamp) {
$values[$timestamp] = $now;
}
}

$candidate->pool_candidate_status = $status;
if ($status === PoolCandidateStatus::QUALIFIED_WITHDREW->name) {
$values['suspended_at'] = $now;
}

$values['pool_candidate_status'] = $status;

}

if (isset($args['expiry_date'])) {
$candidate->expiry_date = $args['expiry_date'];
$values['expiry_date'] = $args['expiry_date'];
}

$candidate->save();
$candidate->update($values);
$candidate->refresh();

return $candidate;
}
Expand Down
3 changes: 3 additions & 0 deletions api/app/Models/PoolCandidate.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ class PoolCandidate extends Model
'archived_at',
'submitted_at',
'suspended_at',
'removed_at',
'final_decision_at',
'placed_at',
'user_id',
'pool_id',
'signature',
Expand Down
Loading

0 comments on commit f77899b

Please sign in to comment.