#121: Bulk updates to photos now take place on the queue if enabled (just need to handle album changes)

This commit is contained in:
Andy Heathershaw 2019-07-10 14:31:04 +01:00
parent 0cca6eec66
commit ebbc5ba097
5 changed files with 265 additions and 98 deletions

View File

@ -7,6 +7,7 @@ use App\Photo;
use App\QueueItem; use App\QueueItem;
use App\Services\PhotoService; use App\Services\PhotoService;
use App\Services\RabbitMQService; use App\Services\RabbitMQService;
use App\User;
use App\UserActivity; use App\UserActivity;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@ -84,6 +85,17 @@ class ProcessQueueCommand extends Command
$this->processPhotoAnalyseMessage($queueItem); $this->processPhotoAnalyseMessage($queueItem);
break; break;
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;
default: default:
$this->output->writeln(sprintf('Action %s is not recognised, skipping', $queueItem->action_type)); $this->output->writeln(sprintf('Action %s is not recognised, skipping', $queueItem->action_type));
return; return;
@ -102,7 +114,7 @@ class ProcessQueueCommand extends Command
} }
} }
private function createActivityRecord(Photo $photo, $type, $activityDateTime = null) private function createActivityRecord(Photo $photo, $type, $userID, $activityDateTime = null)
{ {
if (is_null($activityDateTime)) if (is_null($activityDateTime))
{ {
@ -110,7 +122,7 @@ class ProcessQueueCommand extends Command
} }
$userActivity = new UserActivity(); $userActivity = new UserActivity();
$userActivity->user_id = $photo->user_id; $userActivity->user_id = $userID;
$userActivity->activity_at = $activityDateTime; $userActivity->activity_at = $activityDateTime;
$userActivity->type = $type; $userActivity->type = $type;
$userActivity->photo_id = $photo->id; $userActivity->photo_id = $photo->id;
@ -121,11 +133,12 @@ class ProcessQueueCommand extends Command
{ {
$this->output->writeln(sprintf('Analysing photo ID %l (batch: %s)', $queueItem->photo_id, $queueItem->batch_reference)); $this->output->writeln(sprintf('Analysing photo ID %l (batch: %s)', $queueItem->photo_id, $queueItem->batch_reference));
$photo = Photo::where('id', $queueItem->photo_id)->first(); /** @var Photo $photo */
$photo = $queueItem->photo;
if (is_null($photo)) if (is_null($photo))
{ {
$this->output->writeln('Photo does not exist; skipping'); $this->output->writeln('Photo does not exist; skipping');
return;
} }
/* IF CHANGING THIS LOGIC, ALSO CHECK PhotoController::analyse */ /* IF CHANGING THIS LOGIC, ALSO CHECK PhotoController::analyse */
@ -138,7 +151,104 @@ class ProcessQueueCommand extends Command
if (!is_null($photo->taken_at)) if (!is_null($photo->taken_at))
{ {
// Log an activity record for the user's feed // Log an activity record for the user's feed
$this->createActivityRecord($photo, 'photo.taken', $photo->taken_at); $this->createActivityRecord($photo, 'photo.taken', $queueItem->user_id, $photo->taken_at);
}
}
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 */
/*case 'change_album':
if ($user->can('change-metadata', $photo))
{
$newAlbumId = intval($request->get('new-album-id'));
if ($newAlbumId == $photo->album_id)
{
// Photo already belongs to this album, don't move
return;
}
$newAlbum = $this->loadAlbum($newAlbumId, 'upload-photos');
$photoService->changeAlbum($newAlbum);
$changed = true;
}
break;*/
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;
} }
} }

View File

@ -350,6 +350,7 @@ class PhotoController extends Controller
'action_type' => 'photo.analyse', 'action_type' => 'photo.analyse',
'album_id' => $photo->album_id, 'album_id' => $photo->album_id,
'photo_id' => $photo->id, 'photo_id' => $photo->id,
'user_id' => $this->getUser()->id,
'queued_at' => new \DateTime() 'queued_at' => new \DateTime()
]); ]);
$queueItem->save(); $queueItem->save();
@ -528,7 +529,14 @@ class PhotoController extends Controller
$numberChanged = $this->updatePhotoDetails($request, $album); $numberChanged = $this->updatePhotoDetails($request, $album);
} }
$request->session()->flash('success', trans_choice('admin.bulk_photos_changed', $numberChanged, ['number' => $numberChanged])); $request->session()->flash(
'success',
trans_choice(
UserConfig::isImageProcessingQueueEnabled() ? 'admin.bulk_photos_changed_queued' : 'admin.bulk_photos_changed',
$numberChanged,
['number' => $numberChanged]
)
);
return redirect(route('albums.show', array('id' => $albumId, 'page' => $request->get('page', 1)))); return redirect(route('albums.show', array('id' => $albumId, 'page' => $request->get('page', 1))));
} }
@ -570,103 +578,130 @@ class PhotoController extends Controller
$action = $request->get('bulk-action'); $action = $request->get('bulk-action');
$numberChanged = 0; $numberChanged = 0;
foreach ($photosToProcess as $photo) if (UserConfig::isImageProcessingQueueEnabled())
{ {
$changed = false; $queueUid = MiscHelper::randomString();
$photoService = new PhotoService($photo);
$doNotSave = false; foreach ($photosToProcess as $photo)
switch (strtolower($action))
{ {
case 'change_album': $queueItem = new QueueItem([
if (Auth::user()->can('change-metadata', $photo)) 'batch_reference' => $queueUid,
{ 'action_type' => sprintf('photo.bulk_action.%s', strtolower($action)),
$newAlbumId = intval($request->get('new-album-id')); 'album_id' => $photo->album_id,
if ($newAlbumId == $photo->album_id) 'photo_id' => $photo->id,
{ 'user_id' => $this->getUser()->id,
// Photo already belongs to this album, don't move 'queued_at' => new \DateTime()
continue; ]);
} $queueItem->save();
$newAlbum = $this->loadAlbum($newAlbumId, 'upload-photos'); $rabbitmq = new RabbitMQService();
$photoService->changeAlbum($newAlbum); $rabbitmq->queueItem($queueItem);
$changed = true;
}
break;
case 'delete':
if (Auth::user()->can('delete', $photo))
{
$photoService->delete();
$doNotSave = true;
$changed = true;
}
break;
case 'flip_both':
if (Auth::user()->can('manipulate', $photo))
{
$photoService->flip(true, true);
$changed = true;
}
break;
case 'flip_horizontal':
if (Auth::user()->can('manipulate', $photo))
{
$photoService->flip(true, false);
$changed = true;
}
break;
case 'flip_vertical':
if (Auth::user()->can('manipulate', $photo))
{
$photoService->flip(false, true);
$changed = true;
}
break;
case 'refresh_thumbnails':
if (Auth::user()->can('change-metadata', $photo))
{
$photoService->regenerateThumbnails();
$changed = true;
}
break;
case 'rotate_left':
if (Auth::user()->can('manipulate', $photo))
{
$photoService->rotate(90);
$changed = true;
}
break;
case 'rotate_right':
if (Auth::user()->can('manipulate', $photo))
{
$photoService->rotate(270);
$changed = true;
}
break;
}
if (!$doNotSave)
{
$photo->save();
}
if (!in_array(strtolower($action), ['delete', 'refresh_thumbnails', 'change_album']))
{
// Log an activity record for the user's feed
$this->createActivityRecord($photo, 'photo.edited');
}
if ($changed)
{
$numberChanged++; $numberChanged++;
} }
} }
else
{
foreach ($photosToProcess as $photo)
{
$changed = false;
$photoService = new PhotoService($photo);
$doNotSave = false;
/* IF CHANGING THIS LOGIC OR ADDING EXTRA case OPTIONS, ALSO CHECK ProcessQueueCommand::processQueueItem AND ProcessQueueCommand::processPhotoBulkActionMessage */
switch (strtolower($action))
{
case 'change_album':
if (Auth::user()->can('change-metadata', $photo))
{
$newAlbumId = intval($request->get('new-album-id'));
if ($newAlbumId == $photo->album_id)
{
// Photo already belongs to this album, don't move
continue 2;
}
$newAlbum = $this->loadAlbum($newAlbumId, 'upload-photos');
$photoService->changeAlbum($newAlbum);
$changed = true;
}
break;
case 'delete':
if (Auth::user()->can('delete', $photo))
{
$photoService->delete();
$doNotSave = true;
$changed = true;
}
break;
case 'flip_both':
if (Auth::user()->can('manipulate', $photo))
{
$photoService->flip(true, true);
$changed = true;
}
break;
case 'flip_horizontal':
if (Auth::user()->can('manipulate', $photo))
{
$photoService->flip(true, false);
$changed = true;
}
break;
case 'flip_vertical':
if (Auth::user()->can('manipulate', $photo))
{
$photoService->flip(false, true);
$changed = true;
}
break;
case 'refresh_thumbnails':
if (Auth::user()->can('change-metadata', $photo))
{
$photoService->regenerateThumbnails();
$changed = true;
}
break;
case 'rotate_left':
if (Auth::user()->can('manipulate', $photo))
{
$photoService->rotate(90);
$changed = true;
}
break;
case 'rotate_right':
if (Auth::user()->can('manipulate', $photo))
{
$photoService->rotate(270);
$changed = true;
}
break;
}
if (!$doNotSave)
{
$photo->save();
}
if (!in_array(strtolower($action), ['delete', 'refresh_thumbnails', 'change_album']))
{
// Log an activity record for the user's feed
$this->createActivityRecord($photo, 'photo.edited');
}
if ($changed)
{
$numberChanged++;
}
}
}
return $numberChanged; return $numberChanged;
} }

View File

@ -16,6 +16,22 @@ class QueueItem extends Model
'action_type', 'action_type',
'album_id', 'album_id',
'photo_id', 'photo_id',
'queued_at' 'queued_at',
'user_id'
]; ];
public function album()
{
return $this->belongsTo(Album::class);
}
public function photo()
{
return $this->belongsTo(Photo::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
} }

View File

@ -15,14 +15,19 @@ class CreateQueueItemsTable extends Migration
{ {
Schema::create('queue_items', function (Blueprint $table) { Schema::create('queue_items', function (Blueprint $table) {
$table->increments('id'); $table->increments('id');
$table->unsignedInteger('user_id');
$table->string('batch_reference')->nullable(true); $table->string('batch_reference')->nullable(true);
$table->string('action_type', 20); $table->string('action_type', 50);
$table->unsignedInteger('album_id'); $table->unsignedInteger('album_id');
$table->unsignedBigInteger('photo_id'); $table->unsignedBigInteger('photo_id');
$table->dateTime('queued_at'); $table->dateTime('queued_at');
$table->dateTime('completed_at')->nullable(true); $table->dateTime('completed_at')->nullable(true);
$table->timestamps(); $table->timestamps();
$table->foreign('user_id')
->references('id')->on('users')
->onDelete('cascade');
$table->foreign('album_id') $table->foreign('album_id')
->references('id')->on('albums') ->references('id')->on('albums')
->onDelete('cascade'); ->onDelete('cascade');

View File

@ -66,6 +66,7 @@ return [
'bulk_comments_approved' => ':number comment was approved successfully.|:number comments were approved successfully.', 'bulk_comments_approved' => ':number comment was approved successfully.|:number comments were approved successfully.',
'bulk_comments_deleted' => ':number comment was deleted successfully.|:number comments were deleted successfully.', 'bulk_comments_deleted' => ':number comment was deleted successfully.|:number comments were deleted successfully.',
'bulk_photos_changed' => ':number photo was updated successfully.|:number photos were updated successfully.', 'bulk_photos_changed' => ':number photo was updated successfully.|:number photos were updated successfully.',
'bulk_photos_changed_queued' => 'Your requested change has been queued. :number photo will be updated shortly.|Your requested change has been queued. :number photos will be updated shortly.',
'cannot_delete_own_user_account' => 'It is not possible to delete your own user account. Please ask another administrator to delete it for you.', 'cannot_delete_own_user_account' => 'It is not possible to delete your own user account. Please ask another administrator to delete it for you.',
'cannot_remove_own_admin' => 'You cannot remove your own administrator permissions. Please ask another administrator to remove the administrator permissions for you.', 'cannot_remove_own_admin' => 'You cannot remove your own administrator permissions. Please ask another administrator to remove the administrator permissions for you.',
'change_album_message' => 'Please select the album to move the photo(s) to:', 'change_album_message' => 'Please select the album to move the photo(s) to:',