Skip to content

Commit

Permalink
[Feature] Send 'new pool' emails overnight (#11804)
Browse files Browse the repository at this point in the history
* remove old observer and event

* new artisan command

* no publishing group other

* add schedule

* don't send closed pools

* fix linting

* update tests

* fix comment
  • Loading branch information
petertgiles authored Nov 1, 2024
1 parent b9911a5 commit 3cbb385
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 155 deletions.
108 changes: 108 additions & 0 deletions api/app/Console/Commands/SendNotificationsPoolPublished.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php

namespace App\Console\Commands;

use App\Enums\NotificationFamily;
use App\Enums\PublishingGroup;
use App\Models\Pool;
use App\Models\User;
use App\Notifications\NewJobPosted;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Log;
use Throwable;

class SendNotificationsPoolPublished extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'send-notifications:pool-published';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Send notifications to users about a new pool being published.';

/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
// workaround until we get better logging in prod #11289
$onDemandLog = Log::build([
'driver' => 'single',
'path' => App::isProduction() // workaround for storage_path misconfigured in prod #11471
? '/tmp/api/storage/logs/jobs.log'
: storage_path('logs/jobs.log'),
]);

$this->info('SendNotificationsPoolPublished running at '.Carbon::now()->toDateTimeString().'.');

$successCount = 0;
$failureCount = 0;

$endOfSpan = Carbon::now();
$startOfSpan = Carbon::now()->subHours(24); // assuming the last reporting job ran about 24 hours ago
$this->info("Finding pools published between $startOfSpan and $endOfSpan.");

$poolsPublishedRecently = Pool::query()
->where('published_at', '>=', $startOfSpan)
->where('published_at', '<', $endOfSpan)
->whereNotClosed() // don't notify of pools that have already been closed
->where('publishing_group', '<>', PublishingGroup::OTHER->name) // don't notify of testing pools
->get();

$this->info('Found '.$poolsPublishedRecently->count().' pools.');

$notifications = $poolsPublishedRecently
->map(fn ($model) => get_class($model) == Pool::class
? new NewJobPosted(
$model->name['en'],
$model->name['fr'],
$model->id
)
: null
)
->whereNotNull();

$successCount = 0;
$failureCount = 0;

if ($notifications->count() > 0) {
User::whereJsonContains('enabled_email_notifications', NotificationFamily::JOB_ALERT->name)
->orWhereJsonContains('enabled_in_app_notifications', NotificationFamily::JOB_ALERT->name)
->chunk(200, function (Collection $users) use ($notifications, &$successCount, &$failureCount, $onDemandLog) {
foreach ($users as $user) {
foreach ($notifications as $notification) {
try {
$user->notify($notification);
$successCount++;
} catch (Throwable $e) {
// best-effort: log and continue
$onDemandLog->error('Failed to send "new job posted" notification for "'.$notification->poolNameEn.'" ('.$notification->poolId.') to user " '.$user->first_name.' '.$user->last_name.' ('.$user->id.'). '.$e->getMessage());
$failureCount++;
}
}
}
});
} else {
$this->info('No notifications to send.');
}

$this->info("Success: $successCount Failure: $failureCount");
if ($failureCount > 0) {
return Command::FAILURE;
} else {
return Command::SUCCESS;
}
}
}
7 changes: 7 additions & 0 deletions api/app/Console/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Console\Commands\PruneUserGeneratedFiles;
use App\Console\Commands\SendNotificationsApplicationDeadlineApproaching;
use App\Console\Commands\SendNotificationsPoolPublished;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

Expand Down Expand Up @@ -33,6 +34,12 @@ protected function schedule(Schedule $schedule)
// 10 PM Eastern is the same day across the country, close to the end of the day in NL
->dailyAt('22:00')
->appendOutputTo('/tmp/send-notifications-application-deadline-approaching.log');

// send out 'new pool' emails overnight
$schedule->command(SendNotificationsPoolPublished::class)
->timezone('America/Toronto')
->dailyAt('3:00')
->appendOutputTo('/tmp/send-notifications-pool-published.log');
}

/**
Expand Down
17 changes: 0 additions & 17 deletions api/app/Events/PoolPublished.php

This file was deleted.

69 changes: 0 additions & 69 deletions api/app/Listeners/SendNewJobPostedNotification.php

This file was deleted.

9 changes: 0 additions & 9 deletions api/app/Models/Pool.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use App\Enums\SkillCategory;
use App\GraphQL\Validators\AssessmentPlanIsCompleteValidator;
use App\GraphQL\Validators\PoolIsCompleteValidator;
use App\Observers\PoolObserver;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
Expand Down Expand Up @@ -130,14 +129,6 @@ class Pool extends Model
'community_id',
];

/**
* The "booted" method of the model.
*/
protected static function booted(): void
{
Pool::observe(PoolObserver::class);
}

/**
* Boot function for using with User Events
*
Expand Down
55 changes: 0 additions & 55 deletions api/app/Observers/PoolObserver.php

This file was deleted.

5 changes: 0 additions & 5 deletions api/app/Providers/EventServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@

use App\Events\AssessmentResultSaved;
use App\Events\CandidateStatusChanged;
use App\Events\PoolPublished;
use App\Events\UserFileGenerated;
use App\Listeners\ComputeCandidateAssessmentStatus;
use App\Listeners\ComputeCandidateFinalDecision;
use App\Listeners\SendFileGeneratedNotification;
use App\Listeners\SendNewJobPostedNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
Expand All @@ -26,9 +24,6 @@ class EventServiceProvider extends ServiceProvider
CandidateStatusChanged::class => [
ComputeCandidateFinalDecision::class,
],
PoolPublished::class => [
SendNewJobPostedNotification::class,
],
UserFileGenerated::class => [
SendFileGeneratedNotification::class,
],
Expand Down
26 changes: 26 additions & 0 deletions api/tests/Feature/Notifications/TriggerNewJobPostedTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Notification;
use Nuwave\Lighthouse\Testing\RefreshesSchemaCache;
use Tests\TestCase;
Expand Down Expand Up @@ -65,6 +66,8 @@ public function testNothingSentForDraftPool(): void
];
$pool->save();

$this->travel(1)->minutes();
Artisan::call('send-notifications:pool-published');
Notification::assertNothingSent();
}

Expand All @@ -82,9 +85,30 @@ public function testNotifyWhenPublished(): void
$pool->published_at = Carbon::now();
$pool->save();

$this->travel(1)->minutes();
Artisan::call('send-notifications:pool-published');
Notification::assertSentTimes(NewJobPosted::class, 2);
}

// no notification when the pool is published with the "other" group
public function testNothingSentForOtherGroup(): void
{
$pool = Pool::factory()
->for($this->adminUser)
->draft()
->create([
'publishing_group' => PublishingGroup::OTHER->name,
'published_at' => null,
]);

$pool->published_at = Carbon::now();
$pool->save();

$this->travel(1)->minutes();
Artisan::call('send-notifications:pool-published');
Notification::assertNothingSent();
}

// no notification when pool is closed or archived
public function testNothingSentForClosedPool(): void
{
Expand All @@ -99,6 +123,8 @@ public function testNothingSentForClosedPool(): void
$pool->archived_at = Carbon::now();
$pool->save();

$this->travel(1)->minutes();
Artisan::call('send-notifications:pool-published');
Notification::assertNothingSent();
}
}

0 comments on commit 3cbb385

Please sign in to comment.