Skip to content

Commit

Permalink
Merge branch 'main' into 12164-candidate-status-chips
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesHuf committed Dec 20, 2024
2 parents 148361b + b280377 commit 7b4024d
Show file tree
Hide file tree
Showing 171 changed files with 5,579 additions and 2,084 deletions.
71 changes: 50 additions & 21 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ on:
- "**.md"

pull_request:
paths-ignore:
- "**.md"
paths:
- .github/workflows/*playwright*.yml
- apps/**
- packages/**
- api/**
merge_group:
branches: [main]
# Concurrency is used to cancel other currently-running jobs, to preserve
Expand All @@ -29,6 +32,11 @@ jobs:
playwright:
name: Playwright
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
env:
# Use native docker command within docker compose
COMPOSE_DOCKER_CLI_BUILD: 1
Expand Down Expand Up @@ -69,28 +77,49 @@ jobs:
- name: Install Playwright Browsers
run: npx playwright install chromium webkit --with-deps

- name: Run Chromium Tests
run: pnpm run e2e:playwright:chromium
- name: Run Tests
run: pnpm run e2e:playwright --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: chromium-report
path: ./apps/playwright/playwright-report/
retention-days: 30
name: blob-report-${{ matrix.shardIndex }}
path: ./apps/playwright/blob-report
retention-days: 1

- name: Run Webkit Tests
run: pnpm run e2e:playwright:webkit
- uses: actions/upload-artifact@v4
if: always()
with:
name: webkit-report
path: ./apps/playwright/playwright-report/
retention-days: 30
merge-reports:
# Merge reports after playwright-tests, even if some shards have failed
if: ${{ !cancelled() }}
needs: [playwright]
runs-on: ubuntu-latest
env:
PNPM_VERSION: "9.12.3"
steps:
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: pnpm

# Fix some issue with cache in setup-node
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Download blob reports from GitHub Actions Artifacts
uses: actions/download-artifact@v4
with:
path: all-blob-reports
pattern: blob-report-*
merge-multiple: true

- name: Check status of containers
if: failure()
run: docker compose ps
- name: Merge into HTML Report
run: npx playwright merge-reports --reporter html ./all-blob-reports

- name: "Check logs: web server container"
if: failure()
run: docker compose logs webserver
- name: Upload HTML report
uses: actions/upload-artifact@v4
with:
name: html-report--attempt-${{ github.run_attempt }}
path: playwright-report
retention-days: 14
27 changes: 24 additions & 3 deletions api/app/Builders/PoolBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,16 @@ public function publishingGroups(?array $publishingGroups): self
return $this->whereIn('publishing_group', $publishingGroups);
}

public function streams(?array $streams): self
public function whereWorkStreamsIn(?array $streams): self
{

if (empty($streams)) {
return $this;
}

return $this->whereIn('stream', $streams);
return $this->whereHas('workStream', function ($query) use ($streams) {
$query->whereIn('id', $streams);
});
}

public function whereClassifications(?array $classifications): self
Expand All @@ -168,7 +170,7 @@ public function whereClassifications(?array $classifications): self
/**
* Custom sort to handle issues with how laravel aliases
* aggregate selects and orderBys for json fields in `lighthouse-php`
*
* The column used in the orderBy is `table_aggregate_column->property`
* But is actually aliased to snake case `table_aggregate_columnproperty`
*/
Expand All @@ -184,6 +186,25 @@ public function orderByTeamDisplayName(?array $args): self
return $this;
}

/**
* Custom sort to handle issues with how laravel aliases
* aggregate selects and orderBys for json fields in `lighthouse-php`
*
* The column used in the orderBy is `table_aggregate_column->property`
* But is actually aliased to snake case `table_aggregate_columnproperty`
*/
public function orderByWorkStreamName(?array $args): self
{
$order = $args['order'] ?? null;
$locale = $args['locale'] ?? null;

if ($order && $locale) {
return $this->withMax('workStream', 'name->'.$locale)->orderBy('work_stream_max_name'.$locale, $order);
}

return $this;
}

public function orderByPoolBookmarks(?array $args): self
{
/** @var \App\Models\User|null */
Expand Down
40 changes: 40 additions & 0 deletions api/app/Casts/LocalizedString.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\App;

class LocalizedString implements CastsAttributes
{
/**
* Cast the given value.
*
* @param array<string, mixed> $attributes
*/
public function get(Model $model, string $key, mixed $value, array $attributes): mixed
{
if (is_null($value) || $value === '' || $value === 'null') {
return null;
}

$decodedValue = is_array($value) ? $value : json_decode($value, true);
$locale = App::getLocale() ?? 'en';

return [
...$decodedValue,
'localized' => $decodedValue[$locale] ?? null,
];
}

/**
* Prepare the given value for storage.
*
* @param array<string, mixed> $attributes
*/
public function set(Model $model, string $key, mixed $value, array $attributes): mixed
{
return json_encode($value);
}
}
111 changes: 111 additions & 0 deletions api/app/Console/Commands/MigratePoolStream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php

namespace App\Console\Commands;

use App\Models\ApplicantFilter;
use App\Models\JobPosterTemplate;
use App\Models\Pool;
use App\Models\WorkStream;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;

/**
* Temporary command to support migration of
* PoolStream enum to WorkStream model.
*
* NOTE: Should be ran at any point during deployment.
* The rest of the code does not explicitly depend on this
* command but it should not be left since the frontend will
* display empty values until this has been ran.
*
* TODO: Remove in #12142
*/
class MigratePoolStream extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:migrate-pool-stream';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Migrates the pool stream enum to work stream model';

/**
* Execute the console command.
*/
public function handle()
{

$workStreams = WorkStream::all();

$this->info('Updating job poster template work streams...');
$jobPosterTemplatesUpdated = 0;
JobPosterTemplate::whereNotNull('stream')->chunkById(200, function (Collection $jobPosterTemplates) use ($workStreams, &$jobPosterTemplatesUpdated) {
foreach ($jobPosterTemplates as $jobPosterTemplate) {
$stream = $workStreams->where('key', $jobPosterTemplate->stream)->first();
if ($stream) {
$jobPosterTemplate->work_stream_id = $stream->id;
$jobPosterTemplate->save();
$jobPosterTemplatesUpdated++;
} else {
$this->error(sprintf('Work stream (%s) not found for job poster template (%s)',
$jobPosterTemplate->stream,
$jobPosterTemplate->id));
}
}
});
$this->info("Updated $jobPosterTemplatesUpdated job poster templates");

$this->newLine();
$this->info('Updating pool work streams...');
$poolsUpdated = 0;
Pool::whereNotNull('stream')->chunkById(200, function (Collection $pools) use ($workStreams, &$poolsUpdated) {
foreach ($pools as $pool) {
$stream = $workStreams->where('key', $pool->stream)->first();
if ($stream) {
$pool->work_stream_id = $stream->id;
$pool->save();
$poolsUpdated++;
} else {
$this->error(sprintf('Work stream (%s) not found for pool (%s)',
$pool->stream,
$pool->id));
}
}
});
$this->info("Updated $poolsUpdated pools");

$applicantFiltersUpdated = 0;
$this->newLine();
$this->info('Updated applicant filter work streams...');
ApplicantFilter::whereNotNull('qualified_streams')->chunkById(200, function (Collection $applicantFilters) use ($workStreams, &$applicantFiltersUpdated) {
foreach ($applicantFilters as $applicantFilter) {
$streams = $workStreams->whereIn('key', $applicantFilter->qualified_streams)->pluck('id');
if ($streams->count()) {
$applicantFilter->workStreams()->sync($streams);
$applicantFiltersUpdated++;
} else {
$this->error(sprintf('Work streams (%s) not found for applicant filter (%s)',
implode(', ', $applicantFilter->qualified_streams),
$applicantFilter->id));
}
}
});
$this->info("Updated $applicantFiltersUpdated applicant filters");

$this->newLine();
$this->info('Migration completed');
$this->table(['Model', 'Affected'], [
['JobPosterTemplate', $jobPosterTemplatesUpdated],
['Pool', $poolsUpdated],
['ApplicantFilter', $applicantFiltersUpdated],
]);

}
}
50 changes: 50 additions & 0 deletions api/app/Console/Commands/SuspendPlacedCandidates.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace App\Console\Commands;

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

class SuspendPlacedCandidates extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:suspend-placed-candidates';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Set suspended at date to now() for placed term and indeterminate candidates';

/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$applicableStatuses = [PoolCandidateStatus::PLACED_TERM->name, PoolCandidateStatus::PLACED_INDETERMINATE->name];

PoolCandidate::whereIn('pool_candidate_status', $applicableStatuses)
->whereNull('suspended_at')
->with('user')
->chunkById(100, function (Collection $candidates) {
foreach ($candidates as $candidate) {
/** @var \App\Models\PoolCandidate $candidate */
$candidate->suspended_at = Carbon::now();
$candidate->save();
}
}
);

return Command::SUCCESS;
}
}
3 changes: 3 additions & 0 deletions api/app/Enums/PoolCandidateStatus.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,15 @@ enum PoolCandidateStatus
case EXPIRED;
case REMOVED;

// searchable candidates, so the available status and others treated as available for search purposes
public static function qualifiedEquivalentGroup(): array
{
return [
PoolCandidateStatus::QUALIFIED_AVAILABLE->name,
PoolCandidateStatus::PLACED_TENTATIVE->name,
PoolCandidateStatus::PLACED_CASUAL->name,
PoolCandidateStatus::PLACED_TERM->name,
PoolCandidateStatus::PLACED_INDETERMINATE->name,
];
}

Expand Down
8 changes: 8 additions & 0 deletions api/app/GraphQL/Mutations/PlaceCandidate.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\GraphQL\Mutations;

use App\Enums\PlacementType;
use App\Models\PoolCandidate;
use Carbon\Carbon;

Expand All @@ -25,6 +26,13 @@ public function __invoke($_, array $args)
$candidate->computed_final_decision = $finalDecision['decision'];
$candidate->computed_final_decision_weight = $finalDecision['weight'];

// If setting to term or indeterminate automatically suspend the candidate, otherwise null the field
if ($placementType === PlacementType::PLACED_TERM->name || $placementType === PlacementType::PLACED_INDETERMINATE->name) {
$candidate->suspended_at = $now;
} else {
$candidate->suspended_at = null;
}

$candidate->save();

return $candidate;
Expand Down
1 change: 1 addition & 0 deletions api/app/GraphQL/Mutations/RevertPlaceCandidate.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public function __invoke($_, array $args)
$candidate->pool_candidate_status = PoolCandidateStatus::QUALIFIED_AVAILABLE->name;
$candidate->placed_at = null;
$candidate->placed_department_id = null;
$candidate->suspended_at = null;

$candidate->save();

Expand Down
Loading

0 comments on commit 7b4024d

Please sign in to comment.