-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1018 from biigle/merge-sync
Merge biigle/sync
- Loading branch information
Showing
94 changed files
with
9,352 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?php | ||
|
||
namespace Biigle\Console\Commands; | ||
|
||
use Biigle\Services\Import\ArchiveManager; | ||
use Illuminate\Console\Command; | ||
|
||
class SyncPrune extends Command | ||
{ | ||
/** | ||
* The console command name. | ||
* | ||
* @var string | ||
*/ | ||
protected $name = 'sync:prune'; | ||
|
||
/** | ||
* The console command description. | ||
* | ||
* @var string | ||
*/ | ||
protected $description = 'Delete uploaded import files that are older than one week and where the import was not finished.'; | ||
|
||
/** | ||
* Execute the command. | ||
* | ||
* @return void | ||
*/ | ||
public function handle(ArchiveManager $manager) | ||
{ | ||
$manager->prune(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
<?php | ||
|
||
namespace Biigle\Console\Commands; | ||
|
||
use Biigle\User; | ||
use File; | ||
use Hash; | ||
use Illuminate\Console\Command; | ||
|
||
class SyncUuids extends Command | ||
{ | ||
/** | ||
* The name and signature of the console command. | ||
* | ||
* @var string | ||
*/ | ||
protected $signature = 'sync:uuids | ||
{file? : If a file is provided, use it for syncing. If not, output the contents of such a file.} | ||
{--dry-run : Do not change the database records} | ||
{--force : Synchronise matching users without asking}'; | ||
|
||
/** | ||
* The console command description. | ||
* | ||
* @var string | ||
*/ | ||
protected $description = 'Synchronize user UUIDs across BIIGLE instances'; | ||
|
||
/** | ||
* Execute the command. | ||
* | ||
* @return void | ||
*/ | ||
public function handle() | ||
{ | ||
if ($this->argument('file')) { | ||
$this->handleSync(); | ||
} else { | ||
$this->generateOutput(); | ||
} | ||
} | ||
|
||
/** | ||
* Read the input file and perform interactive synchronization of user UUIDs. | ||
*/ | ||
protected function handleSync() | ||
{ | ||
$matches = 0; | ||
$input = collect(json_decode(File::get($this->argument('file')), true)); | ||
$inputUuids = $input->pluck('uuid'); | ||
|
||
// All users with a UUID not among those of the input are candidates for syncing. | ||
$users = User::select('id', 'email', 'firstname', 'lastname') | ||
->whereNotIn('uuid', $inputUuids) | ||
->get(); | ||
|
||
// Remove input users with a UUID already in the database as they should not be | ||
// considered for syncing. | ||
$input = $input->whereNotIn('uuid', User::whereIn('uuid', $inputUuids)->pluck('uuid')); | ||
|
||
// Find matches and sync. | ||
foreach ($users as $user) { | ||
foreach ($input as $inputUser) { | ||
$emailMatches = Hash::check($user->email, $inputUser['email']); | ||
$nameMatches = Hash::check($user->firstname, $inputUser['firstname']) && Hash::check($user->lastname, $inputUser['lastname']); | ||
|
||
if ($emailMatches || $nameMatches) { | ||
$matches += 1; | ||
} | ||
|
||
$sync = false; | ||
if ($emailMatches && $nameMatches) { | ||
$this->info("Found matching email address and name for {$user->firstname} {$user->lastname} ({$user->email})."); | ||
$sync = $this->option('force') || $this->confirm("Synchronize UUID with file?"); | ||
} elseif ($emailMatches) { | ||
$this->info("Found matching email address but different name for {$user->firstname} {$user->lastname} ({$user->email})."); | ||
$sync = $this->option('force') || $this->confirm("Synchronize UUID with file?"); | ||
} elseif ($nameMatches) { | ||
$this->info("Found matching name but different email address for {$user->firstname} {$user->lastname} ({$user->email})."); | ||
$sync = $this->option('force') || $this->confirm("Synchronize UUID with file?"); | ||
} | ||
|
||
if ($sync) { | ||
$user->uuid = $inputUser['uuid']; | ||
if (!$this->option('dry-run')) { | ||
$user->save(); | ||
} | ||
$this->warn("UUID synchronized for {$user->firstname} {$user->lastname} ({$user->email})."); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
if ($matches === 0) { | ||
$this->info('No candidates found'); | ||
} else { | ||
$this->info('Finished'); | ||
} | ||
} | ||
|
||
/** | ||
* Generate the output that can be used as file to synchronize user UUIDs. | ||
*/ | ||
protected function generateOutput() | ||
{ | ||
$users = User::select('uuid', 'firstname', 'lastname', 'email')->get(); | ||
$bar = $this->output->createProgressBar($users->count()); | ||
$users = | ||
$users->map(function ($user) use ($bar) { | ||
$bar->advance(); | ||
|
||
return [ | ||
'uuid' => $user->uuid, | ||
'firstname' => Hash::make($user->firstname), | ||
'lastname' => Hash::make($user->lastname), | ||
'email' => Hash::make($user->email), | ||
]; | ||
})->toJson(JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); | ||
$bar->finish(); | ||
|
||
echo $users; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
<?php | ||
|
||
namespace Biigle\Http\Controllers\Api\Export; | ||
|
||
use Biigle\Http\Controllers\Api\Controller as BaseController; | ||
use Illuminate\Http\Request; | ||
|
||
abstract class Controller extends BaseController | ||
{ | ||
/** | ||
* Creates a new instance. | ||
*/ | ||
public function __construct() | ||
{ | ||
$this->middleware('can:sudo'); | ||
} | ||
|
||
/** | ||
* Handle a generic export request. | ||
* | ||
* @param Request $request | ||
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse | ||
*/ | ||
public function show(Request $request) | ||
{ | ||
if (!$this->isAllowed()) { | ||
abort(404); | ||
} | ||
|
||
$this->validate($request, ['except' => 'filled', 'only' => 'filled']); | ||
$query = $this->getQuery(); | ||
|
||
if ($request->filled('except')) { | ||
$query = $query->whereNotIn('id', explode(',', $request->input('except'))); | ||
} elseif ($request->filled('only')) { | ||
$query = $query->whereIn('id', explode(',', $request->input('only'))); | ||
} | ||
|
||
$export = $this->getExport($query->pluck('id')->toArray()); | ||
|
||
return response() | ||
->download($export->getArchive(), $this->getExportFilename(), [ | ||
'Content-Type' => 'application/zip', | ||
]) | ||
->deleteFileAfterSend(true); | ||
} | ||
|
||
/** | ||
* Get the query for the model to export. | ||
* | ||
* @return \Illuminate\Database\Query\Builder | ||
*/ | ||
abstract protected function getQuery(); | ||
|
||
/** | ||
* Get the new export instance. | ||
* | ||
* @param array $ids | ||
* @return \Biigle\Services\Export\Export | ||
*/ | ||
abstract protected function getExport(array $ids); | ||
|
||
/** | ||
* Get the filename of the export archive. | ||
* | ||
* @return string | ||
*/ | ||
abstract protected function getExportFilename(); | ||
|
||
/** | ||
* Determine if this kind of export is allowed by the config. | ||
* | ||
* @return bool | ||
*/ | ||
protected function isAllowed() | ||
{ | ||
return false; | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
app/Http/Controllers/Api/Export/LabelTreeExportController.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<?php | ||
|
||
namespace Biigle\Http\Controllers\Api\Export; | ||
|
||
use Biigle\LabelTree; | ||
use Biigle\Services\Export\LabelTreeExport; | ||
|
||
class LabelTreeExportController extends Controller | ||
{ | ||
/** | ||
* @api {get} export/label-trees Get a label tree export | ||
* @apiGroup Sync | ||
* @apiName ShowLabelTreeExport | ||
* | ||
* @apiParam (Optional arguments) {String} except Comma separated IDs of the label trees that should not be included in the export file. | ||
* @apiParam (Optional arguments) {String} only Comma separated IDs of the label trees that should only be included in the export file. | ||
* @apiDescription The response is a ZIP archive that can be used for the label tree import. By default all label trees are exported. | ||
* @apiPermission admin | ||
*/ | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function getQuery() | ||
{ | ||
return LabelTree::getQuery(); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function getExport(array $ids) | ||
{ | ||
return new LabelTreeExport($ids); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function getExportFilename() | ||
{ | ||
return 'biigle_label_tree_export.zip'; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function isAllowed() | ||
{ | ||
return in_array('labelTrees', config('sync.allowed_exports')); | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
app/Http/Controllers/Api/Export/PublicLabelTreeExportController.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<?php | ||
|
||
namespace Biigle\Http\Controllers\Api\Export; | ||
|
||
use Biigle\Http\Controllers\Api\Controller as BaseController; | ||
use Biigle\Http\Requests\ShowPublicLabelTreeExport; | ||
use Biigle\Services\Export\PublicLabelTreeExport; | ||
|
||
class PublicLabelTreeExportController extends BaseController | ||
{ | ||
/** | ||
* Handle a public label tree export request. | ||
* | ||
* @api {get} public-export/label-trees/:id Download a label tree | ||
* @apiGroup Sync | ||
* @apiName ShowPublicLabelTreeExport | ||
* @apiDescription The response is a ZIP archive that contains a JSON file with label tree attributes and a CSV file with label attributes. | ||
* | ||
* @apiParam {Number} id The label tree ID | ||
* | ||
* @apiPermission labelTreeMemberIfPrivate | ||
* | ||
* @param ShowPublicLabelTreeExport $request | ||
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse | ||
*/ | ||
public function show(ShowPublicLabelTreeExport $request) | ||
{ | ||
$export = new PublicLabelTreeExport([$request->tree->id]); | ||
|
||
return response() | ||
->download($export->getArchive(), 'biigle_label_tree_export.zip', [ | ||
'Content-Type' => 'application/zip', | ||
]) | ||
->deleteFileAfterSend(true); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<?php | ||
|
||
namespace Biigle\Http\Controllers\Api\Export; | ||
|
||
use Biigle\Services\Export\UserExport; | ||
use Biigle\User; | ||
|
||
class UserExportController extends Controller | ||
{ | ||
/** | ||
* @api {get} export/users Get a user export | ||
* @apiGroup Sync | ||
* @apiName ShowUserExport | ||
* | ||
* @apiParam (Optional arguments) {String} except Comma separated IDs of the users that should not be included in the export file. | ||
* @apiParam (Optional arguments) {String} only Comma separated IDs of the users that should only be included in the export file. | ||
* @apiDescription The response is a ZIP archive that can be used for the user import. By default all users are exported. | ||
* @apiPermission admin | ||
*/ | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function getQuery() | ||
{ | ||
return User::getQuery(); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function getExport(array $ids) | ||
{ | ||
return new UserExport($ids); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function getExportFilename() | ||
{ | ||
return 'biigle_user_export.zip'; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function isAllowed() | ||
{ | ||
return in_array('users', config('sync.allowed_exports')); | ||
} | ||
} |
Oops, something went wrong.