mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-15 14:03:27 +00:00
PHRAS-1055 #time 5h
- better error handling - argument "databox" can be specified as id, dbname or viewname - add option "--partition=n/N" - argument "type" changed to option "--record_type=a,b,c" (can also be set as "--record_type=a,b --record_type=c") - argument "subdefs" changed to option "--name=x,y,z" (can also be set as "--name=x,y --name=z") - option "--with-substitution" changed to "--with_substituted" - option "--substitution-only" changed to "--substituted-only"
This commit is contained in:
@@ -25,19 +25,159 @@ use media_subdef;
|
||||
|
||||
class BuildSubdefs extends Command
|
||||
{
|
||||
const OPTION_DISTINT_VALUES = 0;
|
||||
const OPTION_ALL_VALUES = 1;
|
||||
|
||||
/** @var InputInterface */
|
||||
private $input;
|
||||
/** @var OutputInterface */
|
||||
private $output;
|
||||
/** @var bool */
|
||||
var $argsOK;
|
||||
/** @var \Databox */
|
||||
var $databox;
|
||||
/** @var connection */
|
||||
var $connection;
|
||||
var $recmin;
|
||||
var $recmax;
|
||||
var $substitutedOnly;
|
||||
var $withSubstituted;
|
||||
var $subdefsNameByType;
|
||||
|
||||
/** @var int */
|
||||
private $partitionIndex;
|
||||
/** @var int */
|
||||
private $partitionCount;
|
||||
|
||||
/** @var bool */
|
||||
var $dry;
|
||||
|
||||
public function __construct($name = null)
|
||||
{
|
||||
parent::__construct($name);
|
||||
|
||||
$this->setDescription('Build subviews for given subview names and record types');
|
||||
$this->addArgument('databox', InputArgument::REQUIRED, 'The databox id');
|
||||
$this->addArgument('type', InputArgument::REQUIRED, 'Type(s) of document(s) to rebuild ex. "image,video", or "ALL"');
|
||||
$this->addArgument('subdefs', InputArgument::REQUIRED, 'Name(s) of sub-definition(s) to re-build, ex. "thumbnail,preview", or "ALL"');
|
||||
$this->addOption('min_record', 'min', InputOption::VALUE_OPTIONAL, 'Min record id');
|
||||
$this->addOption('max_record', 'max', InputOption::VALUE_OPTIONAL, 'Max record id');
|
||||
$this->addOption('with-substitution', 'wsubstit', InputOption::VALUE_NONE, 'Regenerate subdefs for substituted records as well');
|
||||
$this->addOption('substitution-only', 'substito', InputOption::VALUE_NONE, 'Regenerate subdefs for substituted records only');
|
||||
$this->addOption('dry', '', InputOption::VALUE_NONE, 'dry run, list but do nothing');
|
||||
$this->addArgument('databox', InputArgument::REQUIRED, 'The id (or dbname or viewname) of the databox');
|
||||
$this->addOption('record_type', null, InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, 'Type(s) of records(s) to (re)build ex. "image,video", dafault=ALL');
|
||||
$this->addOption('name', null, InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, 'Name(s) of sub-definition(s) to (re)build, ex. "thumbnail,preview", default=ALL');
|
||||
$this->addOption('min_record', null, InputOption::VALUE_OPTIONAL, 'Min record id');
|
||||
$this->addOption('max_record', null, InputOption::VALUE_OPTIONAL, 'Max record id');
|
||||
$this->addOption('with_substituted', null, InputOption::VALUE_NONE, 'Regenerate subdefs for substituted records as well');
|
||||
$this->addOption('substituted_only', null, InputOption::VALUE_NONE, 'Regenerate subdefs for substituted records only');
|
||||
$this->addOption('partition', null, InputOption::VALUE_REQUIRED, 'n/N : work only on records belonging to partition \'n\'');
|
||||
$this->addOption('dry', null, InputOption::VALUE_NONE, 'dry run, list but don\'t act');
|
||||
}
|
||||
|
||||
/**
|
||||
* merge options so one can mix csv-option and/or multiple options
|
||||
* ex. with keepUnique = false : --opt=a,b --opt=c --opt=b ==> [a,b,c,b]
|
||||
* ex. with keepUnique = true : --opt=a,b --opt=c --opt=b ==> [a,b,c]
|
||||
*
|
||||
* @param InputInterface $input
|
||||
* @param string $optionName
|
||||
* @param int $option
|
||||
* @return array
|
||||
*/
|
||||
private function getOptionAsArray(InputInterface $input, $optionName, $option)
|
||||
{
|
||||
$ret = [];
|
||||
foreach($input->getOption($optionName) as $v0) {
|
||||
foreach(explode(',', $v0) as $v) {
|
||||
$v = trim($v);
|
||||
if($option & self::OPTION_ALL_VALUES || !in_array($v, $ret)) {
|
||||
$ret[] = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* print a string if verbosity >= verbose (-v)
|
||||
* @param string $s
|
||||
*/
|
||||
private function verbose($s)
|
||||
{
|
||||
if($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
|
||||
$this->output->write($s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sanity check the cmd line options
|
||||
*
|
||||
*/
|
||||
protected function interact(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->argsOK = true;
|
||||
|
||||
// find the databox / collection by id or by name
|
||||
$this->databox = null;
|
||||
$d = trim($input->getArgument('databox'));
|
||||
foreach ($this->container->getDataboxes() as $db) {
|
||||
if ($db->get_sbas_id() == (int)$d || $db->get_viewname() == $d || $db->get_dbname() == $d) {
|
||||
$this->databox = $db;
|
||||
$this->connection = $db->get_connection();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($this->databox == null) {
|
||||
$output->writeln(sprintf("<error>Unknown databox \"%s\"</error>", $input->getArgument('databox')));
|
||||
$this->argsOK = false;
|
||||
}
|
||||
|
||||
// get options
|
||||
$this->dry = $input->getOption('dry') ? true : false;
|
||||
$this->recmin = $input->getOption('min_record');
|
||||
$this->recmax = $input->getOption('max_record');
|
||||
$this->substitutedOnly = $input->getOption('substituted_only') ? true : false;
|
||||
$this->withSubstituted = $input->getOption('with_substituted') ? true : false;
|
||||
if ($this->withSubstituted && $this->substitutedOnly) {
|
||||
$output->writeln("<error>--substituted_only and --with_substituted are mutually exclusive<error>");
|
||||
$this->argsOK = false;
|
||||
}
|
||||
|
||||
// validate types and subdefs
|
||||
$this->subdefsNameByType = [];
|
||||
|
||||
if($this->databox !== null) {
|
||||
$types = $this->getOptionAsArray($input, 'record_type', self::OPTION_DISTINT_VALUES);
|
||||
$names = $this->getOptionAsArray($input, 'name', self::OPTION_DISTINT_VALUES);
|
||||
|
||||
/** @var SubdefGroup $sg */
|
||||
foreach ($this->databox->get_subdef_structure() as $sg) {
|
||||
if (empty($types) || in_array($sg->getName(), $types)) {
|
||||
$this->subdefsNameByType[$sg->getName()] = [];
|
||||
/** @var databox_subdef $sd */
|
||||
foreach ($sg as $sd) {
|
||||
if (empty($names) || in_array($sd->get_name(), $names)) {
|
||||
$this->subdefsNameByType[$sg->getName()][] = $sd->get_name();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($types as $t) {
|
||||
if (!array_key_exists($t, $this->subdefsNameByType)) {
|
||||
$output->writeln(sprintf("<error>unknown type \"%s\"</error>", $t));
|
||||
$this->argsOK = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validate partition
|
||||
$this->partitionIndex = $this->partitionCount = null;
|
||||
if( ($arg = $input->getOption('partition')) !== null) {
|
||||
$arg = explode('/', $arg);
|
||||
if(count($arg) == 2 && ($arg0 = (int)trim($arg[0]))>0 && ($arg1 = (int)trim($arg[1]))>1 && $arg0<=$arg1 ) {
|
||||
$this->partitionIndex = $arg0;
|
||||
$this->partitionCount = $arg1;
|
||||
}
|
||||
else {
|
||||
$output->writeln(sprintf('<error>partition must be n/N</error>'));
|
||||
$this->argsOK = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,54 +185,21 @@ class BuildSubdefs extends Command
|
||||
*/
|
||||
protected function doExecute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$availableTypes = array('document', 'audio', 'video', 'image', 'flash', 'map');
|
||||
|
||||
$min = $input->getOption('min_record');
|
||||
$max = $input->getOption('max_record');
|
||||
$substitutionOnly = $input->getOption('substitution-only') ? true : false;
|
||||
$withSubstitution = $input->getOption('with-substitution') ? true : false;
|
||||
$dry = $input->getOption('dry') ? true : false;
|
||||
|
||||
if ($withSubstitution && $substitutionOnly) {
|
||||
throw new InvalidArgumentException('--substitution-only and --with-substitution are mutually exclusive');
|
||||
if(!$this->argsOK) {
|
||||
return -1;
|
||||
}
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
|
||||
$databox = $this->container->findDataboxById($input->getArgument('databox'));
|
||||
$connection = $databox->get_connection();
|
||||
|
||||
$subdefsNameByType = [];
|
||||
|
||||
$typesOption = $input->getArgument('type');
|
||||
$typesArray = explode(',', $typesOption);
|
||||
|
||||
$subdefsOption = $input->getArgument('subdefs');
|
||||
$subdefsArray = explode(',', $subdefsOption);
|
||||
|
||||
/** @var SubdefGroup $sg */
|
||||
foreach($databox->get_subdef_structure() as $sg)
|
||||
{
|
||||
if($typesOption == "ALL" || in_array($sg->getName(), $typesArray)) {
|
||||
$subdefsNameByType[$sg->getName()] = [];
|
||||
/** @var databox_subdef $sd */
|
||||
foreach ($sg as $sd) {
|
||||
if($subdefsOption == "ALL" || in_array($sd->get_name(), $subdefsArray)) {
|
||||
$subdefsNameByType[$sg->getName()][] = $sd->get_name();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$recordsType = array_keys($subdefsNameByType);
|
||||
|
||||
list($sql, $params, $types) = $this->generateSQL($connection, $recordsType, $min, $max);
|
||||
list($sql, $params, $types) = $this->getSQL();
|
||||
|
||||
$sqlCount = sprintf('SELECT COUNT(*) FROM (%s) AS c', $sql);
|
||||
$output->writeln($sqlCount);
|
||||
|
||||
$totalRecords = (int)$connection->executeQuery($sqlCount, $params, $types)->fetchColumn();
|
||||
$totalRecords = (int)$this->connection->executeQuery($sqlCount, $params, $types)->fetchColumn();
|
||||
|
||||
if ($totalRecords === 0) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
$progress = null;
|
||||
@@ -102,36 +209,36 @@ class BuildSubdefs extends Command
|
||||
$progress->display();
|
||||
}
|
||||
|
||||
$rows = $connection->executeQuery($sql, $params, $types)->fetchAll(\PDO::FETCH_ASSOC);
|
||||
$rows = $this->connection->executeQuery($sql, $params, $types)->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$type = $row['type'];
|
||||
$output->write(sprintf(' [#%s] (%s)', $row['record_id'], $type));
|
||||
|
||||
try {
|
||||
$record = $databox->get_record($row['record_id']);
|
||||
$record = $this->databox->get_record($row['record_id']);
|
||||
|
||||
$subdefNamesToDo = array_flip($subdefsNameByType[$type]); // do all subdefs ?
|
||||
$subdefNamesToDo = array_flip($this->subdefsNameByType[$type]); // do all subdefs ?
|
||||
|
||||
/** @var media_subdef $subdef */
|
||||
foreach ($record->get_subdefs() as $subdef) {
|
||||
if(!in_array($subdef->get_name(), $subdefsNameByType[$type])) {
|
||||
if(!in_array($subdef->get_name(), $this->subdefsNameByType[$type])) {
|
||||
continue;
|
||||
}
|
||||
if($subdef->is_substituted()) {
|
||||
if(!$withSubstitution && !$substitutionOnly) {
|
||||
if(!$this->withSubstituted && !$this->substitutedOnly) {
|
||||
unset($subdefNamesToDo[$subdef->get_name()]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if($substitutionOnly) {
|
||||
if($this->substitutedOnly) {
|
||||
unset($subdefNamesToDo[$subdef->get_name()]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// here an existing subdef must be re-done
|
||||
if(!$dry) {
|
||||
if(!$this->dry) {
|
||||
$subdef->remove_file();
|
||||
$subdef->set_substituted(false);
|
||||
}
|
||||
@@ -139,26 +246,20 @@ class BuildSubdefs extends Command
|
||||
|
||||
$subdefNamesToDo = array_keys($subdefNamesToDo);
|
||||
if(!empty($subdefNamesToDo)) {
|
||||
if(!$dry) {
|
||||
if(!$this->dry) {
|
||||
/** @var SubdefGenerator $subdefGenerator */
|
||||
$subdefGenerator = $this->container['subdef.generator'];
|
||||
$subdefGenerator->generateSubdefs($record, $subdefNamesToDo);
|
||||
}
|
||||
|
||||
if($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
|
||||
$output->writeln(sprintf(" subdefs[%s] done", join(',', $subdefNamesToDo)));
|
||||
}
|
||||
$this->verbose(sprintf(" subdefs[%s] done\n", join(',', $subdefNamesToDo)));
|
||||
}
|
||||
else {
|
||||
if($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
|
||||
$output->writeln(sprintf(" nothing to do"));
|
||||
}
|
||||
$this->verbose(" nothing to do\n");
|
||||
}
|
||||
}
|
||||
catch(\Exception $e) {
|
||||
if($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
|
||||
$output->writeln(sprintf("failed"));
|
||||
}
|
||||
$this->verbose("failed\n");
|
||||
}
|
||||
|
||||
if($progress) {
|
||||
@@ -168,30 +269,33 @@ class BuildSubdefs extends Command
|
||||
|
||||
if($progress) {
|
||||
$progress->finish();
|
||||
$output->writeln("");
|
||||
$output->writeln('');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $recordTypes
|
||||
* @param null|int $min
|
||||
* @param null|int $max
|
||||
* @return array
|
||||
*/
|
||||
protected function generateSQL(Connection $connection, array $recordTypes, $min, $max)
|
||||
protected function getSQL()
|
||||
{
|
||||
$sql = "SELECT record_id, type FROM record WHERE parent_record_id=0";
|
||||
|
||||
$types = array_map(function($v) use($connection){return $connection->quote($v);}, $recordTypes);
|
||||
$recordTypes = array_keys($this->subdefsNameByType);
|
||||
$types = array_map(function($v) {return $this->connection->quote($v);}, $recordTypes);
|
||||
|
||||
if(!empty($types)) {
|
||||
$sql .= ' AND type IN(' . join(',', $types) . ')';
|
||||
}
|
||||
if (null !== $min) {
|
||||
$sql .= ' AND (record_id >= ' . $connection->quote($min) . ')';
|
||||
if ($this->recmin !== null) {
|
||||
$sql .= ' AND (record_id >= ' . (int)($this->recmin) . ')';
|
||||
}
|
||||
if (null !== $max) {
|
||||
$sql .= ' AND (record_id <= ' . $connection->quote($max) . ')';
|
||||
if ($this->recmax) {
|
||||
$sql .= ' AND (record_id <= ' . (int)($this->recmax) . ')';
|
||||
}
|
||||
if($this->partitionCount !== null && $this->partitionIndex !== null) {
|
||||
$sql .= ' AND MOD(record_id, ' . $this->partitionCount . ')=' . ($this->partitionIndex-1);
|
||||
}
|
||||
|
||||
return array($sql, [], []);
|
||||
|
Reference in New Issue
Block a user