2019-07-09 22:03:54 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
|
2019-07-13 10:15:13 +01:00
|
|
|
use App\Album;
|
2019-07-09 22:03:54 +01:00
|
|
|
use App\Facade\UserConfig;
|
|
|
|
use App\Photo;
|
|
|
|
use App\QueueItem;
|
|
|
|
use App\Services\PhotoService;
|
|
|
|
use App\Services\RabbitMQService;
|
2019-07-10 14:31:04 +01:00
|
|
|
use App\User;
|
2019-07-09 23:05:22 +01:00
|
|
|
use App\UserActivity;
|
2019-07-09 22:03:54 +01:00
|
|
|
use Illuminate\Console\Command;
|
|
|
|
|
|
|
|
class ProcessQueueCommand extends Command
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* The name and signature of the console command.
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
2019-07-13 10:15:13 +01:00
|
|
|
protected $signature = 'bt-queue:process';
|
2019-07-09 22:03:54 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The console command description.
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $description = 'Processes items in the processing queue.';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new command instance.
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function __construct()
|
|
|
|
{
|
|
|
|
parent::__construct();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Execute the console command.
|
|
|
|
*
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public function handle()
|
|
|
|
{
|
|
|
|
if (!UserConfig::isImageProcessingQueueEnabled())
|
|
|
|
{
|
|
|
|
$this->output->error('The image processing queue is not enabled');
|
|
|
|
}
|
|
|
|
|
|
|
|
$rabbitmq = new RabbitMQService();
|
|
|
|
|
|
|
|
$this->output->writeln('Monitoring queue');
|
|
|
|
|
|
|
|
$rabbitmq->waitOnQueue([$this, 'processQueueItem']);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Processes a single item from the queue.
|
|
|
|
*
|
|
|
|
* @param $msg
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function processQueueItem($msg)
|
|
|
|
{
|
|
|
|
$queueItemID = intval($msg->body);
|
|
|
|
|
|
|
|
$this->output->writeln(sprintf('Processing queue item %d', $queueItemID));
|
|
|
|
|
|
|
|
/** @var QueueItem $queueItem */
|
|
|
|
$queueItem = QueueItem::where('id', $queueItemID)->first();
|
|
|
|
if (is_null($queueItem))
|
|
|
|
{
|
|
|
|
$this->output->writeln('Queue item does not exist; skipping');
|
|
|
|
$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
switch (strtolower($queueItem->action_type))
|
|
|
|
{
|
|
|
|
case 'photo.analyse':
|
|
|
|
$this->processPhotoAnalyseMessage($queueItem);
|
|
|
|
break;
|
|
|
|
|
2019-07-10 14:31:04 +01:00
|
|
|
case 'photo.bulk_action.change_album':
|
|
|
|
case 'photo.bulk_action.delete':
|
|
|
|
case 'photo.bulk_action.flip_both':
|
|
|
|
case 'photo.bulk_action.flip_horizontal':
|
|
|
|
case 'photo.bulk_action.flip_vertical':
|
|
|
|
case 'photo.bulk_action.refresh_thumbnails':
|
|
|
|
case 'photo.bulk_action.rotate_left':
|
|
|
|
case 'photo.bulk_action.rotate_right':
|
|
|
|
$this->processPhotoBulkActionMessage($queueItem);
|
|
|
|
break;
|
|
|
|
|
2019-07-09 22:03:54 +01:00
|
|
|
default:
|
|
|
|
$this->output->writeln(sprintf('Action %s is not recognised, skipping', $queueItem->action_type));
|
2019-07-09 23:05:22 +01:00
|
|
|
return;
|
2019-07-09 22:03:54 +01:00
|
|
|
}
|
2019-07-09 23:05:22 +01:00
|
|
|
|
|
|
|
$queueItem->completed_at = new \DateTime();
|
|
|
|
$queueItem->save();
|
|
|
|
}
|
|
|
|
catch (\Exception $ex)
|
|
|
|
{
|
|
|
|
$this->output->error($ex->getMessage());
|
2019-07-21 17:22:40 +01:00
|
|
|
$queueItem->error_message = $ex->getMessage();
|
2019-07-09 22:03:54 +01:00
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
2019-07-21 17:22:40 +01:00
|
|
|
$queueItem->completed_at = new \DateTime();
|
|
|
|
$queueItem->save();
|
|
|
|
|
2019-07-09 22:03:54 +01:00
|
|
|
$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-10 14:31:04 +01:00
|
|
|
private function createActivityRecord(Photo $photo, $type, $userID, $activityDateTime = null)
|
2019-07-09 23:05:22 +01:00
|
|
|
{
|
|
|
|
if (is_null($activityDateTime))
|
|
|
|
{
|
|
|
|
$activityDateTime = new \DateTime();
|
|
|
|
}
|
|
|
|
|
|
|
|
$userActivity = new UserActivity();
|
2019-07-10 14:31:04 +01:00
|
|
|
$userActivity->user_id = $userID;
|
2019-07-09 23:05:22 +01:00
|
|
|
$userActivity->activity_at = $activityDateTime;
|
|
|
|
$userActivity->type = $type;
|
|
|
|
$userActivity->photo_id = $photo->id;
|
|
|
|
$userActivity->save();
|
|
|
|
}
|
|
|
|
|
2019-07-09 22:03:54 +01:00
|
|
|
private function processPhotoAnalyseMessage(QueueItem $queueItem)
|
|
|
|
{
|
|
|
|
$this->output->writeln(sprintf('Analysing photo ID %l (batch: %s)', $queueItem->photo_id, $queueItem->batch_reference));
|
|
|
|
|
2019-07-10 14:31:04 +01:00
|
|
|
/** @var Photo $photo */
|
|
|
|
$photo = $queueItem->photo;
|
2019-07-09 22:03:54 +01:00
|
|
|
if (is_null($photo))
|
|
|
|
{
|
|
|
|
$this->output->writeln('Photo does not exist; skipping');
|
2019-07-10 14:31:04 +01:00
|
|
|
return;
|
2019-07-09 22:03:54 +01:00
|
|
|
}
|
|
|
|
|
2019-07-09 23:05:22 +01:00
|
|
|
/* IF CHANGING THIS LOGIC, ALSO CHECK PhotoController::analyse */
|
2019-07-09 22:03:54 +01:00
|
|
|
$photoService = new PhotoService($photo);
|
|
|
|
$photoService->analyse($queueItem->batch_reference);
|
2019-07-09 23:05:22 +01:00
|
|
|
|
|
|
|
// Log an activity record for the user's feed (remove an existing one as the date may have changed)
|
|
|
|
$this->removeExistingActivityRecords($photo, 'photo.taken');
|
|
|
|
|
|
|
|
if (!is_null($photo->taken_at))
|
|
|
|
{
|
|
|
|
// Log an activity record for the user's feed
|
2019-07-10 14:31:04 +01:00
|
|
|
$this->createActivityRecord($photo, 'photo.taken', $queueItem->user_id, $photo->taken_at);
|
|
|
|
}
|
2019-07-21 17:22:40 +01:00
|
|
|
|
|
|
|
$queueItem->is_successful = true;
|
2019-07-10 14:31:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private function processPhotoBulkActionMessage(QueueItem $queueItem)
|
|
|
|
{
|
|
|
|
$action = str_replace('photo.bulk_action.', '', $queueItem->action_type);
|
|
|
|
$this->output->writeln(sprintf('Apply action \'%s\' to photo ID %l (batch: %s)', $action, $queueItem->photo_id, $queueItem->batch_reference));
|
|
|
|
|
|
|
|
/** @var Photo $photo */
|
|
|
|
$photo = $queueItem->photo;
|
|
|
|
if (is_null($photo))
|
|
|
|
{
|
|
|
|
$this->output->writeln('Photo does not exist; skipping');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @var User $user */
|
|
|
|
$user = $queueItem->user;
|
|
|
|
if (is_null($user))
|
|
|
|
{
|
|
|
|
$this->output->writeln('User does not exist; skipping');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$photoService = new PhotoService($photo);
|
|
|
|
|
|
|
|
switch (strtolower($action))
|
|
|
|
{
|
|
|
|
/* This needs a bit more work - we need to also store the new album ID in the queue_items table */
|
2019-07-13 10:15:13 +01:00
|
|
|
case 'change_album':
|
2019-07-10 14:31:04 +01:00
|
|
|
if ($user->can('change-metadata', $photo))
|
|
|
|
{
|
2019-07-13 10:15:13 +01:00
|
|
|
$newAlbum = Album::where('id', intval($queueItem->new_album_id))->first();
|
|
|
|
if (is_null($newAlbum) || !$user->can('upload-photos', $newAlbum))
|
2019-07-10 14:31:04 +01:00
|
|
|
{
|
2019-07-13 10:15:13 +01:00
|
|
|
$this->output->writeln('Target album does not exist or user does not have permission.');
|
2019-07-10 14:31:04 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-13 10:15:13 +01:00
|
|
|
$this->output->writeln(sprintf('Moving photo to album \'%s\'', $newAlbum->name));
|
2019-07-10 14:31:04 +01:00
|
|
|
$photoService->changeAlbum($newAlbum);
|
|
|
|
}
|
2019-07-13 10:15:13 +01:00
|
|
|
break;
|
2019-07-10 14:31:04 +01:00
|
|
|
|
|
|
|
case 'delete':
|
|
|
|
if ($user->can('delete', $photo))
|
|
|
|
{
|
|
|
|
$photoService->delete();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'flip_both':
|
|
|
|
if ($user->can('manipulate', $photo))
|
|
|
|
{
|
|
|
|
$photoService->flip(true, true);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'flip_horizontal':
|
|
|
|
if ($user->can('manipulate', $photo))
|
|
|
|
{
|
|
|
|
$photoService->flip(true, false);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'flip_vertical':
|
|
|
|
if ($user->can('manipulate', $photo))
|
|
|
|
{
|
|
|
|
$photoService->flip(false, true);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'refresh_thumbnails':
|
|
|
|
if ($user->can('change-metadata', $photo))
|
|
|
|
{
|
|
|
|
$photoService->regenerateThumbnails();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'rotate_left':
|
|
|
|
if ($user->can('manipulate', $photo))
|
|
|
|
{
|
|
|
|
$photoService->rotate(90);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'rotate_right':
|
|
|
|
if ($user->can('manipulate', $photo))
|
|
|
|
{
|
|
|
|
$photoService->rotate(270);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
$this->output->writeln(sprintf('Action \'%s\' not recognised; skipping'));
|
|
|
|
return;
|
2019-07-09 23:05:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function removeExistingActivityRecords(Photo $photo, $type)
|
|
|
|
{
|
|
|
|
$existingFeedRecords = UserActivity::where([
|
|
|
|
'user_id' => $photo->user_id,
|
|
|
|
'photo_id' => $photo->id,
|
|
|
|
'type' => $type
|
|
|
|
])->get();
|
|
|
|
|
|
|
|
foreach ($existingFeedRecords as $existingFeedRecord)
|
|
|
|
{
|
|
|
|
$existingFeedRecord->delete();
|
|
|
|
}
|
2019-07-09 22:03:54 +01:00
|
|
|
}
|
|
|
|
}
|